有没有更快的方法为Django视图编写类似的测试用例?

时间:2017-11-10 04:46:33

标签: python django python-unittest parameterized-tests

基本上,我意识到我正在为多个模型的类似URL编写相同的测试用例(test_update_with_only_1_field

from django.test import RequestFactory, TestCase
class BaseApiTest(TestCase):
def setUp(self):
    superuser = User.objects.create_superuser('test', 'test@api.com', 'testpassword')
    self.factory = RequestFactory()
    self.user = superuser
    self.client.login(username=superuser.username, password='testpassword')

class SomeModelApiTests(base_tests.BaseApiTest):
def test_update_with_only_1_field(self):
    """
    Tests for update only 1 field 

    GIVEN the following shape and related are valid
    WHEN we update only with just 1 field
    THEN we expect the update to be successful
    """
    shape_data = {
        'name': 'test shape',
        'name_en': 'test shape en',
        'name_zh_hans': 'test shape zh hans',
        'serial_number': 'test shape serial number',
        'model_name': {
            'some_field': '123'
        }
    }

    data = json.dumps(shape_data)
    response = self.client.post(reverse('shape-list-create'), data, 'application/json')
    self.assertEqual(response.status_code, status.HTTP_201_CREATED)

    some_model = response.data['some_model']
    new_some_field = '12345'

    data = json.dumps({'some_field': new_some_field, 'id': response.data['some_model']['id']})
    response = self.client.put(reverse('some-model', args=[some_model['id']]), data, 'application/json')
    self.assertEqual(response.status_code, status.HTTP_200_OK)
    self.assertEqual(new_some_field, response.data['some_field'])

我需要这样做超过10次。我已经这样做了。

每次唯一的区别是以下短语" some_model"," some-model"和" some_field"

我想知道是否有更快的方法来做到这一点。

我可以抽象地思考两种方式:

  1. 在文本编辑器中创建一个模板,以某种方式生成最终的测试用例,然后我将其复制并粘贴。我正在使用sublime text 3虽然我可以切换到另一个文本编辑器

  2. 我可以通过将此测试用例转换为单个测试类可以调用的行为类的形式编写更多代码。又名作文。

  3. 哪一个更有意义,或者有不同的方法来做到这一点?

    请注意,BaseApi类也由其他测试类继承,具有重复的测试用例方法。

4 个答案:

答案 0 :(得分:4)

我想您想要的是"参数化测试",标准unittest可以使用parameterized包执行此操作:

import unittest
from parameterized import parameterized

class SomeModelApiTests(unittest.TestCase):

    @parameterized.expand([
        ('case1', 'm1', 'f1', 'nf1'),
        ('case1', 'm2', 'f2', 'nf2'),
    ])
    def test_update_with_only_1_field(self, dummy_subtest_name, model_name, field_name, new_field_value):
        print(model_name, field_name, new_field_value)

将产生:

test_update_with_only_1_field_0_case1 (t.SomeModelApiTests) ... m1 f1 nf1
ok
test_update_with_only_1_field_1_case1 (t.SomeModelApiTests) ... m2 f2 nf2
ok

pytest测试框架在parameterized tests内置了更好的支持,值得关注。

答案 1 :(得分:2)

您可以创建" some_model"的列表/词典。测试,并使用subtesthttps://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests)为您的每个" some_model"项目

my_list_of_model = [FirstModel, SecondModel]

for my_model in my_list_of_model:
    with subTest(model=mymodel):
        # Testing model here

如果您希望每个模型都有不同的TestCase,我认为多重继承是可行的方法:

class BaseApiTestCase(TestCase):
    def setUp():
        # Setup stuff

class RepetitiveTestCaseMixin:
    # Class to do the repetitive stuff
    def test_update_should_work(self):
        # Do some thing with self.model and self.field here

class ModelTestCase(BaseApiTestCase, RepetitiveTestCaseMixin):
    @classmethod
    def setUpClass(cls):
       super().setUpClass()

       cls.model = MyModel
       cls.field = 'some_field'

答案 2 :(得分:2)

我工作的项目我们有时会使用mixin +"定制钩子"当测试需要重复时。 (和" shape-list-create"等端点可能会发生变化/重构)

问题示例:

class TestUpdateWithOnly1FieldMixin(object):
    some_model = None
    some_field = None
    some_model2 = None

    def get_some_model(self):
        return self.some_model

    def get_some_field(self):
        return self.some_field

    def get_some_model2(self):
        return self.some_model2

    def test_update_with_only_1_field(self):
        some_model = self.get_some_model()
        # represents some-model in example
        some_model2 = self.get_some_model2()
        some_field = self.get_some_field()

        shape_data = {
            'name': 'test shape',
            'name_en': 'test shape en',
            'name_zh_hans': 'test shape zh hans',
            'serial_number': 'test shape serial number',
            'model_name': {
                some_field: '123'
            }
        }

      data = json.dumps(shape_data)
      response = self.client.post(reverse('shape-list-create'), data, 'application/json')
      self.assertEqual(response.status_code, status.HTTP_201_CREATED)

      some_model_data = response.data[some_model]



class SomeModelApiTests(base_tests.BaseApiTest, TestUpdateWithOnly1FieldMixin):
    some_model = 'choose your model'
    some_field = 'some_field'
    some_model2 = 'some-model'

    def get_some_field(self):
        # Do customization
        return 'some-field after customize'

如何拆分定制钩子以及放入mixin等内容是基于这种情况。 在我看来,目标是让实际的测试用例易于遵循。 (也许将" post shape-list-create"移到一个单独的函数中,因为它可能与该测试用例不太相关)

另一个例子,有点过分定制,但只是为了给出一个想法。

class TestWithGoodNameMixin(object):
    some_model = None
    some_field = None

    # "Customization hooks"

    def get_shape_data(self):
        return {self.some_field: 'x'}

    def create_model(self, shape_data):
        response = self.client.post(reverse('shape-list-create'), shape_data,
                                    'application/json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        return response[self.some_model]

    def create_put_data(self, some_model_data):
        # Add default implementation
        pass

    # .....

    def test_update_with_only_1_field(self):
        shape_data = self.get_shape_data()
        some_model_data = self.create_model(shape_data)

        data = self.create_put_data(some_model_data)
        response = self.put_data(data)

        self.assert_put_response(response)

答案 3 :(得分:0)

您可以使用 pytest 包进行单元测试。 它非常简单易用。

@pytest.mark.parametrize()装饰器可用于实现该功能。

参数化测试用例的示例如下:

import pytest
class SampleTesting(object):
    data_for_test = [
                      ('{inputdata1:value1}','output1'),
                      ('{inputdata1:value2}','output2')
                     ]
   @pytest.mark.parametrized('input_data, expected_output', data_for_test)
   def test_sample_function(self, input_data, expected_output):
       response = function_to_be_tested(input_data)
       assert response == expected_output

您可以在docs'

中详细了解此装饰器

您还可以使用@pytest.fixture()装饰器来设置测试功能。