Есть ли более быстрый способ написания похожих тестов для представлений Django?
По сути, я понимаю, что пишу один и тот же контрольный пример (test_update_with_only_1_field
) для аналогичного URL для нескольких моделей
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"
Мне было интересно, есть ли более быстрый способ сделать это.
Я могу мыслить абстрактно двумя способами:
создайте шаблон в текстовом редакторе, который каким-то образом может сгенерировать окончательный тестовый пример, который я затем скопирую и вставлю. Я использую возвышенный текст 3, хотя я могу переключиться на другой текстовый редактор
Есть способ, которым я могу написать немного больше кода в форме преобразования этого тестового примера в класс поведения, который может вызвать отдельный тестовый класс. ака состав.
Какой из них имеет больше смысла или есть другой способ сделать это?
Обратите внимание, что класс BaseApi также наследуется другим классом тестирования, который НЕ имеет такого метода повторяющихся тестов.
4 ответа
Я думаю, что вы хотите, это "параметризованные тесты", стандартные unittest
может сделать это с параметризованным пакетом:
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
инфраструктура тестирования лучше поддерживает встроенные параметризованные тесты, на которые стоит обратить внимание.
В проектах, над которыми я работаю, мы иногда используем 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)
Вы можете создать список / dict для "some_model" для тестирования и использовать subtest
( https://docs.python.org/3/library/unittest.html) для каждого из ваших элементов "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'
Вы можете использовать пакет 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
Вы можете прочитать больше об этом декораторе в документации
Вы также можете использовать @pytest.fixture()
декоратор для настройки тестовой функции.