李從新 發表於 2019-8-16 16:50:00

Python项目中的单元测试

<h1>引入</h1>
<p>单元测试负责对最小的软件设计单元(模块)进行验证,<strong><code>unittest</code></strong>是Python自带的单元测试框架。 单元测试与功能测试都是日常开发中必不可少的部分,本文演示了Python中<strong><code>unittest</code></strong>单元测试框架的基本使用。</p>
<p>&nbsp;</p>
<h1>一个简单的测试例子</h1>
<p>定义一个类,简单的实现<strong><code>add</code></strong>、<strong><code>sub</code></strong>两个方法,并对其进行单元测试。</p>
<p>待测试的<strong><code>m1.py</code></strong>文件内容如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MyClass(object):
    </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">just a test case</span><span style="color: rgba(128, 0, 0, 1)">"""</span>
    <span style="color: rgba(0, 0, 255, 1)">def</span> <span style="color: rgba(128, 0, 128, 1)">__init__</span><span style="color: rgba(0, 0, 0, 1)">(self, x, y):
      self.x </span>=<span style="color: rgba(0, 0, 0, 1)"> int(x)
      self.y </span>=<span style="color: rgba(0, 0, 0, 1)"> int(y)

    </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> add(self):
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> self.x +<span style="color: rgba(0, 0, 0, 1)"> self.y

    </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> sub(self):
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> self.x - self.y</pre>
</div>
<p>在与<strong><code>m1.py</code></strong>同级的目录下创建<strong><code>test.py</code></strong>测试文件,使用<strong><code>unittest</code></strong>单元测试框架对A类的方法进行测试。代码内容如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> unittest
</span><span style="color: rgba(0, 0, 255, 1)">from</span> m1 <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> MyClass


</span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MyClassTest(unittest.TestCase):
    </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> setUp(self):
      self.calc </span>= MyClass(7, 5<span style="color: rgba(0, 0, 0, 1)">)

    </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> tearDown(self):
      </span><span style="color: rgba(0, 0, 255, 1)">pass</span>

    <span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> test_add(self):
      ret </span>=<span style="color: rgba(0, 0, 0, 1)"> self.calc.add()
      self.assertEqual(ret, </span>12<span style="color: rgba(0, 0, 0, 1)">)

    </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> test_sub(self):
      ret </span>=<span style="color: rgba(0, 0, 0, 1)"> self.calc.sub()
      self.assertEqual(ret, </span>2<span style="color: rgba(0, 0, 0, 1)">)


</span><span style="color: rgba(0, 0, 255, 1)">if</span> <span style="color: rgba(128, 0, 128, 1)">__name__</span> == <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">__main__</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">:
    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 构造测试集</span>
    suite =<span style="color: rgba(0, 0, 0, 1)"> unittest.TestSuite()
    suite.addTest(MyClassTest(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">test_add</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">))
    suite.addTest(MyClassTest(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">test_sub</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">))
    </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 执行测试</span>
    runner =<span style="color: rgba(0, 0, 0, 1)"> unittest.TextTestRunner()
    runner.run(suite)</span></pre>
</div>
<p>运行测试:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">demo1 $ python3 test.py
..
</span>----------------------------------------------------------------------<span style="color: rgba(0, 0, 0, 1)">
Ran </span>2 tests <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> 0.000s

OK</span></pre>
</div>
<p>到此一个简单的单元测试就完成了。</p>
<p>&nbsp;</p>
<h1 id="autoid-1-0-0">unittest框架知识点</h1>
<p>unittest框架中4个重要的概念:</p>
<ol>
<li><strong><code>test fixture:</code></strong>是初始化和清理测试数据及环境,通过重写TestCase的<strong><code>setUp()</code></strong>和<strong><code>tearDown()</code></strong>方法来实现</li>
<li><strong><code>test case:</code></strong>是测试用例</li>
<li><strong><code>test suite:</code></strong>是测试用例的集合(俗称测试套件),通过addTest加载TestCase到TestSuite中,返回一个TestSuite实例。</li>
<li><strong><code>test runner:</code></strong>的作用是运行测试用例并返回结果,通过TextTestRunner类提供的run()方法来执行<strong><code>test suite</code></strong>或<strong><code>test case</code></strong>。</li>
</ol>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h1 id="autoid-2-0-0">Django中的单元测试</h1>
<p>Django项目的app目录下都默认生成了一个</p>
<h2 id="autoid-3-0-0"><span style="color: rgba(153, 51, 0, 1)">Model部分单元测试用例</span></h2>
<p>假设项目中有一个<strong><code>Book</code></strong>的model:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Book(models.Model):
    title </span>= models.CharField(max_length=32<span style="color: rgba(0, 0, 0, 1)">)
    price </span>= models.DecimalField(max_digits=10, decimal_places=2)</pre>
</div>
<p>测试用例代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> app01/tests.py</span>
<span style="color: rgba(0, 0, 255, 1)">from</span> django.test <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> TestCase
</span><span style="color: rgba(0, 0, 255, 1)">from</span> app01.models <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> Book
</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> Create your tests here.</span>


<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> BookModelTest(TestCase):
    </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> setUp(self):
      Book.objects.create(title</span>=<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">书名</span><span style="color: rgba(128, 0, 0, 1)">'</span>, price=11.11<span style="color: rgba(0, 0, 0, 1)">)

    </span><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> test_book_model(self):
      </span><span style="color: rgba(0, 0, 255, 1)">from</span> decimal <span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> Decimal
      result </span>= Book.objects.get(title=<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">书名</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
      self.assertEqual(result.price, Decimal(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">11.11</span><span style="color: rgba(128, 0, 0, 1)">'</span>))</pre>
</div>
<p>运行测试,在项目目录下运行:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">$ python3 manage.py test
Creating test database </span><span style="color: rgba(0, 0, 255, 1)">for</span> alias <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">default</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">...
System check identified no issues (0 silenced).
.
</span>----------------------------------------------------------------------<span style="color: rgba(0, 0, 0, 1)">
Ran </span>1 test <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> 0.003s

OK
Destroying test database </span><span style="color: rgba(0, 0, 255, 1)">for</span> alias <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">default</span><span style="color: rgba(128, 0, 0, 1)">'</span>...</pre>
</div>
<p>测试用例OK…</p>
<h2 id="autoid-3-1-0"><span style="color: rgba(153, 51, 0, 1)">视图部分单元测试用例</span></h2>
<p>假设我们有个<strong><code>index</code></strong>视图,代码如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> index(request):
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> render(request, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">index.html</span><span style="color: rgba(128, 0, 0, 1)">'</span>)</pre>
</div>
<p>在<strong><code>app01/tests.py</code></strong>文件中添加测试用例代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> app01/tests.py</span>
<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> IndexPageTest(TestCase):
    </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">测试index页面</span><span style="color: rgba(128, 0, 0, 1)">"""</span>
    <span style="color: rgba(0, 0, 255, 1)">def</span><span style="color: rgba(0, 0, 0, 1)"> test_index_page_renders_index_template(self):
      </span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(128, 0, 0, 1)">测试index视图</span><span style="color: rgba(128, 0, 0, 1)">"""</span><span style="color: rgba(0, 0, 0, 1)">
      response </span>= self.client.get(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">/index/</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
      self.assertEqual(response.status_code, </span>200)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 判断状态码</span>
      self.assertTemplateUsed(response, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">index.html</span><span style="color: rgba(128, 0, 0, 1)">'</span>)<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)"> 判断渲染的模板是否正确</span></pre>
</div>
<p>在项目根目录运行<strong><code>python manage.py test</code></strong>命令:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">$ python3 manage.py test
Creating test database </span><span style="color: rgba(0, 0, 255, 1)">for</span> alias <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">default</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">...
System check identified no issues (0 silenced).
..
</span>----------------------------------------------------------------------<span style="color: rgba(0, 0, 0, 1)">
Ran </span>2 tests <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> 0.027s

OK
Destroying test database </span><span style="color: rgba(0, 0, 255, 1)">for</span> alias <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">default</span><span style="color: rgba(128, 0, 0, 1)">'</span>...</pre>
</div>
<p><strong><code>python manage.py test</code></strong>命令会查找当前项目中的tests.py文件,并运行测试用例。</p><br><br>
来源:https://www.cnblogs.com/peng104/p/11364927.html
頁: [1]
查看完整版本: Python项目中的单元测试