<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Python Unittest测试框架教程 on 重定向学院</title><link>https://itest2025.pages.dev/categories/python-unittest%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6%E6%95%99%E7%A8%8B/</link><description>Recent content in Python Unittest测试框架教程 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/python-unittest%E6%B5%8B%E8%AF%95%E6%A1%86%E6%9E%B6%E6%95%99%E7%A8%8B/index.xml" rel="self" type="application/rss+xml"/><item><title>1. 基本概念</title><link>https://itest2025.pages.dev/tutorials/35-001-1-%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5/</link><pubDate>Thu, 09 Nov 2017 06:23:31 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/35-001-1-%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5/</guid><description>&lt;p>&lt;a href="https://docs.python.org/2/library/unittest.html#module-unittest" target="_blank" rel="noopener noreffer ">unittest&lt;/a>是python自带的单元测试框架，有时候又被称为&amp;quot;PyUnit&amp;quot;，是python版本的&lt;a href="http://junit.org/" target="_blank" rel="noopener noreffer ">JUint&lt;/a>实现。&lt;/p>
&lt;p>该框架的作者是 Kent Beck和Erich Gamma，感谢祖师爷赏饭吃。&lt;/p>
&lt;p>在学习使用unittest库之前，我们需要了解一下unittest库的一些重要概念:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>test fixture&lt;/strong>: 代表了用例执行前的准备工作和用例执行之后的清理工作。比如在用例执行前创建临时文件和文件夹，又或者启动1个server进程等；&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>test case&lt;/strong>: 测试用例，这个相信大家都不陌生。是测试的最小单位，一般检查一组输入的响应(输出)是否符合预期。&lt;a href="https://docs.python.org/2/library/unittest.html#module-unittest" target="_blank" rel="noopener noreffer ">unittest&lt;/a>模块提供了&lt;a href="https://docs.python.org/2/library/unittest.html#unittest.TestCase" target="_blank" rel="noopener noreffer ">TestCase&lt;/a>类来帮助我们创建测试用例；&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>test suite&lt;/strong>: 经常被翻译成&amp;quot;测试套件&amp;quot;，也有人称为&amp;quot;测试套&amp;quot;，是测试用例或测试套件的集合，一般用来把需要一起执行的用例组合到一起;&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>test runner&lt;/strong>: 用来执行测试用例并输出测试结果的组件。可以是图形界面或命令行界面;&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>总之&lt;/p>
&lt;ul>
&lt;li>test fixture的功能可以理解成是初始化和清理测试数据及环境&lt;/li>
&lt;li>test case是测试用例&lt;/li>
&lt;li>test suite是用例集合&lt;/li>
&lt;li>test runner的作用是运行用例并返回结果&lt;/li>
&lt;/ul>
&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-1524749292158-7540c2494485?w=300"
 data-srcset="https://images.unsplash.com/photo-1524749292158-7540c2494485?w=300, https://images.unsplash.com/photo-1524749292158-7540c2494485?w=300 1.5x, https://images.unsplash.com/photo-1524749292158-7540c2494485?w=300 2x"
 data-sizes="auto"
 alt="https://images.unsplash.com/photo-1524749292158-7540c2494485?w=300"
 title="课程图片" />&lt;/p></description></item><item><title>2. 基本用法</title><link>https://itest2025.pages.dev/tutorials/35-002-2-%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95/</link><pubDate>Wed, 08 Nov 2017 06:23:31 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/35-002-2-%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95/</guid><description>&lt;p>我们通过最简单的例子来看一下unittest的基本用法，下面的代码测试了3个python字符串方法，基本上满足了大部分情况下的测试需求&lt;/p>
&lt;pre tabindex="0">&lt;code>import unittest

class TestStringMethods(unittest.TestCase):

 def test_upper(self):
 self.assertEqual(&amp;#39;foo&amp;#39;.upper(), &amp;#39;FOO&amp;#39;)

 def test_isupper(self):
 self.assertTrue(&amp;#39;FOO&amp;#39;.isupper())
 self.assertFalse(&amp;#39;Foo&amp;#39;.isupper())

 def test_split(self):
 s = &amp;#39;hello world&amp;#39;
 self.assertEqual(s.split(), [&amp;#39;hello&amp;#39;, &amp;#39;world&amp;#39;])
 # check that s.split fails when the separator is not a string
 with self.assertRaises(TypeError):
 s.split(2)

if __name__ == &amp;#39;__main__&amp;#39;:
 unittest.main()
&lt;/code>&lt;/pre>&lt;p>解释一下关键点&lt;/p>
&lt;ul>
&lt;li>
&lt;p>可以通过继承&lt;a href="https://docs.python.org/2/library/unittest.html#unittest.TestCase" target="_blank" rel="noopener noreffer ">unittest.TestCase&lt;/a>类来定义我们自己的测试用例，1个测试用例类下面可以有多个测试方法(test)或者叫做测试点&lt;/p>
&lt;/li>
&lt;li>
&lt;p>记住这个套路：测试用例中方法名以&lt;code>test&lt;/code>开头的方法才是测试方法，比如上面的例子里定义了3个以test开头的方法，分别是&lt;code>test_upper&lt;/code>，&lt;code>test_isupper&lt;/code>和&lt;code>test_split&lt;/code>。非测试方法是不会被test runner执行的&lt;/p>
&lt;/li>
&lt;li>
&lt;p>断言是测试用例的核心。我们使用&lt;a href="https://docs.python.org/2/library/unittest.html#unittest.TestCase.assertEqual" target="_blank" rel="noopener noreffer ">assertEqual()&lt;/a>来判断预期结果，用&lt;a href="https://docs.python.org/2/library/unittest.html#unittest.TestCase.assertTrue" target="_blank" rel="noopener noreffer ">assertTrue()&lt;/a>和&lt;a href="https://docs.python.org/2/library/unittest.html#unittest.TestCase.assertFalse" target="_blank" rel="noopener noreffer ">assertFalse&lt;/a>来做是非判断，以及用&lt;a href="https://docs.python.org/2/library/unittest.html#unittest.TestCase.assertRaises" target="_blank" rel="noopener noreffer ">assertRaises()&lt;/a>来判断预期的异常是否有被抛出。这些unittest提供的以assert开头的方法就是断言，一般情况下,每个测试方法里都必须有断言&lt;/p>
&lt;/li>
&lt;li>
&lt;p>最后, &lt;a href="https://docs.python.org/2/library/unittest.html#unittest.main" target="_blank" rel="noopener noreffer ">unittest.main&lt;/a>提供了最简单的运行用例的方式。当我们从命令行运行上面的代码时，我们可以看到如下的输出&lt;/p>
&lt;/li>
&lt;/ul>
&lt;pre tabindex="0">&lt;code>...
----------------------------------------------------------------------
Ran 3 tests in 0.000s

OK
&lt;/code>&lt;/pre>&lt;p>除了使用&lt;a href="https://docs.python.org/2/library/unittest.html#unittest.main" target="_blank" rel="noopener noreffer ">unittest.main&lt;/a>，还有其他的方式可以运行测试用例，比如把最后2行替换为&lt;/p>
&lt;pre tabindex="0">&lt;code>suite = unittest.TestLoader().loadTestsFromTestCase(TestStringMethods)
unittest.TextTestRunner(verbosity=2).run(suite)
&lt;/code>&lt;/pre>&lt;p>运行用例，结果将会如下所示&lt;/p></description></item><item><title>3. 实例: 测试弱密码</title><link>https://itest2025.pages.dev/tutorials/35-003-3-%E5%AE%9E%E4%BE%8B-%E6%B5%8B%E8%AF%95%E5%BC%B1%E5%AF%86%E7%A0%81/</link><pubDate>Wed, 08 Nov 2017 06:23:31 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/35-003-3-%E5%AE%9E%E4%BE%8B-%E6%B5%8B%E8%AF%95%E5%BC%B1%E5%AF%86%E7%A0%81/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>考虑这样一个测试弱密码的实例，这个我们在&lt;a href="https://itest2025.pages.dev/pytest/fixture/" rel="">pytest相关教程&lt;/a>中也有过描述。&lt;/p>
&lt;p>我们需要判断用户的密码中包含简单密码，规则是这样的，密码必须至少6位，满足6位的话判断用户的密码不是password123或者password之类的弱密码。&lt;/p>
&lt;p>对于如下的测试数据，我们要如何使用unittest来进行相关测试呢？&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;h3 id="test-fixture">Test fixture&lt;/h3>
&lt;p>前文我们也说过，text fixture的主要功能是初始化测试数据或环境以及清理测试数据或环境。&lt;/p>
&lt;p>考虑上面的例子，对我们而已，在用例执行之前初始化上面的测试数据是有必要的，我们可以把上面的数据用python的数据结构来表示，比较合适的数据结构是python的字典。这样做有如下的好处&lt;/p>
&lt;ul>
&lt;li>统一初始化一些需要在多个用例之间共享的数据&lt;/li>
&lt;li>可以在初始化的时候做一些数据的处理工作，比如过滤一些无效数据等&lt;/li>
&lt;/ul>
&lt;p>Test fixture最简单的实现方式是通过自定义下面的2个方法:&lt;/p>
&lt;ul>
&lt;li>&lt;code>TestCase.setUp&lt;/code>方法在每个测试方法运行之前都会运行一次，适合为每个用例都初始化一遍数据&lt;/li>
&lt;li>&lt;code>TestCase.tearDown&lt;/code>方法在每个测试方法运行之后都会运行一次，适合为每个用例都清理一遍数据&lt;/li>
&lt;/ul>
&lt;h3 id="代码">代码&lt;/h3>
&lt;p>新建名为&lt;code>test_password_1.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="kn">import&lt;/span> &lt;span class="nn">unittest&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">PasswordTeseCase&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TestCase&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">setUp&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="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;set up&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="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">test_data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">dict&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;jack&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">password&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;Iloverose&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="nb">dict&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;rose&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">password&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;Ilovejack&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="nb">dict&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s1">&amp;#39;tom&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">password&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>&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">test_week_password&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">for&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">test_data&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">data&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertTrue&lt;/span>&lt;span class="p">(&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 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="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">data&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="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertTrue&lt;/span>&lt;span class="p">(&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 class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertTrue&lt;/span>&lt;span class="p">(&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 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_dummy&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">pass&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">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&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="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="运行">运行&lt;/h3>
&lt;p>在命令行里输入 &lt;code>python test_password_1.py&lt;/code>来运行用例，结果如下&lt;/p>
&lt;pre tabindex="0">&lt;code>$ python test_password_1.py
set up
.set up
F
======================================================================
FAIL: test_week_password (__main__.PasswordTeseCase)
----------------------------------------------------------------------
Traceback (most recent call last):
 File &amp;#34;test_password_1.py&amp;#34;, line 21, in test_week_password
 self.assertTrue(passwd != &amp;#39;password123&amp;#39;, msg)
AssertionError: False is not true : user tom has a weak password

----------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1)
&lt;/code>&lt;/pre>&lt;h3 id="解释一下">解释一下&lt;/h3>
&lt;ul>
&lt;li>
&lt;p>setUp方法运行了2次，所以打印出了2次&amp;rsquo;set up&amp;rsquo;，这是因为上面的用例中有2个测试方法(2个方法名以test开头的方法)，setUp会在每个测试方法执行之前执行1次&lt;/p></description></item><item><title>4. 实例: 读取测试数据并测试弱密码</title><link>https://itest2025.pages.dev/tutorials/35-004-4-%E5%AE%9E%E4%BE%8B-%E8%AF%BB%E5%8F%96%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE%E5%B9%B6%E6%B5%8B%E8%AF%95%E5%BC%B1%E5%AF%86%E7%A0%81/</link><pubDate>Tue, 07 Nov 2017 06:23:31 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/35-004-4-%E5%AE%9E%E4%BE%8B-%E8%AF%BB%E5%8F%96%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE%E5%B9%B6%E6%B5%8B%E8%AF%95%E5%BC%B1%E5%AF%86%E7%A0%81/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>接上一节的弱密码例子，我们的用例尽管运行的不错，但还是有点问题。&lt;/p>
&lt;p>假如我们需要增加一些测试数据，那么我们就必须去修改&lt;code>setUp&lt;/code>方法，在&lt;code>test_data&lt;/code>列表中增加数据，频繁修改代码以适应一些不变的测试场景，这是没有必要的开销，可以想办法去优化。&lt;/p>
&lt;p>我们可以把测试数据保存在文件里，通过读取文件的方式，每次动态从测试用例读取数据，这样数据的改变并不会影响用例，用例逻辑相对稳定，维护成本得到一定的降低。&lt;/p>
&lt;h3 id="设计测试数据">设计测试数据&lt;/h3>
&lt;p>我们可以把测试数据保存成json格式，json格式的数据在各个语言间有较好的通用性，比较适合复用。&lt;/p>
&lt;p>新建user_data.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>使用python的json库解析上面的json文件，可以得到如上节中&lt;code>test_data&lt;/code>一致的数据。&lt;/p>
&lt;h3 id="代码">代码&lt;/h3>
&lt;p>新建&lt;code>test_password_2.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="kn">import&lt;/span> &lt;span class="nn">unittest&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">PasswordWithJsonTeseCase&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TestCase&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data_file_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;./user_data.json&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="k">def&lt;/span> &lt;span class="nf">setUp&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="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;set up&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="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">test_data&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="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">data_file_path&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">def&lt;/span> &lt;span class="nf">test_week_password&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">for&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">test_data&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">data&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertTrue&lt;/span>&lt;span class="p">(&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 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="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">data&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="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertTrue&lt;/span>&lt;span class="p">(&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 class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertTrue&lt;/span>&lt;span class="p">(&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 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_dummy&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">pass&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">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&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="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>跟上一节相比，最大的不同点是现在test_data通过解析json文件的方式来赋值&lt;code>self.test_data = json.loads(open(self.data_file_path).read())&lt;/code>。&lt;/p>
&lt;p>执行测试文件，结果应该与上一节一致。&lt;/p>
&lt;h3 id="发现问题">发现问题&lt;/h3>
&lt;p>上面的代码有2个测试方法:&lt;code>test_week_password&lt;/code>和&lt;code>test_dummy&lt;/code>。由于&lt;code>setUp&lt;/code>会在每个测试方法执行之前执行一次，那么setUp方法会执行2次，相应的json文件也会读取2次。如果测试方法多的话，那么反复读取json文件对性能来说是一个巨大的挑战。&lt;/p>
&lt;h3 id="优化">优化&lt;/h3>
&lt;p>对于上面的测试数据读取场景，我们可以在所有测试方法执行前读取一次数据，毕竟测试数据在所有测试方法执行过程中是保持不变的。&lt;/p>
&lt;h3 id="setupclass和teardownclass">setUpClass()和tearDownClass()&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://docs.python.org/2/library/unittest.html#unittest.TestCase.setUpClass" target="_blank" rel="noopener noreffer ">setUpClass&lt;/a>方法在每个测试用例类执行之前会执行一次，接收该class作为唯一的参数，并且必须使用装饰器&lt;a href="https://docs.python.org/2/library/functions.html#classmethod" target="_blank" rel="noopener noreffer ">classmethod()&lt;/a>&lt;/li>
&lt;li>tearDownClass: 在所有测试方法执行完之后被调用1次，调用方式跟上面的方法类似&lt;/li>
&lt;/ul>
&lt;h3 id="重构">重构&lt;/h3>
&lt;p>下面我们重构代码以达到只读取1次测试数据的目的，新建文件````test_password_3.py```，内容如下&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">unittest&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">WeakPasswordTeseCase&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TestCase&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="nd">@classmethod&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">setUpClass&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">kls&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data_file_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;./user_data.json&amp;#39;&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="s1">&amp;#39;before all test methods&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">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data_file_path&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">kls&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">test_data&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="n">f&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">def&lt;/span> &lt;span class="nf">test_week_password&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">for&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">test_data&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">data&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertTrue&lt;/span>&lt;span class="p">(&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 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="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">data&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="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertTrue&lt;/span>&lt;span class="p">(&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 class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertTrue&lt;/span>&lt;span class="p">(&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 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_dummy&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">pass&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">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&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="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>有几点需要提及一下&lt;/p></description></item><item><title>5. 实例: 找出所有是弱密码的用户</title><link>https://itest2025.pages.dev/tutorials/35-005-5-%E5%AE%9E%E4%BE%8B-%E6%89%BE%E5%87%BA%E6%89%80%E6%9C%89%E6%98%AF%E5%BC%B1%E5%AF%86%E7%A0%81%E7%9A%84%E7%94%A8%E6%88%B7/</link><pubDate>Mon, 06 Nov 2017 06:23:31 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/35-005-5-%E5%AE%9E%E4%BE%8B-%E6%89%BE%E5%87%BA%E6%89%80%E6%9C%89%E6%98%AF%E5%BC%B1%E5%AF%86%E7%A0%81%E7%9A%84%E7%94%A8%E6%88%B7/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>当我们的测试数据是下面这些的时候，我们的用例是有问题的。&lt;/p>
&lt;pre tabindex="0">&lt;code>[
 {&amp;#34;name&amp;#34;:&amp;#34;jack&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;Iloverose&amp;#34;},
 {&amp;#34;name&amp;#34;:&amp;#34;rose&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;Ilovejack&amp;#34;},
 {&amp;#34;name&amp;#34;:&amp;#34;tom&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;password123&amp;#34;},
 {&amp;#34;name&amp;#34;:&amp;#34;jerry&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;password&amp;#34;}
]
&lt;/code>&lt;/pre>&lt;p>我们的用例只能找出tom是弱密码的用户，jerry这个用户会成为漏网之鱼。&lt;/p>
&lt;h3 id="为什么">为什么&lt;/h3>
&lt;p>这是因为在unittest中，一旦某个测试方法中的断言失败，后续的断言都不会被执行。&lt;/p>
&lt;p>还原一下上面的例子，当用例在断言tom失败后，for循环就退出了，测试方法也执行完毕了，后面jerry这条数据就不会被断言。&lt;/p>
&lt;h3 id="怎么办">怎么办&lt;/h3>
&lt;p>我们可以重构一下我们的用例，让整个用例只断言1次，断言失败以后就把弱密码的用户打印出来。&lt;/p>
&lt;h3 id="代码">代码&lt;/h3>
&lt;p>修改user_data.json文件，加入一些测试数据，修改后的user_data.json文件应该是&lt;/p>
&lt;pre tabindex="0">&lt;code>[
 {&amp;#34;name&amp;#34;:&amp;#34;jack&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;Iloverose&amp;#34;},
 {&amp;#34;name&amp;#34;:&amp;#34;rose&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;Ilovejack&amp;#34;},
 {&amp;#34;name&amp;#34;:&amp;#34;tom&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;password123&amp;#34;},
 {&amp;#34;name&amp;#34;:&amp;#34;jerry&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;password&amp;#34;},
 {&amp;#34;name&amp;#34;:&amp;#34;fred&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;123456&amp;#34;},
 {&amp;#34;name&amp;#34;:&amp;#34;elma&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;654321&amp;#34;}
]
&lt;/code>&lt;/pre>&lt;p>新建&lt;code>test_password_4.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">unittest&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">WeakPasswordTeseCase1&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TestCase&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="nd">@classmethod&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">setUpClass&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">kls&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">data_file_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;./user_data.json&amp;#39;&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="s1">&amp;#39;before all test methods&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">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data_file_path&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">kls&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">test_data&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="n">f&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">def&lt;/span> &lt;span class="nf">test_week_password&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="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&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="p">[]&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">data&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">test_data&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">data&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="n">tmp_res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&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">tmp_res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tmp_res&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="p">(&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 class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tmp_res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tmp_res&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="p">(&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tmp_res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tmp_res&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="p">(&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>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="ow">not&lt;/span> &lt;span class="n">tmp_res&lt;/span>&lt;span class="p">:&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="n">append&lt;/span>&lt;span class="p">(&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 &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="p">(&lt;/span>&lt;span class="n">data&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="n">data&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="n">res&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">res&lt;/span> &lt;span class="ow">and&lt;/span> &lt;span class="n">tmp_res&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="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertTrue&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">res&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">msg&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_dummy&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">pass&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">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&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="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="运行及结果">运行及结果&lt;/h3>
&lt;p>在命令行中运行&lt;code>python test_password_4.py&lt;/code>，结果如下&lt;/p>
&lt;pre tabindex="0">&lt;code>$ python test_password_4.py
before all test methods
.F
======================================================================
FAIL: test_week_password (__main__.WeakPasswordTeseCase1)
----------------------------------------------------------------------
Traceback (most recent call last):
 File &amp;#34;test_password_4.py&amp;#34;, line 27, in test_week_password
 self.assertTrue(res, &amp;#34;\n&amp;#34;.join(msg))
AssertionError: user tom has a weak password password123
user jerry has a weak password password

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1)
&lt;/code>&lt;/pre>&lt;h3 id="我们能学到什么">我们能学到什么&lt;/h3>
&lt;ul>
&lt;li>断言一旦失败之后测试方法就会结束运行，所以一般来说1个测试方法推荐只有1个断言&lt;/li>
&lt;li>如果一个测试方法里面必须要有多个断言，那么要确保前面的断言失败之后，后面的断言就算不运行也不会影响测试的范围和结果&lt;/li>
&lt;li>for循环中的断言一旦失败，for循环就退出了&lt;/li>
&lt;li>上面演示的测试用例写法其实具备了一定的数据驱动测试的思想&lt;/li>
&lt;/ul>
&lt;p>原始封面&lt;/p></description></item><item><title>6. 命令行接口</title><link>https://itest2025.pages.dev/tutorials/35-006-6-%E5%91%BD%E4%BB%A4%E8%A1%8C%E6%8E%A5%E5%8F%A3/</link><pubDate>Sun, 05 Nov 2017 06:23:31 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/35-006-6-%E5%91%BD%E4%BB%A4%E8%A1%8C%E6%8E%A5%E5%8F%A3/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>unittest支持命令行接口，我们可以在命令行里指定运行具体的测试用例。&lt;/p>
&lt;h3 id="实例">实例&lt;/h3>
&lt;p>在&lt;code>test_password_1.py&lt;/code>中定义了&lt;code>PasswordTeseCase&lt;/code>用例，我们可以从命令行中指定只运行该用例。&lt;/p>
&lt;pre tabindex="0">&lt;code>$ python -m unittest test_password_1.PasswordTeseCase
set up
.set up
F
======================================================================
FAIL: test_week_password (test_password_1.PasswordTeseCase)
----------------------------------------------------------------------
Traceback (most recent call last):
 File &amp;#34;/Users/easonhan/code/testclass.net/src/pyunit/test_password_1.py&amp;#34;, line 21, in test_week_password
 self.assertTrue(passwd != &amp;#39;password123&amp;#39;, msg)
AssertionError: False is not true : user tom has a weak password

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1)
&lt;/code>&lt;/pre>&lt;p>还可以使用-v参数来获得更详细的输出&lt;/p>
&lt;pre tabindex="0">&lt;code>$ python -m unittest test_password_1.PasswordTeseCase -v
test_dummy (test_password_1.PasswordTeseCase) ... set up
ok
test_week_password (test_password_1.PasswordTeseCase) ... set up
FAIL

======================================================================
FAIL: test_week_password (test_password_1.PasswordTeseCase)
----------------------------------------------------------------------
Traceback (most recent call last):
 File &amp;#34;/Users/easonhan/code/testclass.net/src/pyunit/test_password_1.py&amp;#34;, line 21, in test_week_password
 self.assertTrue(passwd != &amp;#39;password123&amp;#39;, msg)
AssertionError: False is not true : user tom has a weak password

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1)
&lt;/code>&lt;/pre>&lt;p>也可以在命令行中一次指定多个测试用例类，具体的大家可以自己尝试一下。&lt;/p></description></item><item><title>7. 各种断言方法</title><link>https://itest2025.pages.dev/tutorials/35-007-7-%E5%90%84%E7%A7%8D%E6%96%AD%E8%A8%80%E6%96%B9%E6%B3%95/</link><pubDate>Sat, 04 Nov 2017 06:23:31 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/35-007-7-%E5%90%84%E7%A7%8D%E6%96%AD%E8%A8%80%E6%96%B9%E6%B3%95/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>unittest支持各种断言方法。&lt;/p>
&lt;h3 id="断言列表">断言列表&lt;/h3>
&lt;p>&lt;a href="https://docs.python.org/2/library/unittest.html" target="_blank" rel="noopener noreffer ">官方文档&lt;/a>&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>方法&lt;/th>
 &lt;th>检查点&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>assertEqual(a, b)&lt;/td>
 &lt;td>a == b&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertNotEqual(a, b)&lt;/td>
 &lt;td>a != b&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertTrue(x)&lt;/td>
 &lt;td>bool(x) is True&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertFalse(x)&lt;/td>
 &lt;td>bool(x) is False&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertIs(a, b)&lt;/td>
 &lt;td>a is b&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertIsNot(a, b)&lt;/td>
 &lt;td>a is not b&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertIsNone(x)&lt;/td>
 &lt;td>x is None&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertIsNotNone(x)&lt;/td>
 &lt;td>x is not None&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertIn(a, b)&lt;/td>
 &lt;td>a in b&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertNotIn(a, b)&lt;/td>
 &lt;td>a not in b&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertIsInstance(a, b)&lt;/td>
 &lt;td>isinstance(a, b)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertNotIsInstance(a, b)&lt;/td>
 &lt;td>not isinstance(a, b)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertRaises(exc, fun, *args, **kwds)&lt;/td>
 &lt;td>fun(*args, **kwds) raises exc&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertRaisesRegexp(exc, r, fun, *args, **kwds)&lt;/td>
 &lt;td>fun(*args, **kwds) raises exc and the message matches regex r&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertAlmostEqual(a, b)&lt;/td>
 &lt;td>round(a-b, 7) == 0&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertNotAlmostEqual(a, b)&lt;/td>
 &lt;td>round(a-b, 7) != 0&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertGreater(a, b)&lt;/td>
 &lt;td>a &amp;gt; b	2.7&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertGreaterEqual(a, b)&lt;/td>
 &lt;td>a &amp;gt;= b&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertLess(a, b)&lt;/td>
 &lt;td>a &amp;lt; b&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertLessEqual(a, b)&lt;/td>
 &lt;td>a &amp;lt;= b&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertRegexpMatches(s, r)&lt;/td>
 &lt;td>r.search(s)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertNotRegexpMatches(s, r)&lt;/td>
 &lt;td>not r.search(s)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertItemsEqual(a, b)&lt;/td>
 &lt;td>sorted(a) == sorted(b) 也支持unhashable对象&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>assertDictContainsSubset(a, b)&lt;/td>
 &lt;td>a里面所有的键值对都在b中存在&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="自己动手">自己动手&lt;/h3>
&lt;p>大家可以自己动手亲自尝试使用这些断言方法。&lt;/p></description></item><item><title>8. 断言异常</title><link>https://itest2025.pages.dev/tutorials/35-008-8-%E6%96%AD%E8%A8%80%E5%BC%82%E5%B8%B8/</link><pubDate>Thu, 02 Nov 2017 06:23:31 +0800</pubDate><guid>https://itest2025.pages.dev/tutorials/35-008-8-%E6%96%AD%E8%A8%80%E5%BC%82%E5%B8%B8/</guid><description>&lt;h3 id="背景">背景&lt;/h3>
&lt;p>我们有时候需要断言一些方法会抛出异常，这些异常需要符合我们的预期。&lt;/p>
&lt;h3 id="代码">代码&lt;/h3>
&lt;p>新建&lt;code>test_exception.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="kn">import&lt;/span> &lt;span class="nn">unittest&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">DivZeroTestCase&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">TestCase&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_should_raise_exception&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">with&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">assertRaises&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="ne">ZeroDivisionError&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="mi">1&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="mi">0&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">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;__main__&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="n">unittest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="运行及结果">运行及结果&lt;/h3>
&lt;pre tabindex="0">&lt;code>$ python test_exception.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
&lt;/code>&lt;/pre>&lt;h3 id="我们能学到什么">我们能学到什么&lt;/h3>
&lt;ul>
&lt;li>上面的例子断言了当0作为除数的时候会抛出&lt;code>ZeroDivisionError&lt;/code>&lt;/li>
&lt;li>断言异常是有套路的，使用&lt;code>with&lt;/code>语句加&lt;code>assertRaises&lt;/code>，assertRaises的参数中传入预期的异常(这些异常可能需要先import进来)，在with的子句中放上会抛出异常的语句或表达式&lt;/li>
&lt;/ul>
&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-1573496130407-57329f01f769?w=300"
 data-srcset="https://images.unsplash.com/photo-1573496130407-57329f01f769?w=300, https://images.unsplash.com/photo-1573496130407-57329f01f769?w=300 1.5x, https://images.unsplash.com/photo-1573496130407-57329f01f769?w=300 2x"
 data-sizes="auto"
 alt="https://images.unsplash.com/photo-1573496130407-57329f01f769?w=300"
 title="课程图片" />&lt;/p></description></item></channel></rss>