以前没怎么写过 Test ,如有原则性错误请勿喷……
我没有直接用 Django 的 TestCase ,而是用的 pytest-django ,但原理都差不多的
我想达到如下的目的:
import pytest @pytest.mark.django_db class Test_API: @classmethod def setup_class(cls): cls.client = APIClient() @classmethod def teardown_class(cls): pass def test_register(self): resp = self.client.put('/account/', { 'username': 'user1', 'password': 'pass1' }) assert resp.status_code == 200 def test_login(self): resp = self.client.post('/login/', { 'username': 'user1', 'password': 'pass1' }) assert resp.status_code == 200 def test_logout(self): r = self.client.get('/logout/') assert r.status_code == 200
我的目的是:先跑注册,再跑登录,最后登出,简单来说,我想编写有前后依赖关系的 test case 然而上面的代码运行结果如下:
tests/test_api.py::Test_API::test_register PASSED tests/test_api.py::Test_API::test_login FAILED tests/test_api.py::Test_API::test_logout FAILED
问题的原因我知道,文档中也有说明,跟 Django 类似,每一个 test 跑完后,会自动 rollback 。这个原因导致了我上述目的无法达成。
然而我很想知道,有没有办法禁用每一个 test 跑完后的自动 rollback?最好能让我在setup
中手动开启 transaction ,在teardown
中手动 rollback
我找遍了 Django 、 pytest 、 pytest-django 的文档,不知道怎么搞。
还是说我的思路根本就是错误的,不要写这种有依赖性的 test case?
1 sylecn 2016-09-22 15:33:33 +08:00 ![]() 流程性的东西,放到同一个 test case 里面做 setup 和 assert 。比如,如果我注册了一个用户,那么它应该可以正常登录。如果我发布了一条信息,那么应该能查询到这条信息。如果我删除了一条信息,那么应该无法查询到该信息。 不同的测试之间可以共享测试数据(比如用户名,测试消息什么的)和 setup 代码(比如创建一组用户和数据用于测试),但是不要让不同的测试场景和测试用例互相依赖。互相依赖的测试,一旦有错误,无法准确定位问题。牵一发而动全身。对开发和测试都不好。 |
2 laoyur OP @sylecn 感谢回复 如果在同一个 def test_register(self) 里面按顺序执行 register login logout ,是没有问题的,但我就是觉得 test 的 log 输出可读性不强(特指出现错误 case 的时候,不能一眼看过去就知道流程中哪个点出了问题) 所以我才准备写一个 class-based testcase ,然后试图用里面的各 test_ 方法来表达各个被测试点 暂时我先把注册 /登录封装成通用方法,然后在各 test_* 方法中按需调用吧,不知道有没有更优雅的方法 |
3 sylecn 2016-09-22 16:16:17 +08:00 >> 觉得 test 的 log 输出可读性不强(特指出现错误 case 的时候,不能一眼看过去就知道流程中哪个点出了问题) @laoyur IDE 都是直接跳转到出错的那个 assert ,并且显示对应的 expect value 和 real value 。不知道你说的一眼看过去不容易看出哪个点是什么意思。实际做 TDD 的时候,通常都直接看错误的 assert 的代码行,不会去着重看测试名称的。 |
4 laoyur OP @sylecn 谢谢! 现在把主帖中的代码换了一种写法,稍微看着顺眼一点了: import pytest @pytest.mark.django_db class Test_API: @classmethod def setup_class(cls): cls.client = APIClient() @classmethod def teardown_class(cls): pass def _test_register(self): resp = self.client.put('/account/', { 'username': 'user1', 'password': 'pass1' }) assert resp.status_code == 200 def _test_login(self): resp = self.client.post('/login/', { 'username': 'user1', 'password': 'pass1' }) assert resp.status_code == 200 def _test_logout(self): r = self.client.et('/logout/') assert r.status_code == 200 def test(self): self._test_register() self._test_login() self._test_logout() |