求教 Django test 问题

2016-09-22 14:10:36 +08:00
 laoyur

以前没怎么写过 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

2447 次点击
所在节点    Django
4 条回复
sylecn
2016-09-22 15:33:33 +08:00
流程性的东西,放到同一个 test case 里面做 setup 和 assert 。比如,如果我注册了一个用户,那么它应该可以正常登录。如果我发布了一条信息,那么应该能查询到这条信息。如果我删除了一条信息,那么应该无法查询到该信息。

不同的测试之间可以共享测试数据(比如用户名,测试消息什么的)和 setup 代码(比如创建一组用户和数据用于测试),但是不要让不同的测试场景和测试用例互相依赖。互相依赖的测试,一旦有错误,无法准确定位问题。牵一发而动全身。对开发和测试都不好。
laoyur
2016-09-22 16:07:13 +08:00
@sylecn 感谢回复

如果在同一个 def test_register(self) 里面按顺序执行 register login logout ,是没有问题的,但我就是觉得 test 的 log 输出可读性不强(特指出现错误 case 的时候,不能一眼看过去就知道流程中哪个点出了问题)
所以我才准备写一个 class-based testcase ,然后试图用里面的各 test_ 方法来表达各个被测试点

暂时我先把注册 /登录封装成通用方法,然后在各 test_* 方法中按需调用吧,不知道有没有更优雅的方法
sylecn
2016-09-22 16:16:17 +08:00
>> 觉得 test 的 log 输出可读性不强(特指出现错误 case 的时候,不能一眼看过去就知道流程中哪个点出了问题)

@laoyur IDE 都是直接跳转到出错的那个 assert ,并且显示对应的 expect value 和 real value 。不知道你说的一眼看过去不容易看出哪个点是什么意思。实际做 TDD 的时候,通常都直接看错误的 assert 的代码行,不会去着重看测试名称的。
laoyur
2016-09-22 16:33:41 +08:00
@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()

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/308136

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX