$ ls ~yifei/notes/

使用 pytest 进行测试

Posted on:

Last modified:

Python 常用的测试工具有三种:

  1. unittest
  2. nose[2]
  3. pytest

其中 unittest 完全是从 JUnit 移植过来的,用起来稍微有些别扭。nose 和 pytest 相比,网上 大多推荐 pytest。详细的比较可以见参考文档。

和传统 unittest 中复杂的 assertEqual 等语句不同的是,pytest 中只需要简单地写 assert 语句就可以了。

命令行选项

  • pytest some_mod.py 运行某个文件中的中的测试
  • pytest tests/ 运行某个目录中的测试
  • pytest -x 在第一个错误的地方结束
  • pytest --pdb,当出现异常的时候,打开 pdb

  测试函数使用 test_开头,pytest 默认会查找当前目录中的 test_ 开头或者 _test 结尾的文件 中的测试并运行。使用 assert 来验证语句。

测试某个异常抛出

import pytest
def f():
    raise SystemExit(1)
 
def test_mytest():
    with pytest.raises(SystemExit):
        f()

执行顺序

如果在一个文件中定义了多个测试函数,那么 pytest 将按照函数定义的顺序执行。

setup 和 teardown

setup 和 teardown 用来在测试开始前加载资源,并在测试结束后卸载资源。

  1. 可以在文件中定义 setup_moduleteardown_module
  2. 可以在类中定义 setup_classteardown_class 中定义加载和卸载方法。

mock 和 patch

太复杂了,还没仔细研究。

如何测试一个 while True 循环

如果真的是一个无限循环,那么是没法测试的,不过:

  1. 把 True 改成一个条件,在测试的时候 mock 一下
  2. 把循环内部改成一个函数,然后测试这个函数
  3. 找到循环运行时候的副作用(比如写了文件),新开一个线程中运行这个循环,然后测试副作用

其中的第一个是比较好做的,对代码的侵入性也最小。具体可以这样做:

class Foo(object):
    RUNNING = 1  # sentinel value

    def start(self):
        while self.RUNNING:
            do_something_useful()

然后在测试中:

foo = Foo()
type(foo).RUNNING =  PropertyMock(side_effect=[1, 0])

asyncio

安装 pytest-asyncio 插件,然后在 pyproject.toml 中增加:

[tool.pytest.ini_options]
pythonpath = [ "." ]
asyncio_mode = "auto"

找不到当前路径下的包也解决了。

其他测试工具

unittest

unittest 这个库没有按照 PEP8 来,看着就不爽

import unittest

def fun(x):
    return x + 1

class MyTest(unittest.TestCase):
    def setUp(self):
        # bootstrapping
    def tearDown(self):
        #clean up
    def test(self):
        self.assertEqual(fun(3), 4)

Unittest 中的 assert 方法:

方法含义
assertEqual(a, b)a == b
assertNotEqual(a, b)a != b
assertTrue(x)bool(x) is True
assertFalse(x)bool(x) is False
assertIs(a, b)a is b
assertIsNot(a, b)a is not b
assertIsNone(x)x is None
assertIsNotNone(x)x is not None
assertIn(a, b)a in b
assertNotIn(a, b)a not in b
assertIsInstance(a, b)isinstance(a, b)
assertNotIsInstance(a, b)not isinstance(a, b)
python -m unittest fun_test

Note: only method starts with test is run by unittest module

doctest

"""
>>> print 'hello'
hello
"""

doctest.testmod()

doctest.testmod(verbose=True) or python module.py -v

Notes on testing classes

  • If it's testing the class as a whole, I'd put them in the class' docstring.
  • If it's testing the constructor, I'd put them in the constructor's docstring.
  • If it's testing a method (as it seems to be in this case), I'd actually put it them in that method's docstring.

需要转义 \n

需要使用<BLANKLINE>来代表空行

参考

  1. https://www.reddit.com/r/Python/comments/50nqlp/is_nose_still_relevant_how_about_unittest/
  2. https://agopian.info/presentations/2015_06_djangocon_europe/?full#pythonic
  3. http://docs.python-guide.org/en/latest/writing/tests/
  4. https://realpython.com/python-testing/
  5. https://pytest-benchmark.readthedocs.io/en/latest/
  6. https://medium.com/@yeraydiazdiaz/what-the-mock-cheatsheet-mocking-in-python-6a71db997832
  7. https://softwareengineering.stackexchange.com/questions/151360/how-to-unit-test-with-lots-of-io
  8. https://stackoverflow.com/questions/16541571/unit-testing-methods-with-file-io
  9. https://matthias-endler.de/2018/go-io-testing/
  10. https://dzone.com/articles/unit-testing-file-io
  11. https://www.quora.com/How-do-we-test-while-true-loop-using-the-Python-unittest
  12. http://oldhammade.net/blog/2016/06/15/unit_testing_infinite_while_loops_in_python.html
WeChat Qr Code

© 2016-2022 Yifei Kong. Powered by ynotes

All contents are under the CC-BY-NC-SA license, if not otherwise specified.

Opinions expressed here are solely my own and do not express the views or opinions of my employer.

友情链接: MySQL 教程站