以前没怎么写过 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 1
流程性的东西,放到同一个 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.get('/logout/') assert r.status_code == 200 def test(self): self._test_register() self._test_login() self._test_logout() |