Как провести модульное тестирование формы с чистым полем в Django?
Я потратил довольно много времени на создание некоторых модульных тестов, и одной из проблем было задание некоторых полей, которые я определяю как nullable и blankable. Подстановка фиктивных значений не была проблемой, но я задался вопросом: как быть с полями, которые должны быть пустыми, в частности, для чисел?
.
Позвольте мне написать в качестве примера выдержку из моего кода.
Модель:
class Company(models.Model):
company_name = models.CharField("nom", max_length=200)
comp_slug = models.SlugField("slug")
street_num = models.IntegerField("N° de rue", null=True, blank=True)
street_cplt = models.CharField("complément", max_length=50, null=True, blank=True)
address = models.CharField("adresse", max_length=300)
Форма:
class CompanyForm(forms.ModelForm):
company_name = forms.CharField(label="Société", disabled=True)
class Meta:
model = Company
exclude = []
Вид:
def adm_options(request, comp_slug):
'''
Manage Company options
'''
company = Company.get_company(comp_slug)
comp_form = CompanyForm(request.POST or None, instance=company)
if request.method == "POST":
if comp_form.is_valid():
comp_form.save()
return render(request, "polls/adm_options.html", locals())
Очень простой модульный тест:
def create_dummy_company(name):
return Company.objects.create(
company_name=name,
comp_slug=slugify(name),
street_num=1,
street_cplt='',
address='dummy address'
)
def test_adm_options_update(self):
self.company = create_dummy_company("Test company")
url = reverse("polls:adm_options", args=[self.company.comp_slug])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.company.address = 'new address'
response = self.client.post(
reverse("polls:adm_options", args=[self.company.comp_slug]),
self.company.__dict__,
)
self.company.refresh_from_db()
self.assertEqual(response.status_code, 200)
Конечно, ключевым моментом является размещение формы после обновления. Различные случаи, с которыми я столкнулся, были следующими:
- Нет проблем с тестом, как написано выше
- Если я опускаю одно из полей (
street_num
илиstreet_cplt
), пост поднимаетсяTypeError: Cannot encode None as POST data. Did you mean to pass an empty string or omit the value?
Это может быть основной проблемой, поскольку оба поля могут быть пустыми .
- Тогда как быть с числовыми полями? Как я могу установить
street_num
на пустое место? Если я пытаюсьstreet_num=''
, то возникает ошибка Value:ValueError: invalid literal for int() with base 10: ''
Итак, как я могу справиться с этим, например, создать модульный тест, который проверяет, что я могу опубликовать форму без значений для каждого отдельного поля, установленных в null=True, blank=True
?
TypeError: Cannot encode None for key 'street_num' as POST data. Вы хотели передать пустую строку или опустить значение?
.
ValueError: invalid literal for int() with base 10: ''
content_type='multipart/form-data'
(по умолчанию) не поддерживает None
, поэтому исключите его.
comp_data = {k: v for k, v in self.company.__dict__.items() if v is not None}
content_type='application/json'
не поддерживает ImageField
без особых проблем.
Вам также потребуется обрабатывать request.body
в дополнение к request.POST
.
Ошибка в значении: Атрибут 'logo' не имеет связанного с ним файла.
Вам необходимо вручную исключить <ImageFieldFile: None>
.
if not self.company.logo:
del comp_data['logo']
Решение как часть кода:
comp_data = {k: v for k, v in self.company.__dict__.items() if v is not None} # Add this
if not self.company.logo: # Add this
del comp_data['logo'] # Add this
response = self.client.post(
reverse("polls:adm_options", args=[self.company.comp_slug]),
# self.company.__dict__, # Replace this
comp_data, # with this
)