Issue
I spent quite a lot of time to set some unit test, and one of the issues was the setting of some fields that I define to be nullable and blankable. Putting dummy values was not an issue, but I wonder: how to deal with fields that need to be blank, in particular for numbers?
Let me write as an example an extract of my code.
The model:
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()
The form:
class CompanyForm(forms.ModelForm):
company_name = forms.CharField(label="Société", disabled=True)
class Meta:
model = Company
exclude = []
The view:
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())
A simple unit test:
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")
The key part is posting the form after the update. The different cases I met were the following:
No problem with the test as written above.
If I omit
street_num
orstreet_cplt
, the post raises:TypeError: Cannot encode None as POST data. Did you mean to pass an empty string or omit the value?
If I omit
logo
, it raises:ValueError: The 'logo' attribute has no file associated with it.
This is my main issue as far as, in my opinion, the fields should stay blank with no error.
Then, what about number fields? How can I set
street_num
to blank? If I trystreet_num=''
, it raises:ValueError: invalid literal for int() with base 10: ''
So how could I deal with that, to build a unit test that checks that I can post a form with no values for each single field set to null=True, blank=True
?
Solution
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'
(the default) does not support None
, so exclude it.
comp_data = {k: v for k, v in self.company.__dict__.items() if v is not None}
content_type='application/json'
does not support ImageField
without much hassle.
You would also need to handle request.body
in addition to request.POST
.
ValueError: The 'logo' attribute has no file associated with it.
You need to manually exclude <ImageFieldFile: None>
.
if not self.company.logo:
del comp_data['logo']
The solution as part of the code:
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
)
Answered By - aaron
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.