猿问

如何避免单元测试 Django 视图中的组合爆炸

我有一个相当复杂的 Django(Rest Framework)视图,它更新数据库中的一个对象。

为了更新对象,需要满足一些条件(这些不是真正的条件,但它们是相似的):

  1. 用户必须登录

  2. 用户名需要以 admin

  3. 更新数据需要根据一些规则有效

  4. is_moon_phase_ok需要返回一个单独的昂贵函数True

我正在尝试为此视图编写一组可靠的单元测试,我提出的场景如下:

when not logged in, return 401

when not logged in, return {"fail": "login"}

when not logged in, don't touch database

when not logged in, don't check moon phase

when username is not admin_*, return 401

when username is not admin_*, return {"fail": "username"}

when username is not admin_*, don't touch database

when username is not admin_*, don't check moon phase

when invalid data, return 400

when invalid data, return {"fail": "data"}

when invalid data, don't touch database

when invalid data, don't check moon phase

when logged in, return 200

when logged in, return updated data

when logged in, check moon phase

when logged in, update database

如您所知,这些单元测试中的每一个都需要大量代码来设置然后执行。就我而言,每个单元测试有 7 到 20 行代码。


想象一下,如果需求发生变化,并且它们确实发生了变化,那么忽略测试用例、确保它们仍然适用、根据新需求更新它们等会是多么痛苦。


有没有更好的方法来完成同样的事情,使用相同的测试覆盖率,但用更少的劳动力?


MMMHUHU
浏览 107回答 1
1回答

哆啦的时光机

对我来说,这听起来很像参数化测试,pytest 对此有支持,基本上你可以编写一个测试并提供输入参数,以及预期的。因此,您只需编写一个测试,但足够通用以支持不同的参数,从而减少了需要维护的代码。在幕后 pytest 使用您定义的参数一一运行相同的测试。编写通用测试可能会引入一些逻辑(如您在我的示例中所见),但我认为您可以接受作为一个通用的例子:@pytest.mark.parametrize('is_admin,expected_status_code,expected_error', [    (True, 200, {}),    (False, 401, {"fail": "login"})])def test_sample(is_admin, expected_status_code, expected_data):    # do your setup    if is_admin:        user =  create_super_user()    else:        user =  normal_user()    # do your request    response = client.get('something')    # make assertion on response    assert response.status_code == expected_status_code    assert response.data == expected_data您还可以有多个参数层,例如:@pytest.mark.parametrize('is_admin', [    True,    False])@pytest.mark.parametrize('some_condition,expected_status_code,expected_error', [    (True, 200, {}),    (False, 401, {"fail": "login"})])这将为 is_admin (True/False) 和其他参数的每个组合执行测试,好吗?
随时随地看视频慕课网APP

相关分类

Java
我要回答