<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Pytest从入门到精通 on 重定向学院</title><link>https://itest2025.pages.dev/categories/pytest%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E7%B2%BE%E9%80%9A/</link><description>Recent content in Pytest从入门到精通 on 重定向学院</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Tue, 17 Jun 2025 17:29:15 +0800</lastBuildDate><atom:link href="https://itest2025.pages.dev/categories/pytest%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E7%B2%BE%E9%80%9A/index.xml" rel="self" type="application/rss+xml"/><item><title>1.快速开始</title><link>https://itest2025.pages.dev/tutorials/25-001-1%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/</link><pubDate>Thu, 02 Nov 2017 07:22:42 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/25-001-1%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B/</guid><description>&lt;h3 id="简介">简介&lt;/h3>
&lt;p>pytest测试框架可以让我们很方便的编写测试用例，这些用例写起来虽然简单，但仍然可以规模化以及编写更加复杂的测试用例。&lt;/p>
&lt;p>&lt;a href="https://docs.pytest.org/en/latest/index.html" target="_blank" rel="noopener noreffer ">官方文档&lt;/a>&lt;/p>
&lt;h3 id="安装">安装&lt;/h3>
&lt;pre tabindex="0">&lt;code>pip install -U pytest
&lt;/code>&lt;/pre>&lt;p>用下面的命令去检查一下pytest是否成功安装&lt;/p>
&lt;pre tabindex="0">&lt;code>$ pytest --version
This is pytest version 3.x.y, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py
&lt;/code>&lt;/pre>&lt;h3 id="快速开始">快速开始&lt;/h3>
&lt;p>创建名为&lt;code>test_quick_start.py&lt;/code>的文件，敲如下内容&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">reverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">string&lt;/span>&lt;span class="p">[::&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_reverse&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">string&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;good&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">reverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;doog&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">another_string&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;itest&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">reverse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">another_string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;tseti&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面的代码做了2件事情&lt;/p>
&lt;ul>
&lt;li>定义了名为&lt;code>reverse(string)&lt;/code>的全局函数，作用是把string反转并返回。比如输入&amp;quot;abc&amp;quot;会反转成&amp;quot;cba&amp;quot;&lt;/li>
&lt;li>定义了名为&lt;code>test_reverse()&lt;/code>的函数，包含了2个断言，用来测试&lt;code>reverse()&lt;/code>方法的正确性&lt;/li>
&lt;/ul>
&lt;p>在命令行中使用下面的命令去运行用例&lt;/p>
&lt;pre tabindex="0">&lt;code>pytest
&lt;/code>&lt;/pre>&lt;p>结果应该大致如下&lt;/p>
&lt;pre tabindex="0">&lt;code>========================================================================= test session starts =========================================================================
platform darwin -- Python 2.7.12, pytest-3.2.3, py-1.4.34, pluggy-0.4.0
rootdir: /Users/easonhan/code/testclass.net/src/pytest, inifile:
collected 1 item

test_quick_start.py .

====================================================================== 1 passed in 0.01 seconds =======================================================================
&lt;/code>&lt;/pre>&lt;h3 id="总结">总结&lt;/h3>
&lt;p>这是最简单的单元测试实例，实现了用代码去测试代码的目的。&lt;/p>
&lt;p>原始封面&lt;/p>
&lt;p>&lt;img
 class="lazyload"
 src="https://itest2025.pages.dev/svg/loading.min.svg"
 data-src="https://images.unsplash.com/photo-1533880020541-22c0ff17d4a2?w=300"
 data-srcset="https://images.unsplash.com/photo-1533880020541-22c0ff17d4a2?w=300, https://images.unsplash.com/photo-1533880020541-22c0ff17d4a2?w=300 1.5x, https://images.unsplash.com/photo-1533880020541-22c0ff17d4a2?w=300 2x"
 data-sizes="auto"
 alt="https://images.unsplash.com/photo-1533880020541-22c0ff17d4a2?w=300"
 title="课程图片" />&lt;/p></description></item><item><title>2.运行多个文件</title><link>https://itest2025.pages.dev/tutorials/25-002-2%E8%BF%90%E8%A1%8C%E5%A4%9A%E4%B8%AA%E6%96%87%E4%BB%B6/</link><pubDate>Wed, 01 Nov 2017 07:22:42 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/25-002-2%E8%BF%90%E8%A1%8C%E5%A4%9A%E4%B8%AA%E6%96%87%E4%BB%B6/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>在现实的测试活动中，我们经常会定义多个用例文件，用来实现业务逻辑或其他逻辑上用例的物理分隔，比如&lt;/p>
&lt;pre tabindex="0">&lt;code>login_test.py # 登录相关功能的测试用例
cart_test.py # 购物车相关功能的测试用例
checkout_test.py # 结算相关功能的用例
order_test.py # 订单相关功能的用例
&lt;/code>&lt;/pre>&lt;p>使用pytest可以很方便的执行批量执行一组文件中定义的用例。&lt;/p>
&lt;h3 id="潜规则">潜规则&lt;/h3>
&lt;p>在批量执行用例之前，我们需要了解一下pytest的潜规则，注意，由于pytest可以支持丰富的定制选项，下面的潜规则是在&lt;strong>没有定制的默认情况下&lt;/strong>的缺省规则&lt;/p>
&lt;ul>
&lt;li>pytest会找当前以及递查找子文件夹下面所有的&lt;code>test_*.py&lt;/code>或&lt;code>*_test.py&lt;/code>的文件，把其当作测试文件&lt;/li>
&lt;li>在这些文件里，pytest会收集下面的一些函数或方法，当作测试用例
&lt;ul>
&lt;li>不在类定义中的以&lt;code>test_&lt;/code>开头的函数或方法&lt;/li>
&lt;li>在以&lt;code>Test&lt;/code>开头的类中(不能包含&lt;code>__init__&lt;/code>方法)，以&lt;code>test_&lt;/code>开头的方法&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>pytest也支持unittest模式的用例定义&lt;/li>
&lt;/ul>
&lt;h3 id="实例">实例&lt;/h3>
&lt;p>新建&lt;code>test_calc.py&lt;/code>文件，与上一节的&lt;code>test_quick_start.py&lt;/code>放在同一文件夹下，敲入下面的内容&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">x&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">y&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_add&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">2&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">99&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">100&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>现在当前文件夹下应该有2个文件&lt;/p>
&lt;pre tabindex="0">&lt;code>test_calc.py
test_quick_start.py
&lt;/code>&lt;/pre>&lt;h3 id="运行">运行&lt;/h3>
&lt;p>在当前文件夹敲下面的命令&lt;/p>
&lt;pre tabindex="0">&lt;code>pytest
&lt;/code>&lt;/pre>&lt;p>结果大致应该如下所示&lt;/p>
&lt;pre tabindex="0">&lt;code>$ pytest
========================================================================= test session starts =========================================================================
platform darwin -- Python 2.7.12, pytest-3.2.3, py-1.4.34, pluggy-0.4.0
rootdir: /Users/easonhan/code/testclass.net/src/pytest, inifile:
collected 2 items

test_calc.py .
test_quick_start.py .

====================================================================== 2 passed in 0.01 seconds =======================================================================
&lt;/code>&lt;/pre>&lt;h3 id="总结">总结&lt;/h3>
&lt;p>上面的例子里&lt;/p>
&lt;ul>
&lt;li>所有以test_开头的py文件都被当成了测试文件&lt;/li>
&lt;li>所有测试文件中以test开头的方法被当成了测试用例执行&lt;/li>
&lt;/ul>
&lt;p>原始封面&lt;/p></description></item><item><title>3.Assert</title><link>https://itest2025.pages.dev/tutorials/25-003-3assert/</link><pubDate>Wed, 01 Nov 2017 07:22:42 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/25-003-3assert/</guid><description>&lt;p>Assert就是断言，每个测试用例都需要断言。&lt;/p>
&lt;p>与unittest不同，pytest使用的是python自带的assert关键字来进行断言，大大降低了学习成本。&lt;/p>
&lt;p>assert关键字后面可以接一个表达式，只要表达式的最终结果为True，那么断言通过，用例执行成功，否则用例执行失败。&lt;/p>
&lt;h3 id="详尽的用例失败描述">详尽的用例失败描述&lt;/h3>
&lt;p>pytest的用例失败描述非常详尽，一目了人。考虑下面的例子&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># content of test_assert1.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">f&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_function&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="mi">4&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>执行上面的用例&lt;/p>
&lt;pre tabindex="0">&lt;code>$ pytest test_assert1.py
======= test session starts ========
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 item

test_assert1.py F

======= FAILURES ========
_______ test_function ________

 def test_function():
&amp;gt; assert f() == 4
E assert 3 == 4
E + where 3 = f()

test_assert1.py:5: AssertionError
======= 1 failed in 0.12 seconds ========
&lt;/code>&lt;/pre>&lt;p>可以很明显的看出，pytest给出的错误提示是：f()的值是3，也就是实际结果是3，而预期结果是4，3不等于4，因此断言未通过，用例失败。&lt;/p></description></item><item><title>4.Fixture</title><link>https://itest2025.pages.dev/tutorials/25-004-4fixture/</link><pubDate>Tue, 31 Oct 2017 07:22:42 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/25-004-4fixture/</guid><description>&lt;p>我们可以简单的把Fixture理解为准备测试数据和初始化测试对象的阶段。&lt;/p>
&lt;p>一般我们对测试数据和测试对象的管理有这样的一些场景&lt;/p>
&lt;ul>
&lt;li>所有用例开始之前初始化测试数据或对象&lt;/li>
&lt;li>所有用例结束之后销毁测试数据或对象&lt;/li>
&lt;li>每个用例开始之前初始化测试数据或对象&lt;/li>
&lt;li>每个用例结束之后销毁测试数据或对象&lt;/li>
&lt;li>在每个／所有module的用例开始之前初始化数据或对象&lt;/li>
&lt;li>在每个／所有module的用例开始之后销毁数据或对象&lt;/li>
&lt;li>&amp;hellip;&amp;hellip;&lt;/li>
&lt;li>&amp;hellip;&amp;hellip;&lt;/li>
&lt;/ul>
&lt;p>pytest的fixture特性可以满足上面的需求。&lt;/p>
&lt;h3 id="简单的例子">简单的例子&lt;/h3>
&lt;p>考虑这种场景，我们需要判断用户的密码中包含简单密码，规则是这样的，密码必须至少6位，满足6位的话判断用户的密码不是password123或者password之类的弱密码。&lt;/p>
&lt;p>我们将用户的信息导出成名为users.dev.json的文件，该文件如下所示&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;jack&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;password&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;Iloverose&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;rose&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;password&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;Ilovejack&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;tom&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;password&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;password123&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>新建名为test_user_password.py的文件，键入以下内容，一定要&lt;strong>保证users.dev.json文件与该文件在同一路径下&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">pytest&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">json&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">TestUserPassword&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">object&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@pytest.fixture&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">users&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">loads&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;./users.dev.json&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;r&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">read&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="c1"># 读取当前路径下的users.dev.json文件，返回的结果是dict&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">test_user_password&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">users&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 遍历每条user数据&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">user&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">users&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">passwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">user&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;password&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">passwd&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">6&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">msg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;user &lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s2"> has a weak password&amp;#34;&lt;/span> &lt;span class="o">%&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">passwd&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s1">&amp;#39;password&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">msg&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">passwd&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s1">&amp;#39;password123&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">msg&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="运行">运行&lt;/h3>
&lt;p>pytest可以通过指定文件名的方式运行单个用例文件，这里我们只运行test_user_password.py文件&lt;/p>
&lt;pre tabindex="0">&lt;code>pytest test_user_password.py
&lt;/code>&lt;/pre>&lt;h3 id="运行结果">运行结果&lt;/h3>
&lt;pre tabindex="0">&lt;code>$ pytest test_user_password.py
========================================================================= test session starts =========================================================================
platform darwin -- Python 2.7.12, pytest-3.2.3, py-1.4.34, pluggy-0.4.0
rootdir: /Users/easonhan/code/testclass.net/src/pytest, inifile:
collected 1 item

test_user_password.py F

============================================================================== FAILURES ===============================================================================
_________________________________________________________________ TestUserPassword.test_user_password _________________________________________________________________

self = &amp;lt;test_user_password.TestUserPassword object at 0x1046e3290&amp;gt;
users = [{&amp;#39;name&amp;#39;: &amp;#39;jack&amp;#39;, &amp;#39;password&amp;#39;: &amp;#39;Iloverose&amp;#39;}, {&amp;#39;name&amp;#39;: &amp;#39;rose&amp;#39;, &amp;#39;password&amp;#39;: &amp;#39;Ilovejack&amp;#39;}, {&amp;#39;name&amp;#39;: &amp;#39;tom&amp;#39;, &amp;#39;password&amp;#39;: &amp;#39;password123&amp;#39;}]

 def test_user_password(self, users):
 for user in users:
 passwd = user[&amp;#39;password&amp;#39;]
 assert len(passwd) &amp;gt;= 6
 msg = &amp;#34;user %s has a weak password&amp;#34; %(user[&amp;#39;name&amp;#39;])
 assert passwd != &amp;#39;password&amp;#39;, msg
&amp;gt; assert passwd != &amp;#39;password123&amp;#39;, msg
E AssertionError: user tom has a weak password
E assert &amp;#39;password123&amp;#39; != &amp;#39;password123&amp;#39;

test_user_password.py:14: AssertionError
====================================================================== 1 failed in 0.03 seconds =======================================================================
&lt;/code>&lt;/pre>&lt;h3 id="分析">分析&lt;/h3>
&lt;ul>
&lt;li>使用@pytest.fixture装饰器可以定义feature&lt;/li>
&lt;li>在用例的参数中传递fixture的名称以便直接调用fixture，拿到fixture的返回值&lt;/li>
&lt;li>3个assert是递进关系，前1个assert断言失败后，后面的assert是不会运行的，因此重要的assert放到前面&lt;/li>
&lt;li>&lt;code> E AssertionError: user tom has a weak password&lt;/code>可以很容易的判断出是哪条数据出了问题，所以定制可读性好的错误信息是很必要的&lt;/li>
&lt;li>任何1个断言失败以后，for循环就会退出，所以上面的用例1次只能发现1条错误数据，换句话说任何1个assert失败后，用例就终止运行了&lt;/li>
&lt;/ul>
&lt;h3 id="执行顺序">执行顺序&lt;/h3>
&lt;p>pytest是这样运行上面的用例的&lt;/p></description></item><item><title>5.参数化的Fixture</title><link>https://itest2025.pages.dev/tutorials/25-005-5%E5%8F%82%E6%95%B0%E5%8C%96%E7%9A%84fixture/</link><pubDate>Mon, 30 Oct 2017 07:22:42 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/25-005-5%E5%8F%82%E6%95%B0%E5%8C%96%E7%9A%84fixture/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>继续上一节的测试需求，在上一节里，任何1条测试数据导致断言不通过后测试用例就会停止运行，这样每次只能检查出1条不符合规范的数据，有没有什么办法可以一次性把所有的不符合结果都测出来呢？&lt;/p>
&lt;p>这时候我们就需要用到参数化的fixture特性了&lt;/p>
&lt;h3 id="更新数据文件">更新数据文件&lt;/h3>
&lt;p>新建&lt;code>users.test.json&lt;/code>文件，内容如下&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;jack&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;password&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;Iloverose&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;rose&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;password&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;Ilovejack&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;tom&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;password&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;password123&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;mike&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;password&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;password&amp;#34;&lt;/span>&lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;james&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="nt">&amp;#34;password&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="s2">&amp;#34;AGoodPasswordWordShouldBeLongEnough&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们增加了2条用户信息，其中mike的密码是弱密码。&lt;/p>
&lt;h3 id="参数化fixture">参数化fixture&lt;/h3>
&lt;p>参数化fixture允许我们向fixture提供参数，参数可以是list，该list中有几条数据，fixture就会运行几次，相应的测试用例也会运行几次。&lt;/p>
&lt;p>参数化fixture的语法是&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@pytest.fixture&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;smtp.gmail.com&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;mail.python.org&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中&lt;code>len(params)&lt;/code>的值就是用例执行的次数&lt;/p>
&lt;p>在fixture的定义中，可以使用&lt;code>request.param&lt;/code>来获取每次传入的参数，如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@pytest.fixture&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">scope&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;module&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">params&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;smtp.gmail.com&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;mail.python.org&amp;#34;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">smtp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">smtp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">smtplib&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">SMTP&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">param&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">587&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">timeout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">yield&lt;/span> &lt;span class="n">smtp&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;finalizing &lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">smtp&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">smtp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">close&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>上面的代码smtp fixture会执行2次&lt;/li>
&lt;li>第1次&lt;code>request.param == 'smtp.gmail.com'&lt;/code>&lt;/li>
&lt;li>第2次&lt;code>request.param == 'mail.python.org'&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="实现用例">实现用例&lt;/h3>
&lt;p>我们现在使用参数化fixtures来实现一次性检查出弱密码的用例。&lt;/p>
&lt;p>新建文件&lt;code>test_user_password_with_params.py&lt;/code>，内容如下:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">pytest&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">json&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">users&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">json&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">loads&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;./users.test.json&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;r&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">read&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">TestUserPasswordWithParam&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">object&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@pytest.fixture&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">params&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">users&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">user&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">param&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">test_user_password&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">user&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">passwd&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">user&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;password&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">passwd&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">6&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">msg&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;user &lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s2"> has a weak password&amp;#34;&lt;/span> &lt;span class="o">%&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">user&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">passwd&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s1">&amp;#39;password&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">msg&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">passwd&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s1">&amp;#39;password123&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">msg&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面的例子里，我们先把所有用户信息读到users变量里，注意users这时候是list类型，可以直接传入到fixture的params&lt;/p>
&lt;h3 id="运行及结果">运行及结果&lt;/h3>
&lt;p>运行&lt;/p>
&lt;pre tabindex="0">&lt;code>pytest test_user_password_with_params.py
&lt;/code>&lt;/pre>&lt;p>结果&lt;/p>
&lt;pre tabindex="0">&lt;code>$ pytest test_user_password_with_params.py
========================================================================= test session starts =========================================================================
platform darwin -- Python 2.7.12, pytest-3.2.3, py-1.4.34, pluggy-0.4.0
rootdir: /Users/easonhan/code/testclass.net/src/pytest, inifile:
collected 5 items

test_user_password_with_params.py ..FF.

============================================================================== FAILURES ===============================================================================
_________________________________________________________ TestUserPasswordWithParam.test_user_password[user2] _________________________________________________________

self = &amp;lt;test_user_password_with_params.TestUserPasswordWithParam object at 0x10de1d790&amp;gt;, user = {&amp;#39;name&amp;#39;: &amp;#39;tom&amp;#39;, &amp;#39;password&amp;#39;: &amp;#39;password123&amp;#39;}

 def test_user_password(self, user):
 passwd = user[&amp;#39;password&amp;#39;]
 assert len(passwd) &amp;gt;= 6
 msg = &amp;#34;user %s has a weak password&amp;#34; %(user[&amp;#39;name&amp;#39;])
 assert passwd != &amp;#39;password&amp;#39;, msg
&amp;gt; assert passwd != &amp;#39;password123&amp;#39;, msg
E AssertionError: user tom has a weak password
E assert &amp;#39;password123&amp;#39; != &amp;#39;password123&amp;#39;

test_user_password_with_params.py:15: AssertionError
_________________________________________________________ TestUserPasswordWithParam.test_user_password[user3] _________________________________________________________

self = &amp;lt;test_user_password_with_params.TestUserPasswordWithParam object at 0x10de1df50&amp;gt;, user = {&amp;#39;name&amp;#39;: &amp;#39;mike&amp;#39;, &amp;#39;password&amp;#39;: &amp;#39;password&amp;#39;}

 def test_user_password(self, user):
 passwd = user[&amp;#39;password&amp;#39;]
 assert len(passwd) &amp;gt;= 6
 msg = &amp;#34;user %s has a weak password&amp;#34; %(user[&amp;#39;name&amp;#39;])
&amp;gt; assert passwd != &amp;#39;password&amp;#39;, msg
E AssertionError: user mike has a weak password
E assert &amp;#39;password&amp;#39; != &amp;#39;password&amp;#39;

test_user_password_with_params.py:14: AssertionError
================================================================= 2 failed, 3 passed in 0.05 seconds ==================================================================
&lt;/code>&lt;/pre>&lt;p>稍微留意一下, 可以看出tom和mike使用了弱密码。总共运行了5个用例,3个成功,2个失败。&lt;/p></description></item><item><title>6.Parametrize Fixture</title><link>https://itest2025.pages.dev/tutorials/25-006-6parametrize-fixture/</link><pubDate>Sun, 29 Oct 2017 07:22:42 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/25-006-6parametrize-fixture/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>&lt;code>@pytest.mark.parametrize&lt;/code> 装饰器可以让我们每次参数化fixture的时候传入多个项目。回忆上一节，我们参数化的时候只能传入传入1个字符串或者是其他的数据对象，parametrize每次多个参数，更加灵活。&lt;/p>
&lt;h3 id="例子">例子&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">pytest&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@pytest.mark.parametrize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;test_input,expected&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;3+5&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;2+4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;6*9&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">42&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">test_eval&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">test_input&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">expected&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="nb">eval&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">test_input&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">expected&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>test_eval方法中传入了2个参数，就如同&lt;code>@pytest.mark.parametrize&lt;/code>装饰器中定义的那样，因此简单理解，我们可以把parametrize装饰器想象成是数据表格，有表头(test_input,expected)以及具体的数据。&lt;/p>
&lt;h3 id="运行结果">运行结果&lt;/h3>
&lt;pre tabindex="0">&lt;code>$ pytest
======= test session starts ========
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile:
collected 3 items

test_expectation.py ..F

======= FAILURES ========
_______ test_eval[6*9-42] ________

test_input = &amp;#39;6*9&amp;#39;, expected = 42

 @pytest.mark.parametrize(&amp;#34;test_input,expected&amp;#34;, [
 (&amp;#34;3+5&amp;#34;, 8),
 (&amp;#34;2+4&amp;#34;, 6),
 (&amp;#34;6*9&amp;#34;, 42),
 ])
 def test_eval(test_input, expected):
&amp;gt; assert eval(test_input) == expected
E AssertionError: assert 54 == 42
E + where 54 = eval(&amp;#39;6*9&amp;#39;)

test_expectation.py:8: AssertionError
======= 1 failed, 2 passed in 0.12 seconds ========
&lt;/code>&lt;/pre>&lt;p>原始封面&lt;/p></description></item><item><title>7.常见套路</title><link>https://itest2025.pages.dev/tutorials/25-007-7%E5%B8%B8%E8%A7%81%E5%A5%97%E8%B7%AF/</link><pubDate>Sat, 28 Oct 2017 07:22:42 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/25-007-7%E5%B8%B8%E8%A7%81%E5%A5%97%E8%B7%AF/</guid><description>&lt;p>在使用pytest的时候，下面这些问题我们可能会经常遇到，这里给出官方的解决方案，按照套路来执行就可以了。&lt;/p>
&lt;h3 id="基础用法">基础用法&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/simple.html#pass-different-values-to-a-test-function-depending-on-command-line-options" target="_blank" rel="noopener noreffer ">把命令行参数传入到用例&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/simple.html#dynamically-adding-command-line-options" target="_blank" rel="noopener noreffer ">动态添加命令行参数&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/simple.html#control-skipping-of-tests-according-to-command-line-option" target="_blank" rel="noopener noreffer ">根据命令行参数来忽略用例执行&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/simple.html#writing-well-integrated-assertion-helpers" target="_blank" rel="noopener noreffer ">编写集成度更好的辅助断言&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/simple.html#detect-if-running-from-within-a-pytest-run" target="_blank" rel="noopener noreffer ">判断是否由pytest执行&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/simple.html#adding-info-to-test-report-header" target="_blank" rel="noopener noreffer ">在测试报告的头部添加内容&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/simple.html#profiling-test-duration" target="_blank" rel="noopener noreffer ">统计用例运行时间&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/simple.html#profiling-test-duration" target="_blank" rel="noopener noreffer ">定义测试步骤，也就是让用例按照一定的顺序执行&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/simple.html#package-directory-level-fixtures-setups" target="_blank" rel="noopener noreffer ">Package/Directory-level fixtures (setups)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/simple.html#post-process-test-reports-failures" target="_blank" rel="noopener noreffer ">在报告和用例失败之前添加钩子&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/simple.html#making-test-result-information-available-in-fixtures" target="_blank" rel="noopener noreffer ">在fixtures中访问测试结果&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/simple.html#pytest-current-test-environment-variable" target="_blank" rel="noopener noreffer ">PYTEST_CURRENT_TEST环境变量&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/simple.html#freezing-pytest" target="_blank" rel="noopener noreffer ">冻结pytest&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="参数化">参数化&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/parametrize.html#generating-parameters-combinations-depending-on-command-line" target="_blank" rel="noopener noreffer ">根据命令行参数来组合测试参数&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/parametrize.html#generating-parameters-combinations-depending-on-command-line" target="_blank" rel="noopener noreffer ">配置test ID&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/parametrize.html#a-quick-port-of-testscenarios" target="_blank" rel="noopener noreffer ">快速创建测试场景的功能&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/parametrize.html#deferring-the-setup-of-parametrized-resources" target="_blank" rel="noopener noreffer ">延迟参数资源加载&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/parametrize.html#apply-indirect-on-particular-arguments" target="_blank" rel="noopener noreffer ">间接参数&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/parametrize.html#parametrizing-test-methods-through-per-class-configuration" target="_blank" rel="noopener noreffer ">为不同的方法设置不同的参数&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/parametrize.html#indirect-parametrization-with-multiple-fixtures" target="_blank" rel="noopener noreffer ">在多个fixture中使用间接参数&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/parametrize.html#indirect-parametrization-of-optional-implementations-imports" target="_blank" rel="noopener noreffer ">Indirect parametrization of optional implementations/imports&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.pytest.org/en/latest/example/parametrize.html#set-marks-or-test-id-for-individual-parametrized-test" target="_blank" rel="noopener noreffer ">单独的为每个参数化用例设置标记和ID&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>原始封面&lt;/p></description></item><item><title>8.使用pytest做简单的接口测试</title><link>https://itest2025.pages.dev/tutorials/25-008-8%E4%BD%BF%E7%94%A8pytest%E5%81%9A%E7%AE%80%E5%8D%95%E7%9A%84%E6%8E%A5%E5%8F%A3%E6%B5%8B%E8%AF%95/</link><pubDate>Fri, 27 Oct 2017 07:22:42 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/25-008-8%E4%BD%BF%E7%94%A8pytest%E5%81%9A%E7%AE%80%E5%8D%95%E7%9A%84%E6%8E%A5%E5%8F%A3%E6%B5%8B%E8%AF%95/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>我们现在需要测试&lt;a href="http://www.v2ex.com" target="_blank" rel="noopener noreffer ">v2ex&lt;/a>网站的查看论坛节点信息的api。具体如下:&lt;/p>
&lt;pre tabindex="0">&lt;code>节点信息

获得指定节点的名字，简介，URL 及头像图片的地址。

https://www.v2ex.com/api/nodes/show.json

Method: GET
Authentication: None
接受参数：

name: 节点名（V2EX 的节点名全是半角英文或者数字）
例如：

https://www.v2ex.com/api/nodes/show.json?name=python

接口返回

{
 &amp;#34;id&amp;#34; : 90,
 &amp;#34;name&amp;#34; : &amp;#34;python&amp;#34;,
 &amp;#34;url&amp;#34; : &amp;#34;http://www.v2ex.com/go/python&amp;#34;,
 &amp;#34;title&amp;#34; : &amp;#34;Python&amp;#34;,
 &amp;#34;title_alternative&amp;#34; : &amp;#34;Python&amp;#34;,
 &amp;#34;topics&amp;#34; : 7963,
 &amp;#34;stars&amp;#34; : 5138,

 &amp;#34;header&amp;#34; : &amp;#34;这里讨论各种 Python 语言编程话题，也包括 Django，Tornado 等框架的讨论。这里是一个能够帮助你解决实际问题的地方。&amp;#34;,


 &amp;#34;footer&amp;#34; : null,

 &amp;#34;created&amp;#34; : 1278683336,
 &amp;#34;avatar_mini&amp;#34; : &amp;#34;//v2ex.assets.uxengine.net/navatar/8613/985e/90_mini.png?m=1509941286&amp;#34;,
 &amp;#34;avatar_normal&amp;#34; : &amp;#34;//v2ex.assets.uxengine.net/navatar/8613/985e/90_normal.png?m=1509941286&amp;#34;,
 &amp;#34;avatar_large&amp;#34; : &amp;#34;//v2ex.assets.uxengine.net/navatar/8613/985e/90_large.png?m=1509941286&amp;#34;
}
&lt;/code>&lt;/pre>&lt;h3 id="需求分析">需求分析&lt;/h3>
&lt;p>该接口是获取数据的接口，一般来说，这种接口我们的测试重点是验证数据的准确性。&lt;/p>
&lt;p>根据&lt;a href="https://itest2025.pages.dev/interface/3a/" rel="">3A原则&lt;/a>，我们可以设计如下的&lt;a href="https://itest2025.pages.dev/interface/first_case/" rel="">用例&lt;/a>&lt;/p>
&lt;ul>
&lt;li>测试数据: 节点的名称:python&lt;/li>
&lt;li>接口地址: &lt;a href="https://www.v2ex.com/api/nodes/show.json" target="_blank" rel="noopener noreffer ">https://www.v2ex.com/api/nodes/show.json&lt;/a>&lt;/li>
&lt;li>断言: 返回的结果里，id必须是90,name必须等于python，大家想一想为什么？&lt;/li>
&lt;/ul>
&lt;h3 id="代码">代码&lt;/h3>
&lt;p>新建名为&lt;code>v2ex_api_test.py&lt;/code>的文件，键入如下内容&lt;/p></description></item><item><title>9.使用fixture参数化接口入参</title><link>https://itest2025.pages.dev/tutorials/25-009-9%E4%BD%BF%E7%94%A8fixture%E5%8F%82%E6%95%B0%E5%8C%96%E6%8E%A5%E5%8F%A3%E5%85%A5%E5%8F%82/</link><pubDate>Thu, 26 Oct 2017 07:22:42 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/25-009-9%E4%BD%BF%E7%94%A8fixture%E5%8F%82%E6%95%B0%E5%8C%96%E6%8E%A5%E5%8F%A3%E5%85%A5%E5%8F%82/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>接上一节&lt;a href="http://www.v2ex.com" target="_blank" rel="noopener noreffer ">v2ex&lt;/a>网站的查看论坛节点信息的api。具体如下:&lt;/p>
&lt;pre tabindex="0">&lt;code>节点信息

获得指定节点的名字，简介，URL 及头像图片的地址。

https://www.v2ex.com/api/nodes/show.json

Method: GET
Authentication: None
接受参数：

name: 节点名（V2EX 的节点名全是半角英文或者数字）
例如：

https://www.v2ex.com/api/nodes/show.json?name=python
&lt;/code>&lt;/pre>&lt;p>我们试一下，通过传入不同的name，我们可以获取不同的节点信息。上面例子里我们获取了python讨论区的信息。现在我们把name改成java，该接口会返回java讨论区节点的信息，如下所示&lt;/p>
&lt;pre tabindex="0">&lt;code>https://www.v2ex.com/api/nodes/show.json?name=python

{
 &amp;#34;id&amp;#34; : 63,
 &amp;#34;name&amp;#34; : &amp;#34;java&amp;#34;,
 &amp;#34;url&amp;#34; : &amp;#34;http://www.v2ex.com/go/java&amp;#34;,
 &amp;#34;title&amp;#34; : &amp;#34;Java&amp;#34;,
 &amp;#34;title_alternative&amp;#34; : &amp;#34;Java&amp;#34;,
 &amp;#34;topics&amp;#34; : 1219,
 &amp;#34;stars&amp;#34; : 1547,

 &amp;#34;header&amp;#34; : &amp;#34;The most popular programming language.&amp;#34;,


 &amp;#34;footer&amp;#34; : null,

 &amp;#34;created&amp;#34; : 1272669207,
 &amp;#34;avatar_mini&amp;#34; : &amp;#34;//v2ex.assets.uxengine.net/navatar/03af/dbd6/63_mini.png?m=1509589840&amp;#34;,
 &amp;#34;avatar_normal&amp;#34; : &amp;#34;//v2ex.assets.uxengine.net/navatar/03af/dbd6/63_normal.png?m=1509589840&amp;#34;,
 &amp;#34;avatar_large&amp;#34; : &amp;#34;//v2ex.assets.uxengine.net/navatar/03af/dbd6/63_large.png?m=1509589840&amp;#34;
}
&lt;/code>&lt;/pre>&lt;p>那么新的需求来了，现在我们要测试给定的几个节点名称(python/java/go/nodejs)，v2ex的节点api可以正确返回节点的名字&lt;/p>
&lt;h3 id="需求分析">需求分析&lt;/h3>
&lt;p>根据&lt;a href="https://itest2025.pages.dev/interface/3a/" rel="">3A原则&lt;/a>，我们可以设计如下的用例&lt;/p>
&lt;ul>
&lt;li>测试数据: 节点的名称:python/java/go/nodejs&lt;/li>
&lt;li>接口地址: &lt;a href="https://www.v2ex.com/api/nodes/show.json" target="_blank" rel="noopener noreffer ">https://www.v2ex.com/api/nodes/show.json&lt;/a>&lt;/li>
&lt;li>断言: 返回的结果里，name字段的值必须等于传入的节点名称&lt;/li>
&lt;/ul>
&lt;h3 id="代码">代码&lt;/h3>
&lt;p>在&lt;code>v2ex_api_test.py&lt;/code>的文件中添加如下内容&lt;/p></description></item><item><title>10.使用fixture参数化测试预期结果</title><link>https://itest2025.pages.dev/tutorials/25-010-10%E4%BD%BF%E7%94%A8fixture%E5%8F%82%E6%95%B0%E5%8C%96%E6%B5%8B%E8%AF%95%E9%A2%84%E6%9C%9F%E7%BB%93%E6%9E%9C/</link><pubDate>Wed, 25 Oct 2017 07:22:42 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/25-010-10%E4%BD%BF%E7%94%A8fixture%E5%8F%82%E6%95%B0%E5%8C%96%E6%B5%8B%E8%AF%95%E9%A2%84%E6%9C%9F%E7%BB%93%E6%9E%9C/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>接上一节&lt;a href="http://www.v2ex.com" target="_blank" rel="noopener noreffer ">v2ex&lt;/a>网站的查看论坛节点信息的api。&lt;/p>
&lt;p>我们在上一节的测试用例里只断言了返回值的name字段必须与我们传入的入参相同，但是返回值的id却没有进行判定。这一节里我们加强一下测试脚本，实现判断id的功能。&lt;/p>
&lt;h3 id="测试数据">测试数据&lt;/h3>
&lt;pre tabindex="0">&lt;code>python, id=90
java, id=63
nodejs, id=436
go, id=375
&lt;/code>&lt;/pre>&lt;h3 id="代码实现">代码实现&lt;/h3>
&lt;p>在&lt;code>v2ex_api_test.py&lt;/code>的文件中加入如下内容:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">TestV2exApiWithExpectation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">object&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">domain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;https://www.v2ex.com/&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@pytest.mark.parametrize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;name,node_id&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[(&lt;/span>&lt;span class="s1">&amp;#39;python&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">90&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;java&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">63&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;go&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">375&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;nodejs&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">436&lt;/span>&lt;span class="p">)])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">test_node&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">node_id&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;api/nodes/show.json?name=&lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s1">&amp;#39;&lt;/span> &lt;span class="o">%&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">domain&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">path&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">requests&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">url&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">name&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="n">res&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;id&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">node_id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">assert&lt;/span> &lt;span class="mi">0&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="运行及结果">运行及结果&lt;/h3>
&lt;pre tabindex="0">&lt;code>$ pytest v2ex_api_test.py
======================================================================== test session starts ========================================================================
platform darwin -- Python 2.7.12, pytest-3.2.3, py-1.4.34, pluggy-0.4.0
rootdir: /Users/easonhan/code/testclass.net/src/pytest, inifile:
collected 9 items

v2ex_api_test.py .FFFFFFFF

============================================================================= FAILURES ==============================================================================
______________________________________________________________ TestV2exApiWithParams.test_node[python] ______________________________________________________________

self = &amp;lt;v2ex_api_test.TestV2exApiWithParams object at 0x10618eb10&amp;gt;, lang = &amp;#39;python&amp;#39;

 def test_node(self, lang):
 path = &amp;#39;api/nodes/show.json?name=%s&amp;#39; %(lang)
 url = self.domain + path
 res = requests.get(url).json()
 assert res[&amp;#39;name&amp;#39;] == lang
&amp;gt; assert 0
E assert 0

v2ex_api_test.py:27: AssertionError
_______________________________________________________________ TestV2exApiWithParams.test_node[java] _______________________________________________________________

self = &amp;lt;v2ex_api_test.TestV2exApiWithParams object at 0x106691790&amp;gt;, lang = &amp;#39;java&amp;#39;

 def test_node(self, lang):
 path = &amp;#39;api/nodes/show.json?name=%s&amp;#39; %(lang)
 url = self.domain + path
 res = requests.get(url).json()
 assert res[&amp;#39;name&amp;#39;] == lang
&amp;gt; assert 0
E assert 0

v2ex_api_test.py:27: AssertionError
________________________________________________________________ TestV2exApiWithParams.test_node[go] ________________________________________________________________

self = &amp;lt;v2ex_api_test.TestV2exApiWithParams object at 0x10666dc50&amp;gt;, lang = &amp;#39;go&amp;#39;

 def test_node(self, lang):
 path = &amp;#39;api/nodes/show.json?name=%s&amp;#39; %(lang)
 url = self.domain + path
 res = requests.get(url).json()
 assert res[&amp;#39;name&amp;#39;] == lang
&amp;gt; assert 0
E assert 0

v2ex_api_test.py:27: AssertionError
______________________________________________________________ TestV2exApiWithParams.test_node[nodejs] ______________________________________________________________

self = &amp;lt;v2ex_api_test.TestV2exApiWithParams object at 0x106691890&amp;gt;, lang = &amp;#39;nodejs&amp;#39;

 def test_node(self, lang):
 path = &amp;#39;api/nodes/show.json?name=%s&amp;#39; %(lang)
 url = self.domain + path
 res = requests.get(url).json()
 assert res[&amp;#39;name&amp;#39;] == lang
&amp;gt; assert 0
E assert 0

v2ex_api_test.py:27: AssertionError
__________________________________________________________ TestV2exApiWithExpectation.test_node[python-90] __________________________________________________________

self = &amp;lt;v2ex_api_test.TestV2exApiWithExpectation object at 0x1066d20d0&amp;gt;, name = &amp;#39;python&amp;#39;, node_id = 90

 @pytest.mark.parametrize(&amp;#39;name,node_id&amp;#39;, [(&amp;#39;python&amp;#39;, 90), (&amp;#39;java&amp;#39;, 63), (&amp;#39;go&amp;#39;, 375), (&amp;#39;nodejs&amp;#39;, 436)])

 def test_node(self, name, node_id):
 path = &amp;#39;api/nodes/show.json?name=%s&amp;#39; %(name)
 url = self.domain + path
 res = requests.get(url).json()
 assert res[&amp;#39;name&amp;#39;] == name
 assert res[&amp;#39;id&amp;#39;] == node_id
&amp;gt; assert 0
E assert 0

v2ex_api_test.py:40: AssertionError
___________________________________________________________ TestV2exApiWithExpectation.test_node[java-63] ___________________________________________________________

self = &amp;lt;v2ex_api_test.TestV2exApiWithExpectation object at 0x1066e9690&amp;gt;, name = &amp;#39;java&amp;#39;, node_id = 63

 @pytest.mark.parametrize(&amp;#39;name,node_id&amp;#39;, [(&amp;#39;python&amp;#39;, 90), (&amp;#39;java&amp;#39;, 63), (&amp;#39;go&amp;#39;, 375), (&amp;#39;nodejs&amp;#39;, 436)])

 def test_node(self, name, node_id):
 path = &amp;#39;api/nodes/show.json?name=%s&amp;#39; %(name)
 url = self.domain + path
 res = requests.get(url).json()
 assert res[&amp;#39;name&amp;#39;] == name
 assert res[&amp;#39;id&amp;#39;] == node_id
&amp;gt; assert 0
E assert 0

v2ex_api_test.py:40: AssertionError
___________________________________________________________ TestV2exApiWithExpectation.test_node[go-375] ____________________________________________________________

self = &amp;lt;v2ex_api_test.TestV2exApiWithExpectation object at 0x10666d790&amp;gt;, name = &amp;#39;go&amp;#39;, node_id = 375

 @pytest.mark.parametrize(&amp;#39;name,node_id&amp;#39;, [(&amp;#39;python&amp;#39;, 90), (&amp;#39;java&amp;#39;, 63), (&amp;#39;go&amp;#39;, 375), (&amp;#39;nodejs&amp;#39;, 436)])

 def test_node(self, name, node_id):
 path = &amp;#39;api/nodes/show.json?name=%s&amp;#39; %(name)
 url = self.domain + path
 res = requests.get(url).json()
 assert res[&amp;#39;name&amp;#39;] == name
 assert res[&amp;#39;id&amp;#39;] == node_id
&amp;gt; assert 0
E assert 0

v2ex_api_test.py:40: AssertionError
_________________________________________________________ TestV2exApiWithExpectation.test_node[nodejs-436] __________________________________________________________

self = &amp;lt;v2ex_api_test.TestV2exApiWithExpectation object at 0x1066d2710&amp;gt;, name = &amp;#39;nodejs&amp;#39;, node_id = 436

 @pytest.mark.parametrize(&amp;#39;name,node_id&amp;#39;, [(&amp;#39;python&amp;#39;, 90), (&amp;#39;java&amp;#39;, 63), (&amp;#39;go&amp;#39;, 375), (&amp;#39;nodejs&amp;#39;, 436)])

 def test_node(self, name, node_id):
 path = &amp;#39;api/nodes/show.json?name=%s&amp;#39; %(name)
 url = self.domain + path
 res = requests.get(url).json()
 assert res[&amp;#39;name&amp;#39;] == name
 assert res[&amp;#39;id&amp;#39;] == node_id
&amp;gt; assert 0
E assert 0

v2ex_api_test.py:40: AssertionError
================================================================ 8 failed, 1 passed in 1.81 seconds =================================================================
&lt;/code>&lt;/pre>&lt;p>原始封面&lt;/p></description></item><item><title>11.生成xml格式的测试报告</title><link>https://itest2025.pages.dev/tutorials/25-011-11%E7%94%9F%E6%88%90xml%E6%A0%BC%E5%BC%8F%E7%9A%84%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A/</link><pubDate>Wed, 25 Oct 2017 07:22:42 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/25-011-11%E7%94%9F%E6%88%90xml%E6%A0%BC%E5%BC%8F%E7%9A%84%E6%B5%8B%E8%AF%95%E6%8A%A5%E5%91%8A/</guid><description>&lt;p>pytest有非常友好的命令行报告输出，在做用例开发的时候，这是极好的。然而我们在运行用例后经常会需要将测试结果广而告之，发送给相关责任人浏览，所以生成简洁美观的测试报告是很有必要的。&lt;/p>
&lt;h3 id="生成junit格式的xml报告">生成junit格式的xml报告&lt;/h3>
&lt;p>pytest可以生成junit格式的xml报告，在命令行中加入&lt;code>--junit-xml=path&lt;/code> 参数就可以了。&lt;/p>
&lt;pre tabindex="0">&lt;code>pytest test_quick_start.py --junit-xml=report.xml
&lt;/code>&lt;/pre>&lt;p>上面的命令在当前文件夹中生成了名为report.xml的测试报告&lt;/p>
&lt;pre tabindex="0">&lt;code>&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;&amp;lt;testsuite errors=&amp;#34;0&amp;#34; failures=&amp;#34;0&amp;#34; name=&amp;#34;pytest&amp;#34; skips=&amp;#34;0&amp;#34; tests=&amp;#34;1&amp;#34; time=&amp;#34;0.009&amp;#34;&amp;gt;&amp;lt;testcase classname=&amp;#34;test_quick_start&amp;#34; file=&amp;#34;test_quick_start.py&amp;#34; line=&amp;#34;3&amp;#34; name=&amp;#34;test_reverse&amp;#34; time=&amp;#34;0.000499725341797&amp;#34;&amp;gt;&amp;lt;/testcase&amp;gt;&amp;lt;/testsuite&amp;gt;%
&lt;/code>&lt;/pre>&lt;h3 id="ci集成">ci集成&lt;/h3>
&lt;p>jenkins在build后可以通过插件展示junit格式的测试报告，具体步骤就不描述了。&lt;/p>
&lt;p>原始封面&lt;/p>
&lt;p>&lt;img
 class="lazyload"
 src="https://itest2025.pages.dev/svg/loading.min.svg"
 data-src="https://images.unsplash.com/photo-1426604966848-d7adac402bff?w=300"
 data-srcset="https://images.unsplash.com/photo-1426604966848-d7adac402bff?w=300, https://images.unsplash.com/photo-1426604966848-d7adac402bff?w=300 1.5x, https://images.unsplash.com/photo-1426604966848-d7adac402bff?w=300 2x"
 data-sizes="auto"
 alt="https://images.unsplash.com/photo-1426604966848-d7adac402bff?w=300"
 title="课程图片" />&lt;/p></description></item></channel></rss>