如何在 Django 中对空白字段表单进行单元测试?

How to unit test a blank field form in Django?

我花了很多时间来设置一些单元测试,其中一个问题是我定义为可空和可空白的一些字段的设置。放置虚拟值不是问题,但我想知道:如何处理需要留空的字段,尤其是数字?
让我写一个我的代码摘录作为例子。

型号:

class Company(models.Model):
    company_name = models.CharField("nom", max_length=200)
    comp_slug = models.SlugField("slug")
    logo = models.ImageField(upload_to="img/", null=True, blank=True)
    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)

    @classmethod
    def get_company(cls, comp_slug):
        return cls.objects.filter(comp_slug=comp_slug).get()

形式:

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),
        logo=SimpleUploadedFile(name='logo.jpg', content=b'content', content_type='image/jpeg'),
        street_num=1,
        street_cplt='',
        address='dummy address'
    )


class TestOptions(TestCase):

    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)
        self.assertEqual(self.company.address, "new address")

关键部分是post更新后的表格。我遇到的不同案例如下:

  1. 上面写的测试没问题

  2. 如果我省略 street_numstreet_cplt,post 加注:

    TypeError: Cannot encode None as POST data. Did you mean to pass an empty string or omit the value?

    如果我省略 logo,它会引发:

    ValueError: The 'logo' attribute has no file associated with it.

    这是我的主要问题,因为在我看来,这些字段应该保持空白且没有错误。

  3. 那么,数字字段呢?如何将 street_num 设置为空白?如果我尝试 street_num='',它会引发:

    ValueError: invalid literal for int() with base 10: ''

那么我该如何处理,构建一个单元测试来检查我是否可以 post 一个没有每个字段值的表单设置为 null=True, blank=True?

TypeError: Cannot encode None for key 'street_num' as POST data. Did you mean to pass an empty string or omit the value?

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.POST.

之外,您还需要处理 request.body

ValueError: The 'logo' attribute has no file associated with it.

您需要手动排除 <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
)