Files
httprunner/prepare/testcase-layer/index.html
2019-12-11 17:20:18 +08:00

1237 lines
43 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="zh" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="description" content="HttpRunner V2.x User Documentation">
<meta name="author" content="debugtalk">
<meta name="lang:clipboard.copy" content="复制">
<meta name="lang:clipboard.copied" content="已复制">
<meta name="lang:search.language" content="jp">
<meta name="lang:search.pipeline.stopwords" content="True">
<meta name="lang:search.pipeline.trimmer" content="True">
<meta name="lang:search.result.none" content="没有找到符合条件的结果">
<meta name="lang:search.result.one" content="找到 1 个符合条件的结果">
<meta name="lang:search.result.other" content="# 个符合条件的结果">
<meta name="lang:search.tokenizer" content="[\uff0c\u3002]+">
<link rel="shortcut icon" href="../../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.0.4, mkdocs-material-4.4.3">
<title>测试用例分层 - HttpRunner V2.x 中文使用文档</title>
<link rel="stylesheet" href="../../assets/stylesheets/application.30686662.css">
<link rel="stylesheet" href="../../assets/stylesheets/application-palette.a8b3c06d.css">
<meta name="theme-color" content="#3f51b5">
<script src="../../assets/javascripts/modernizr.74668098.js"></script>
<link href="https://fonts.gstatic.com" rel="preconnect" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,400i,700|Roboto+Mono&display=fallback">
<style>body,input{font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif}code,kbd,pre{font-family:"Roboto Mono","Courier New",Courier,monospace}</style>
<link rel="stylesheet" href="../../assets/fonts/material-icons.css">
<script>
window.ga = window.ga || function() {
(ga.q = ga.q || []).push(arguments)
}
ga.l = +new Date
/* Setup integration and send page view */
ga("create", "UA-114587036-2", "auto")
ga("set", "anonymizeIp", true)
ga("send", "pageview")
/* Register handler to log search on blur */
document.addEventListener("DOMContentLoaded", () => {
if (document.forms.search) {
var query = document.forms.search.query
query.addEventListener("blur", function() {
if (this.value) {
var path = document.location.pathname;
ga("send", "pageview", path + "?q=" + this.value)
}
})
}
})
</script>
<script async src="https://www.google-analytics.com/analytics.js"></script>
</head>
<body dir="ltr" data-md-color-primary="indigo" data-md-color-accent="indigo">
<svg class="md-svg">
<defs>
<svg xmlns="http://www.w3.org/2000/svg" width="416" height="448" viewBox="0 0 416 448" id="__github"><path fill="currentColor" d="M160 304q0 10-3.125 20.5t-10.75 19T128 352t-18.125-8.5-10.75-19T96 304t3.125-20.5 10.75-19T128 256t18.125 8.5 10.75 19T160 304zm160 0q0 10-3.125 20.5t-10.75 19T288 352t-18.125-8.5-10.75-19T256 304t3.125-20.5 10.75-19T288 256t18.125 8.5 10.75 19T320 304zm40 0q0-30-17.25-51T296 232q-10.25 0-48.75 5.25Q229.5 240 208 240t-39.25-2.75Q130.75 232 120 232q-29.5 0-46.75 21T56 304q0 22 8 38.375t20.25 25.75 30.5 15 35 7.375 37.25 1.75h42q20.5 0 37.25-1.75t35-7.375 30.5-15 20.25-25.75T360 304zm56-44q0 51.75-15.25 82.75-9.5 19.25-26.375 33.25t-35.25 21.5-42.5 11.875-42.875 5.5T212 416q-19.5 0-35.5-.75t-36.875-3.125-38.125-7.5-34.25-12.875T37 371.5t-21.5-28.75Q0 312 0 260q0-59.25 34-99-6.75-20.5-6.75-42.5 0-29 12.75-54.5 27 0 47.5 9.875t47.25 30.875Q171.5 96 212 96q37 0 70 8 26.25-20.5 46.75-30.25T376 64q12.75 25.5 12.75 54.5 0 21.75-6.75 42 34 40 34 99.5z"/></svg>
</defs>
</svg>
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" data-md-component="overlay" for="__drawer"></label>
<a href="#_1" tabindex="1" class="md-skip">
跳转至
</a>
<header class="md-header" data-md-component="header">
<nav class="md-header-nav md-grid">
<div class="md-flex">
<div class="md-flex__cell md-flex__cell--shrink">
<a href="../.." title="HttpRunner V2.x 中文使用文档" class="md-header-nav__button md-logo">
<i class="md-icon"></i>
</a>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<label class="md-icon md-icon--menu md-header-nav__button" for="__drawer"></label>
</div>
<div class="md-flex__cell md-flex__cell--stretch">
<div class="md-flex__ellipsis md-header-nav__title" data-md-component="title">
<span class="md-header-nav__topic">
HttpRunner V2.x 中文使用文档
</span>
<span class="md-header-nav__topic">
测试用例分层
</span>
</div>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<label class="md-icon md-icon--search md-header-nav__button" for="__search"></label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" placeholder="搜索" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="query" data-md-state="active">
<label class="md-icon md-search__icon" for="__search"></label>
<button type="reset" class="md-icon md-search__icon" data-md-component="reset" tabindex="-1">
&#xE5CD;
</button>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" data-md-scrollfix>
<div class="md-search-result" data-md-component="result">
<div class="md-search-result__meta">
键入以开始搜索
</div>
<ol class="md-search-result__list"></ol>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<div class="md-header-nav__source">
<a href="https://github.com/HttpRunner/HttpRunner" title="前往 Github 仓库" class="md-source" data-md-source="github">
<div class="md-source__icon">
<svg viewBox="0 0 24 24" width="24" height="24">
<use xlink:href="#__github" width="24" height="24"></use>
</svg>
</div>
<div class="md-source__repository">
HttpRunner
</div>
</a>
</div>
</div>
</div>
</nav>
</header>
<div class="md-container">
<main class="md-main" role="main">
<div class="md-main__inner md-grid" data-md-component="container">
<div class="md-sidebar md-sidebar--primary" data-md-component="navigation">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary" data-md-level="0">
<label class="md-nav__title md-nav__title--site" for="__drawer">
<a href="../.." title="HttpRunner V2.x 中文使用文档" class="md-nav__button md-logo">
<i class="md-icon"></i>
</a>
HttpRunner V2.x 中文使用文档
</label>
<div class="md-nav__source">
<a href="https://github.com/HttpRunner/HttpRunner" title="前往 Github 仓库" class="md-source" data-md-source="github">
<div class="md-source__icon">
<svg viewBox="0 0 24 24" width="24" height="24">
<use xlink:href="#__github" width="24" height="24"></use>
</svg>
</div>
<div class="md-source__repository">
HttpRunner
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../.." title="介绍" class="md-nav__link">
介绍
</a>
</li>
<li class="md-nav__item">
<a href="../../Installation/" title="安装说明" class="md-nav__link">
安装说明
</a>
</li>
<li class="md-nav__item">
<a href="../../quickstart/" title="快速上手" class="md-nav__link">
快速上手
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-4" type="checkbox" id="nav-4">
<label class="md-nav__link" for="nav-4">
基础概念
</label>
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
<label class="md-nav__title" for="nav-4">
基础概念
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../concept/nominal/" title="名词解释" class="md-nav__link">
名词解释
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--nested">
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-5" type="checkbox" id="nav-5" checked>
<label class="md-nav__link" for="nav-5">
测试准备
</label>
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
<label class="md-nav__title" for="nav-5">
测试准备
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../record/" title="录制生成用例" class="md-nav__link">
录制生成用例
</a>
</li>
<li class="md-nav__item">
<a href="../project-structure/" title="项目文件组织" class="md-nav__link">
项目文件组织
</a>
</li>
<li class="md-nav__item">
<a href="../testcase-structure/" title="测试用例组织" class="md-nav__link">
测试用例组织
</a>
</li>
<li class="md-nav__item">
<a href="../request-hook/" title="hook机制" class="md-nav__link">
hook机制
</a>
</li>
<li class="md-nav__item">
<a href="../dot-env/" title="环境变量" class="md-nav__link">
环境变量
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-toggle md-nav__toggle" data-md-toggle="toc" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
测试用例分层
</label>
<a href="./" title="测试用例分层" class="md-nav__link md-nav__link--active">
测试用例分层
</a>
<nav class="md-nav md-nav--secondary">
<label class="md-nav__title" for="__toc">目录</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="#_1" class="md-nav__link">
测试用例分层模型
</a>
</li>
<li class="md-nav__item">
<a href="#_2" class="md-nav__link">
分层描述详解
</a>
<nav class="md-nav">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#api" class="md-nav__link">
接口定义API
</a>
</li>
<li class="md-nav__item">
<a href="#testcase" class="md-nav__link">
测试用例testcase
</a>
<nav class="md-nav">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#_3" class="md-nav__link">
引用接口定义
</a>
</li>
<li class="md-nav__item">
<a href="#_4" class="md-nav__link">
引用测试用例
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#testsuite" class="md-nav__link">
测试用例集testsuite
</a>
<nav class="md-nav">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#_5" class="md-nav__link">
非参数化场景
</a>
</li>
<li class="md-nav__item">
<a href="#parameters" class="md-nav__link">
参数化场景parameters
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#_6" class="md-nav__link">
文件目录结构管理 &amp;&amp; 脚手架工具
</a>
</li>
<li class="md-nav__item">
<a href="#_7" class="md-nav__link">
相关参考
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../parameters/" title="参数化数据驱动" class="md-nav__link">
参数化数据驱动
</a>
</li>
<li class="md-nav__item">
<a href="../validate-pretty/" title="Validate & Prettify" class="md-nav__link">
Validate & Prettify
</a>
</li>
<li class="md-nav__item">
<a href="../security/" title="信息安全" class="md-nav__link">
信息安全
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-6" type="checkbox" id="nav-6">
<label class="md-nav__link" for="nav-6">
测试执行
</label>
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
<label class="md-nav__title" for="nav-6">
测试执行
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../run-tests/cli/" title="运行测试(CLI)" class="md-nav__link">
运行测试(CLI)
</a>
</li>
<li class="md-nav__item">
<a href="../../run-tests/report/" title="测试报告" class="md-nav__link">
测试报告
</a>
</li>
<li class="md-nav__item">
<a href="../../run-tests/load-test/" title="性能测试" class="md-nav__link">
性能测试
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-7" type="checkbox" id="nav-7">
<label class="md-nav__link" for="nav-7">
开发扩展
</label>
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
<label class="md-nav__title" for="nav-7">
开发扩展
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../development/architecture/" title="Pipline" class="md-nav__link">
Pipline
</a>
</li>
<li class="md-nav__item">
<a href="../../development/dev-api/" title="基础库调用" class="md-nav__link">
基础库调用
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../../FAQ/" title="FAQ" class="md-nav__link">
FAQ
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-9" type="checkbox" id="nav-9">
<label class="md-nav__link" for="nav-9">
实践案例
</label>
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
<label class="md-nav__title" for="nav-9">
实践案例
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../examples/testerhome-login/" title="TesterHome 登录" class="md-nav__link">
TesterHome 登录
</a>
</li>
<li class="md-nav__item">
<a href="../../examples/demo-klook/" title="klook" class="md-nav__link">
klook
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../../related-docs/" title="相关资料" class="md-nav__link">
相关资料
</a>
</li>
<li class="md-nav__item">
<a href="../../CHANGELOG/" title="CHANGELOG" class="md-nav__link">
CHANGELOG
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="toc">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary">
<label class="md-nav__title" for="__toc">目录</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="#_1" class="md-nav__link">
测试用例分层模型
</a>
</li>
<li class="md-nav__item">
<a href="#_2" class="md-nav__link">
分层描述详解
</a>
<nav class="md-nav">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#api" class="md-nav__link">
接口定义API
</a>
</li>
<li class="md-nav__item">
<a href="#testcase" class="md-nav__link">
测试用例testcase
</a>
<nav class="md-nav">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#_3" class="md-nav__link">
引用接口定义
</a>
</li>
<li class="md-nav__item">
<a href="#_4" class="md-nav__link">
引用测试用例
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#testsuite" class="md-nav__link">
测试用例集testsuite
</a>
<nav class="md-nav">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#_5" class="md-nav__link">
非参数化场景
</a>
</li>
<li class="md-nav__item">
<a href="#parameters" class="md-nav__link">
参数化场景parameters
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#_6" class="md-nav__link">
文件目录结构管理 &amp;&amp; 脚手架工具
</a>
</li>
<li class="md-nav__item">
<a href="#_7" class="md-nav__link">
相关参考
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content">
<article class="md-content__inner md-typeset">
<h1>测试用例分层</h1>
<h2 id="_1">测试用例分层模型<a class="headerlink" href="#_1" title="Permanent link">&para;</a></h2>
<p>在自动化测试领域,自动化测试用例的可维护性是极其重要的因素,直接关系到自动化测试能否持续有效地在项目中开展。</p>
<p>概括来说,测试用例分层机制的核心是将接口定义、测试步骤、测试用例、测试场景进行分离,单独进行描述和维护,从而尽可能地减少自动化测试用例的维护成本。</p>
<p>逻辑关系图如下所示:</p>
<p><img alt="" src="../../images/testcase-layer.png" /></p>
<p>同时,强调如下几点核心概念:</p>
<ul>
<li>测试用例testcase应该是完整且独立的每条测试用例应该是都可以独立运行的</li>
<li>测试用例是测试步骤teststep<code>有序</code> 集合,每一个测试步骤对应一个 API 的请求描述</li>
<li>测试用例集testsuite是测试用例的 <code>无序</code> 集合,集合中的测试用例应该都是相互独立,不存在先后依赖关系的;如果确实存在先后依赖关系,那就需要在测试用例中完成依赖的处理</li>
</ul>
<p>如果对于上述第三点感觉难以理解,不妨看下上图中的示例:</p>
<ul>
<li>testcase1 依赖于 testcase2那么就可以在测试步骤teststep12中对 testcase2 进行引用,然后 testcase1 就是完整且可独立运行的;</li>
<li>在 testsuite 中testcase1 与 testcase2 相互独立,运行顺序就不再有先后依赖关系了。</li>
</ul>
<h2 id="_2">分层描述详解<a class="headerlink" href="#_2" title="Permanent link">&para;</a></h2>
<p>理解了测试用例分层模型,接下来我们再来看下在分层模型下,接口、测试用例、测试用例集的描述形式。</p>
<h3 id="api">接口定义API<a class="headerlink" href="#api" title="Permanent link">&para;</a></h3>
<p>为了更好地对接口描述进行管理,推荐使用独立的文件对接口描述进行存储,即每个文件对应一个接口描述。</p>
<p>接口定义描述的主要内容包括:<strong>name</strong>、variables、<strong>request</strong>、base_url、validate 等,形式如下:</p>
<div class="codehilite"><pre><span></span><span class="nt">name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">get headers</span>
<span class="nt">base_url</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">http://httpbin.org</span>
<span class="nt">variables</span><span class="p">:</span>
<span class="nt">expected_status_code</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">200</span>
<span class="nt">request</span><span class="p">:</span>
<span class="nt">url</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">/headers</span>
<span class="nt">method</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">GET</span>
<span class="nt">validate</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="nt">eq</span><span class="p">:</span> <span class="p p-Indicator">[</span><span class="s">&quot;status_code&quot;</span><span class="p p-Indicator">,</span> <span class="nv">$expected_status_code</span><span class="p p-Indicator">]</span>
<span class="p p-Indicator">-</span> <span class="nt">eq</span><span class="p">:</span> <span class="p p-Indicator">[</span><span class="nv">content.headers.Host</span><span class="p p-Indicator">,</span> <span class="s">&quot;httpbin.org&quot;</span><span class="p p-Indicator">]</span>
</pre></div>
<p>其中name 和 request 部分是必须的request 中的描述形式与 <a href="http://docs.python-requests.org/en/master/api/">requests.request</a> 完全相同。</p>
<p>另外API 描述需要尽量保持完整,做到可以单独运行。如果在接口描述中存在变量引用的情况,可在 variables 中对参数进行定义。通过这种方式,可以很好地实现单个接口的调试。</p>
<div class="codehilite"><pre><span></span>$ hrun api/get_headers.yml
INFO Start to run testcase: get headers
headers
INFO GET http://httpbin.org/headers
INFO status_code: <span class="m">200</span>, response_time<span class="o">(</span>ms<span class="o">)</span>: <span class="m">477</span>.32 ms, response_length: <span class="m">157</span> bytes
.
----------------------------------------------------------------------
Ran <span class="m">1</span> <span class="nb">test</span> in <span class="m">0</span>.478s
OK
</pre></div>
<h3 id="testcase">测试用例testcase<a class="headerlink" href="#testcase" title="Permanent link">&para;</a></h3>
<h4 id="_3">引用接口定义<a class="headerlink" href="#_3" title="Permanent link">&para;</a></h4>
<p>有了接口的定义描述后,我们编写测试场景时就可以直接引用接口定义了。</p>
<p>在测试步骤teststep可通过 <code>api</code> 字段引用接口定义,引用方式为对应 API 文件的路径,绝对路径或相对路径均可。推荐使用相对路径,路径基准为项目根目录,即 <code>debugtalk.py</code> 所在的目录路径。</p>
<div class="codehilite"><pre><span></span><span class="p p-Indicator">-</span> <span class="nt">config</span><span class="p">:</span>
<span class="nt">name</span><span class="p">:</span> <span class="s">&quot;setup</span><span class="nv"> </span><span class="s">and</span><span class="nv"> </span><span class="s">reset</span><span class="nv"> </span><span class="s">all.&quot;</span>
<span class="nt">variables</span><span class="p">:</span>
<span class="nt">user_agent</span><span class="p">:</span> <span class="s">&#39;iOS/10.3&#39;</span>
<span class="nt">device_sn</span><span class="p">:</span> <span class="s">&quot;TESTCASE_SETUP_XXX&quot;</span>
<span class="nt">os_platform</span><span class="p">:</span> <span class="s">&#39;ios&#39;</span>
<span class="nt">app_version</span><span class="p">:</span> <span class="s">&#39;2.8.6&#39;</span>
<span class="nt">base_url</span><span class="p">:</span> <span class="s">&quot;http://127.0.0.1:5000&quot;</span>
<span class="nt">verify</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">False</span>
<span class="nt">output</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">session_token</span>
<span class="p p-Indicator">-</span> <span class="nt">test</span><span class="p">:</span>
<span class="nt">name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">get token (setup)</span>
<span class="nt">api</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">api/get_token.yml</span>
<span class="nt">variables</span><span class="p">:</span>
<span class="nt">user_agent</span><span class="p">:</span> <span class="s">&#39;iOS/10.3&#39;</span>
<span class="nt">device_sn</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">$device_sn</span>
<span class="nt">os_platform</span><span class="p">:</span> <span class="s">&#39;ios&#39;</span>
<span class="nt">app_version</span><span class="p">:</span> <span class="s">&#39;2.8.6&#39;</span>
<span class="nt">extract</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="nt">session_token</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">content.token</span>
<span class="nt">validate</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="nt">eq</span><span class="p">:</span> <span class="p p-Indicator">[</span><span class="s">&quot;status_code&quot;</span><span class="p p-Indicator">,</span> <span class="nv">200</span><span class="p p-Indicator">]</span>
<span class="p p-Indicator">-</span> <span class="nt">len_eq</span><span class="p">:</span> <span class="p p-Indicator">[</span><span class="s">&quot;content.token&quot;</span><span class="p p-Indicator">,</span> <span class="nv">16</span><span class="p p-Indicator">]</span>
<span class="p p-Indicator">-</span> <span class="nt">test</span><span class="p">:</span>
<span class="nt">name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">reset all users</span>
<span class="nt">api</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">api/reset_all.yml</span>
<span class="nt">variables</span><span class="p">:</span>
<span class="nt">token</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">$session_token</span>
</pre></div>
<p>若需要控制或改变接口定义中的参数值,可在测试步骤中指定 variables 参数,覆盖 API 中的 variables 实现。</p>
<p>同样地,在测试步骤中定义 validate 后,也会与 API 中的 validate 合并覆盖。因此推荐的做法是,在 API 定义中的 validate 只描述最基本的校验项,例如 status_code对于与业务逻辑相关的更多校验项在测试步骤的 validate 中进行描述。</p>
<h4 id="_4">引用测试用例<a class="headerlink" href="#_4" title="Permanent link">&para;</a></h4>
<p>在测试用例的测试步骤中,除了可以引用接口定义,还可以引用其它测试用例。通过这种方式,可以在避免重复描述的同时,解决测试用例的依赖关系,从而保证每个测试用例都是独立可运行的。</p>
<p>在测试步骤teststep可通过 <code>testcase</code> 字段引用其它测试用例,引用方式为对应测试用例文件的路径,绝对路径或相对路径均可。推荐使用相对路径,路径基准为项目根目录,即 <code>debugtalk.py</code> 所在的目录路径。</p>
<p>例如,在上面的测试用例("setup and reset all.")中,实现了对获取 token 功能的测试;同时,在很多其它功能中都会依赖于获取 token 的功能,如果将该功能的测试步骤脚本拷贝到其它功能的测试用例中,那么就会存在大量重复,当需要对该部分进行修改时就需要修改所有地方,显然不便于维护。</p>
<p>比较好的做法是,在其它功能的测试用例(如创建用户)中,引用获取 token 功能的测试用例testcases/setup.yml作为一个测试步骤从而创建用户"create user and check result.")这个测试用例也变得独立可运行了。</p>
<div class="codehilite"><pre><span></span><span class="p p-Indicator">-</span> <span class="nt">config</span><span class="p">:</span>
<span class="nt">name</span><span class="p">:</span> <span class="s">&quot;create</span><span class="nv"> </span><span class="s">user</span><span class="nv"> </span><span class="s">and</span><span class="nv"> </span><span class="s">check</span><span class="nv"> </span><span class="s">result.&quot;</span>
<span class="nt">id</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">create_user</span>
<span class="nt">base_url</span><span class="p">:</span> <span class="s">&quot;http://127.0.0.1:5000&quot;</span>
<span class="nt">variables</span><span class="p">:</span>
<span class="nt">uid</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">9001</span>
<span class="nt">device_sn</span><span class="p">:</span> <span class="s">&quot;TESTCASE_CREATE_XXX&quot;</span>
<span class="nt">output</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">session_token</span>
<span class="p p-Indicator">-</span> <span class="nt">test</span><span class="p">:</span>
<span class="nt">name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">setup and reset all (override) for $device_sn.</span>
<span class="nt">testcase</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">testcases/setup.yml</span>
<span class="nt">output</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">session_token</span>
<span class="p p-Indicator">-</span> <span class="nt">test</span><span class="p">:</span>
<span class="nt">name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">create user and check result.</span>
<span class="nt">variables</span><span class="p">:</span>
<span class="nt">token</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">$session_token</span>
<span class="nt">testcase</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">testcases/deps/check_and_create.yml</span>
</pre></div>
<h3 id="testsuite">测试用例集testsuite<a class="headerlink" href="#testsuite" title="Permanent link">&para;</a></h3>
<p>当测试用例数量比较多以后,为了方便管理和实现批量运行,通常需要使用测试用例集来对测试用例进行组织。</p>
<p>在前文的测试用例分层模型中也强调了测试用例集testsuite是测试用例的 <code>无序</code> 集合,集合中的测试用例应该都是相互独立,不存在先后依赖关系的;如果确实存在先后依赖关系,那就需要在测试用例中完成依赖的处理。</p>
<p>因为是 <code>无序</code> 集合,因此测试用例集的描述形式会与测试用例有些不同,在每个测试用例集文件中,第一层级存在两类字段:</p>
<ul>
<li>config: 测试用例集的总体配置参数</li>
<li>testcases: 值为字典结构无序key 为测试用例的名称value 为测试用例的内容;在引用测试用例时也可以指定 variables实现对引用测试用例中 variables 的覆盖。</li>
</ul>
<h4 id="_5">非参数化场景<a class="headerlink" href="#_5" title="Permanent link">&para;</a></h4>
<div class="codehilite"><pre><span></span><span class="nt">config</span><span class="p">:</span>
<span class="nt">name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">create users with uid</span>
<span class="nt">variables</span><span class="p">:</span>
<span class="nt">device_sn</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">${gen_random_string(15)}</span>
<span class="nt">var_a</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">${gen_random_string(5)}</span>
<span class="nt">var_b</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">$var_a</span>
<span class="nt">base_url</span><span class="p">:</span> <span class="s">&quot;http://127.0.0.1:5000&quot;</span>
<span class="nt">testcases</span><span class="p">:</span>
<span class="nt">create user 1000 and check result.</span><span class="p">:</span>
<span class="nt">testcase</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">testcases/create_user.yml</span>
<span class="nt">variables</span><span class="p">:</span>
<span class="nt">uid</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">1000</span>
<span class="nt">var_c</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">${gen_random_string(5)}</span>
<span class="nt">var_d</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">$var_c</span>
<span class="nt">create user 1001 and check result.</span><span class="p">:</span>
<span class="nt">testcase</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">testcases/create_user.yml</span>
<span class="nt">variables</span><span class="p">:</span>
<span class="nt">uid</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">1001</span>
<span class="nt">var_c</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">${gen_random_string(5)}</span>
<span class="nt">var_d</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">$var_c</span>
</pre></div>
<h4 id="parameters">参数化场景parameters<a class="headerlink" href="#parameters" title="Permanent link">&para;</a></h4>
<p>对于参数化场景,可通过 parameters 实现,描述形式如下所示。</p>
<div class="codehilite"><pre><span></span><span class="nt">config</span><span class="p">:</span>
<span class="nt">name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">create users with parameters</span>
<span class="nt">variables</span><span class="p">:</span>
<span class="nt">device_sn</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">${gen_random_string(15)}</span>
<span class="nt">base_url</span><span class="p">:</span> <span class="s">&quot;http://127.0.0.1:5000&quot;</span>
<span class="nt">testcases</span><span class="p">:</span>
<span class="nt">create user $uid and check result for $device_sn.</span><span class="p">:</span>
<span class="nt">testcase</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">testcases/create_user.yml</span>
<span class="nt">variables</span><span class="p">:</span>
<span class="nt">uid</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">1000</span>
<span class="nt">device_sn</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">TESTSUITE_XXX</span>
<span class="nt">parameters</span><span class="p">:</span>
<span class="nt">uid</span><span class="p">:</span> <span class="p p-Indicator">[</span><span class="nv">101</span><span class="p p-Indicator">,</span> <span class="nv">102</span><span class="p p-Indicator">,</span> <span class="nv">103</span><span class="p p-Indicator">]</span>
<span class="nt">device_sn</span><span class="p">:</span> <span class="p p-Indicator">[</span><span class="nv">TESTSUITE_X1</span><span class="p p-Indicator">,</span> <span class="nv">TESTSUITE_X2</span><span class="p p-Indicator">]</span>
</pre></div>
<p>参数化后parameters 中的变量将采用笛卡尔积组合形成参数列表,依次覆盖 variables 中的参数,驱动测试用例的运行。</p>
<h2 id="_6">文件目录结构管理 &amp;&amp; 脚手架工具<a class="headerlink" href="#_6" title="Permanent link">&para;</a></h2>
<p>在对测试用例文件进行组织管理时,对于文件的存储位置均没有要求和限制,在引用时只需要指定对应的文件路径即可。但从约定大于配置的角度,最好是按照推荐的文件夹名称进行存储管理,并可通过子目录实现项目模块分类管理。</p>
<p>推荐的方式汇总如下:</p>
<ul>
<li><code>debugtalk.py</code> 放置在项目根目录下,假设为 <code>PRJ_ROOT_DIR</code></li>
<li><code>.env</code> 放置在项目根目录下,路径为 <code>PRJ_ROOT_DIR/.env</code></li>
<li>接口定义API放置在 <code>PRJ_ROOT_DIR/api/</code> 目录下</li>
<li>测试用例testcase放置在 <code>PRJ_ROOT_DIR/testcases/</code> 目录下</li>
<li>测试用例集testsuite文件必须放置在 <code>PRJ_ROOT_DIR/testsuites/</code> 目录下</li>
<li>data 文件夹:存储参数化文件,或者项目依赖的文件,路径为 <code>PRJ_ROOT_DIR/data/</code></li>
<li>reports 文件夹:存储 HTML 测试报告,生成路径为 <code>PRJ_ROOT_DIR/reports/</code></li>
</ul>
<p>目录结构如下所示:</p>
<div class="codehilite"><pre><span></span>$ tree tests
tests
├── .env
├── data
│ ├── app_version.csv
│ └── account.csv
├── api
│ ├── create_user.yml
│ ├── get_headers.yml
│ ├── get_token.yml
│ ├── get_user.yml
│ └── reset_all.yml
├── debugtalk.py
├── testcases
│ ├── create_user.yml
│ ├── deps
│ │ └── check_and_create.yml
│ └── setup.yml
└── testsuites
├── create_users.yml
└── create_users_with_parameters.yml
</pre></div>
<p><strong>项目脚手架</strong></p>
<p>同时,在 <code>HttpRunner</code> 中实现了一个脚手架工具,可以快速创建项目的目录结构。该想法来源于 <code>Django</code><code>django-admin.py startproject project_name</code></p>
<p>使用方式也与 <code>Django</code> 类似,只需要通过 <code>--startproject</code> 指定新项目的名称即可。</p>
<div class="codehilite"><pre><span></span>$ hrun --startproject demo
Start to create new project: demo
CWD: /Users/debugtalk/MyProjects/examples
created folder: demo
created folder: demo/api
created folder: demo/testcases
created folder: demo/testsuites
created folder: demo/reports
created file: demo/debugtalk.py
created file: demo/.env
</pre></div>
<h2 id="_7">相关参考<a class="headerlink" href="#_7" title="Permanent link">&para;</a></h2>
<ul>
<li><a href="/post/HttpRunner-testcase-layer">《HttpRunner 的测试用例分层机制(已过期)》</a></li>
<li>测试用例分层详细示例:<a href="https://github.com/HttpRunner/HttpRunner/tree/master/tests">HttpRunner/tests</a></li>
</ul>
</article>
</div>
</div>
</main>
<footer class="md-footer">
<div class="md-footer-nav">
<nav class="md-footer-nav__inner md-grid">
<a href="../dot-env/" title="环境变量" class="md-flex md-footer-nav__link md-footer-nav__link--prev" rel="prev">
<div class="md-flex__cell md-flex__cell--shrink">
<i class="md-icon md-icon--arrow-back md-footer-nav__button"></i>
</div>
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
<span class="md-flex__ellipsis">
<span class="md-footer-nav__direction">
上一页
</span>
环境变量
</span>
</div>
</a>
<a href="../parameters/" title="参数化数据驱动" class="md-flex md-footer-nav__link md-footer-nav__link--next" rel="next">
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
<span class="md-flex__ellipsis">
<span class="md-footer-nav__direction">
下一页
</span>
参数化数据驱动
</span>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<i class="md-icon md-icon--arrow-forward md-footer-nav__button"></i>
</div>
</a>
</nav>
</div>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-footer-copyright">
<div class="md-footer-copyright__highlight">
Copyright &copy; 2017 - 2019 debugtalk
</div>
powered by
<a href="https://www.mkdocs.org">MkDocs</a>
and
<a href="https://squidfunk.github.io/mkdocs-material/">
Material for MkDocs</a>
</div>
<div class="md-footer-social">
<link rel="stylesheet" href="../../assets/fonts/font-awesome.css">
<a href="https://debugtalk.com" class="md-footer-social__link fa fa-globe"></a>
<a href="https://github.com/httprunner" class="md-footer-social__link fa fa-github"></a>
</div>
</div>
</div>
</footer>
</div>
<script src="../../assets/javascripts/application.ac79c3b0.js"></script>
<script src="../../assets/javascripts/lunr/lunr.stemmer.support.js"></script>
<script>app.initialize({version:"1.0.4",url:{base:"../.."}})</script>
<script src="../../js/slardar.js"></script>
</body>
</html>