pytest官方文档 6.2 中文翻译版(第十五章):UNITTEST.TESTCASE 支持

灰太狼 2022-10-15 08:56 219阅读 0赞

pytest支持开箱即用的运行以 Python unittest 为基础的测试。这是为了在已有的在以unittest为基础的(unittest-based)测试上面进行扩充,使用pytest作为运行器。同时,这也可以让测试逐步的使用pytest提供的功能以获取个别更多的优势。
使用pytest运行已存在的unittest风格的测试:

  1. pytest tests

pytest会自动的收集 unittest.TestCase 的子类和在test_.py 或是_test.py文件中的测试方法。
几乎所有的unittest的功能都是支持的:

  • @unittest.skip 修饰器
  • setUp/tearDown
  • setUpClass/tearDownClass
  • setUpModule/tearDownModule
    直至今日pytest仍未支持的功能:
  • load_tests protocol;
  • subtests;

15.1 开箱即用的优点

使用pytest来运行你的测试代码,你可以在不修改代码的情况下使用很多pytest的功能:

  • 在结果跟踪中查看更多的信息
  • 标准的输入输出捕获(stdout and stderr capturing)
  • 使用-k 和 -m选择要执行的测试
  • 在第一个失败之后停止测试
  • 使用-pdb命令在测试失败的时候进行debug
  • 使用pytest-xdist将测试分散到多个CPU上
  • 使用assert来替换self.assert方法(unittest2pytest插件可以帮我们做到这一切)

15.2 在unittest.TestCase 子类中使用pytest的功能

下面的pytest功能可以在unittest.TestCase的子类中使用:

  • 标记(mark):skip, skipif , xfail
  • autouse的夹具

下面这些pytest的功能不能使用,因为不同的设计观,这些功能可能永远不会支持:

  • 夹具(除了autouse的夹具)
  • 参数化(Parametrization)
  • 自定义的钩子

第三方插件可能有用,也可能没用,取决于插件本身和测试代码。

15.3 使用标记在unittest.TestCase的子类中插入夹具

使用pytest运行unittest允许你在其中使用夹具的机制。假设你至少浏览过pytest 夹具功能,我们直接跳到这个例子,它集成了一个pytest的db_class夹具,准备了一个类中使用的数据库对象,然后再一个unittest风格的测试中引用了它:

  1. # content of conftest.py
  2. # 我们再下面定义了一个夹具,在测试中可以通过它的名字使用它
  3. import pytest
  4. @pytest.fixture(scope="class")
  5. def db_class(request):
  6. class DummyDB:
  7. pass
  8. # 在引入的测试上下文中设置一个属性
  9. request.cls.db = DummyDB()

上面的代码定义了一个 db_class 函数,如果使用的话,每个测试类都会调用一次,也会给测试类设置一个DummyDB实例的 db 属性。要实现这样的一个夹具,它需要一个特殊的request对象,这个request对象可以访问测试的上下文,就可以访问像cls这样的对象,也可以访问使用了这个夹具的类。这种结构将夹具的编写和真实的代码分开,也使得只需要引用一下夹具的名字,就可以复用这个夹具。下面我们来写一个真实的unittest.TestCase来使用我们的夹具:

  1. # content of test_unittest_db.py
  2. import unittest
  3. import pytest
  4. @pytest.mark.usefixtures("db_class")
  5. class MyTest(unittest.TestCase):
  6. def test_method1(self):
  7. assert hasattr(self, "db")
  8. assert 0, self.db # fail for demo purposes
  9. def test_method2(self):
  10. assert 0, self.db # fail for demo purposes

@pytest.mark.usefixtures(“db_class”)这个类修饰器确保了这个类只调用了一次 db_class。通过故意的失败断言,我们可以在跟踪信息中看到self.db的真实值:

  1. $ pytest test_unittest_db.py
  2. =========================== test session starts ============================
  3. platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y
  4. cachedir: $PYTHON_PREFIX/.pytest_cache
  5. rootdir: $REGENDOC_TMPDIR
  6. collected 2 items
  7. test_unittest_db.py FF [100%]
  8. ================================= FAILURES =================================
  9. ___________________________ MyTest.test_method1 ____________________________
  10. self = <test_unittest_db.MyTest testMethod=test_method1>
  11. def test_method1(self):
  12. assert hasattr(self, "db")
  13. > assert 0, self.db # fail for demo purposes
  14. E AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef>
  15. E assert 0
  16. test_unittest_db.py:10: AssertionError
  17. ___________________________ MyTest.test_method2 ____________________________
  18. self = <test_unittest_db.MyTest testMethod=test_method2>
  19. def test_method2(self):
  20. > assert 0, self.db # fail for demo purposes
  21. E AssertionError: <conftest.db_class.<locals>.DummyDB object at 0xdeadbeef>
  22. E assert 0
  23. test_unittest_db.py:13: AssertionError
  24. ========================= short test summary info ==========================
  25. FAILED test_unittest_db.py::MyTest::test_method1 - AssertionError: <conft...
  26. FAILED test_unittest_db.py::MyTest::test_method2 - AssertionError: <conft...
  27. ============================ 2 failed in 0.12s =============================

上面默认的pytest跟踪信息显示两个方法使用了同一个self.db实例,这跟我们上面写class范围的夹具的目的是一样的。

15.4 使用autouse的夹具和访问其他夹具

虽然显式的指定你要使用的夹具是比较推荐的,但是一些情况下你可能希望在一些给定的上下文中自动的使用某个夹具。毕竟,传统的unittest风格的setup就是隐式的实现的,也有可能式你已经习惯了,或者你真的喜欢这么写。
你可以给你的夹具函数标上 @pytest.fixture(autouse=True),然后在你需要使用它的上下文中定义它。我们看一个例子:initdir初始化目录的夹具,它的作用是让一个类中的所有的方法都在一个临时目录中运行,这个目录要事先初始化一个 samplefile.ini。我们的initdir夹具使用pytest内置的tmpdir夹具去代理创建临时目录的过程。

  1. # content of test_unittest_cleandir.py
  2. import pytest
  3. import unittest
  4. class MyTest(unittest.TestCase):
  5. @pytest.fixture(autouse=True)
  6. def initdir(self, tmpdir):
  7. tmpdir.chdir() # change to pytest-provided temporary directory
  8. tmpdir.join("samplefile.ini").write("# testdata")
  9. def test_method(self):
  10. with open("samplefile.ini") as f:
  11. s = f.read()
  12. assert "testdata" in s

通过autouse标志,initdir夹具会被应用于它定义的类中的所有方法。这种方式是前面例子中使用 @pytest.mark.usefixtures(“initdir”) 的简便实现。
运行这个模块:

  1. $ pytest -q test_unittest_cleandir.py
  2. . [100%]
  3. 1 passed in 0.12s

测试通过了,原因是initdir夹具在test_method执行之前先执行了。

注意: unittest.TestCase不能直接接收夹具参数,在实现过程中这可能会影响pytest运行普通的 unittest 测试。
上面的usefixtures和autouse的例子是我们在unittest中使用夹具的正确方式。
你可以逐渐的替换掉继承 unittest.TestCase 的方式,转而使用assert,后面就可以一步一步的使用pytest提供的所有的支持了。

注意:由于两个架构的不同,unittest中的setup 和 teardown是在测试执行过程之中运行的,而不是pytest标准的setup和teardown中运行的。在测试出错的时候,理解这样的不同很有用。例如,如果一个unittest的测试在setup的时候出错了,pytest会报告setup的时候没有错误,而执行测试的时候出错了。

发表评论

表情:
评论列表 (有 0 条评论,219人围观)

还没有评论,来说两句吧...

相关阅读