Python单元测试unittest入门初探-【原创】

本文先通过基本概念的简单介绍,再以一个简单的Python项目为例,讲解在工程实践中如何通过unittest编写测试用例,同时也简单介绍了unittest的一些常用写法。

引入

什么是单元测试?

单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于Python来说,最小可测试单元就是函数或者方法。

Python支持的测试库有pytestunittest等,这篇文件主要介绍如何使用Python内置的标准库unittest编写单元测试用例。

项目结构

首先需要介绍一下整个项目的一个目录结构:

.
├── example.py  # 入口文件
├── tests  # 测试文件
│   ├── __init__.py
│   └── test_vector.py
└── vector  # 所有核心代码
    ├── __init__.py
    └── vector.py

项目代码如下:

# vector/__init__.py
from .vector import Vector
# vector/vector.py
class Vector:
    def __init__(self, x, y):
        if isinstance(x, (int, float)) and \
           isinstance(x, (int, float)):
            self.x = x
            self.y = y
        else:
            raise ValueError("not a number")

    def add(self, other):
        return Vector(self.x + other.x,
                      self.y + other.y)

    def mul(self, factor):
        return Vector(self.x * factor,
                      self.y * factor)

    def dot(self, other):
        return self.x * other.x + \
               self.y * other.y

    def norm(self):
        return (self.x * self.x +
                self.y * self.y) ** 0.5

编写第一个测试用例

我们在’tests’目录下,以’test_’开头创建一个Python文件,例如项目中的’test_vector.py’文件,我们后面所有的测试代码都在里面编写运行。

通过unittest编写测试用例,需要继承MARKDOWN_HASH2dbf31d49599462c7b7c0b4a56392a62MARKDOWNHASH创建一个类,类名建议以’Test’开头或结尾,而类方法则一定要以’test\‘开头。

下面的测试用例分别测试了x是否等于1,y是否等于2:

import unittest
from vector import Vector

class TestVector(unittest.TestCase):
    def test_init(self):
        v = Vector(1, 2)
        self.assertEqual(v.x, 1)
        self.assertTrue(v.y == 2)

编写完成后只需要在项目根目录下输入以下命令:

python -m unittest

Python就会在你的目录中自动寻找测试代码,并且自动运行测试用例,运行结果如下:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

具体提供哪些测试方法,我们可以参考一下unittest官方文档。

从上面示例可以看到,官方提供了assertTrue方法,既然如此,为什么我们不直接全部使用这个方法就好了?其他方法还有什么作用呢?

下面对assertEqualassertTrue做一个简单对比,来说明其他方法的意义。

先看看assertEqual如果测试不通过,会报什么错误:

import unittest
from vector import Vector

class TestVector(unittest.TestCase):
    def test_init(self):
        v = Vector(1, 2)
        self.assertEqual(v.x, 0)
        # self.assertTrue(v.x == 0)

测试结果:

F
======================================================================
FAIL: test_init (tests.test_vector.TestVector)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/xuzhanhong/Development/Git/PythonNotes/测试/tests/test_vector.py", line 8, in test_init
    self.assertEqual(v.x, 0)
AssertionError: 1 != 0

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

我们看报错就知道v.x是1,不等于0。

如果我们是使用assertTrue方法,再看一下报错:

F
======================================================================
FAIL: test_init (tests.test_vector.TestVector)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/xuzhanhong/Development/Git/PythonNotes/测试/tests/test_vector.py", line 9, in test_init
    self.assertTrue(v.x == 0)
AssertionError: False is not true

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

那我们只能得到False is not true的错误提示,并不知道为什么上面的等式是False

所以说其他方法也有自己的意义,而不是直接梭哈assertTrue方法就好了。

其他常见测试需求

在测试前后分别执行一些操作

如果想在测试类运行测试之前跟运行结束之后进行一些操作的话,就需要用到setUpClasstearDownClass

import unittest
from vector import Vector

class TestVector(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        print('start')

    @classmethod
    def tearDownClass(cls):
        print('end')

    def test_init(self):
        v = Vector(1, 2)
        self.assertEqual(v.x, 1)
        self.assertTrue(v.y == 2)

这样在运行所有测试方法的时候都会自动执行setUpClasstearDownClass方法了:

start
.end

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

跳过某些情况

还有一个很常见的需求就是希望我的测试在某些情况下不运行,这种时候就需要用到unittest.skipIf装饰器。

这个装饰器传入两个参数,第一个参数是一个boolean,当条件成立测试不运行,第二个参数则是提示信息。

下面的示例表示这个测试不在Python3.8以下版本运行:

  @unittest.skipIf(sys.version_info < (3, 8),
                   "Only support 3.8+")
  def test_init(self):
      v = Vector(1, 2)
      self.assertEqual(v.x, 1)
      self.assertTrue(v.y == 2)

由于我的Python版本是3.7,所以运行时会显示跳过:

start
send

----------------------------------------------------------------------
Ran 1 test in 0.000s

OK (skipped=1)

同理,我们可以编写其他的跳过条件,例如跳过Windows系统,跳过某些变量值等等。

小结

本文主要内容小结:

  • 单元测试基础概念。

  • Python工程实践项目结构。

  • 如何使用unittest编写运行一个测试用例。

  • unittest常用写法。

最后

更多Python知识尽在【Python都知道】公众号,欢迎大家!!
扫描下方二维码,关注公众号,了解更多Python内容


小白学堂 » Python单元测试unittest入门初探-【原创】

就聊挣钱,一个带着你做副业的社群。

立即查看 了解详情