V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
laoyur
V2EX  ›  Django

求教 Django test 问题

  •  
  •   laoyur · 2016-09-22 14:10:36 +08:00 · 2337 次点击
    这是一个创建于 2744 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

    不同的测试之间可以共享测试数据(比如用户名,测试消息什么的)和 setup 代码(比如创建一组用户和数据用于测试),但是不要让不同的测试场景和测试用例互相依赖。互相依赖的测试,一旦有错误,无法准确定位问题。牵一发而动全身。对开发和测试都不好。
    laoyur
        2
    laoyur  
    OP
       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
        3
    sylecn  
       2016-09-22 16:16:17 +08:00
    >> 觉得 test 的 log 输出可读性不强(特指出现错误 case 的时候,不能一眼看过去就知道流程中哪个点出了问题)

    @laoyur IDE 都是直接跳转到出错的那个 assert ,并且显示对应的 expect value 和 real value 。不知道你说的一眼看过去不容易看出哪个点是什么意思。实际做 TDD 的时候,通常都直接看错误的 assert 的代码行,不会去着重看测试名称的。
    laoyur
        4
    laoyur  
    OP
       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()
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1548 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 17:11 · PVG 01:11 · LAX 10:11 · JFK 13:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.