Django 更新表单的单元测试
Unit test for Django Update form
我不明白如何管理表单和相关单元测试的更新,非常感谢一些建议 =)
我有一个Company模型,相关的CompanyForm很简单:
class Company(models.Model):
"""
Company informations
- Detailed information for display purposes in the application
but also used in documents built and sent by the application
- Mail information to be able to send emails
"""
company_name = models.CharField("nom", max_length=200)
comp_slug = models.SlugField("slug")
logo = models.ImageField(upload_to="img/", null=True, blank=True)
use_groups = models.BooleanField("utilise les groupes", default=False) # Company uses groups or not
rules = [("MAJ", "Majorité"), ("PROP", "Proportionnelle")] # Default management rule
rule = models.CharField(
"mode de scrutin", max_length=5, choices=rules, default="MAJ"
)
upd_rule = models.BooleanField("choisir la règle de répartition pour chaque événement", default=False) # Event rule might change from one to another or always use default
statut = models.CharField("forme juridique", max_length=50)
siret = models.CharField("SIRET", max_length=50)
street_num = models.IntegerField("N° de rue", null=True, blank=True)
street_cplt = models.CharField("complément", max_length=50, null=True, blank=True)
address1 = models.CharField("adresse", max_length=300)
address2 = models.CharField(
"complément d'adresse", max_length=300, null=True, blank=True
)
zip_code = models.IntegerField("code postal")
city = models.CharField("ville", max_length=200)
host = models.CharField("serveur mail", max_length=50, null=True, blank=True)
port = models.IntegerField("port du serveur", null=True, blank=True)
hname = models.EmailField("utilisateur", max_length=100, null=True, blank=True)
fax = models.CharField("mot de passe", max_length=50, null=True, blank=True)
use_tls = models.BooleanField("authentification requise", default=True, blank=True)
class Meta:
verbose_name = "Société"
constraints = [
models.UniqueConstraint(fields=["comp_slug"], name="unique_comp_slug")
]
def __str__(self):
return self.company_name
@classmethod
def get_company(cls, slug):
""" Retreive company from its slug """
return cls.objects.get(comp_slug=slug)
class CompanyForm(forms.ModelForm):
company_name = forms.CharField(label="Société", disabled=True)
class Meta:
model = Company
exclude = []
视图也很简单:
@user_passes_test(lambda u: u.is_superuser or u.usercomp.is_admin)
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())
这个视图工作正常,我可以更新信息(它实际上不用于创建,这要归功于 Django 管理面板)。
不幸的是,我无法构建单元测试来确保更新有效!
我尝试了 2 种方法,但其中 none 有效。
我的第一次尝试如下:
class TestOptions(TestCase):
def setUp(self):
self.company = create_dummy_company("Société de test")
self.user_staff = create_dummy_user(self.company, "staff", admin=True)
self.client.force_login(self.user_staff.user)
def test_adm_options_update(self):
# Load company options page
url = reverse("polls:adm_options", args=[self.company.comp_slug])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "0123456789")
self.assertEqual(self.company.siret, "0123456789")
# Options update
response = self.client.post(
reverse("polls:adm_options", args=[self.company.comp_slug]),
{"siret": "987654321"}
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "987654321")
self.assertNotContains(response, "0123456789")
self.assertEqual(self.company.siret, "987654321")
在这种情况下,除了最新的断言外,一切正常。看起来更新没有保存,其实不然。我之前尝试读取数据库,密钥存储在上下文中,但它保持不变。
当我找到 时,我正在寻找其他信息,所以我尝试了另一种方法来测试,即使这种方法让我有点惊讶(我没有看到视图是如何实际测试的)。
这是我的第二次尝试(setUp()
保持不变):
def test_adm_options_update(self):
# Load company options page
url = reverse("polls:adm_options", args=[self.company.comp_slug])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "0123456789") # this is the default value in tests for this field
self.assertEqual(self.company.siret, "0123456789")
# Options update
self.company.siret = "987654321"
comp_form = CompanyForm(instance=self.company)
self.assertTrue(comp_form.is_valid())
comp_form.save()
company = Company.get_company(self.company.comp_slug)
self.assertEqual(company.siret, "987654321")
在这种情况下,表单只是空的!
我可以认为我的视图有效并继续,我的问题是我在另一个视图中有一个错误,我想确保我可以构建测试以找出错误!
非常感谢您的回答!
编辑 - 8 月 30 日
按照建议,我尝试使用 self.company.refresh_from_db()
但它并没有改变结果。
尝试传递 self.client.post()
中的所有字段,但一旦字段为空就会失败('Cannot encode None as POST data' 错误消息)
似乎我创建了一个 'dummy' 公司进行测试,必填字段为空……而且它仍然有效。测试环境的问题?我改变了这一点,但我想知道问题是否出在其他地方...
编辑 - 9 月 15 日
正在寻找可以为我提供新想法的人 =)
为了确保我理解最新的命题,这里是测试的完整代码:
def test_adm_options_update(self):
# Load company options page
url = reverse("polls:adm_options", args=[self.company.comp_slug])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "0123456789")
self.assertEqual(self.company.siret, "0123456789")
# Apply changes
company_data = copy.deepcopy(CompanyForm(instance=self.company).initial)
company_data['siret'] = "987654321"
response = self.client.post(
reverse("polls:adm_options", args=[self.company.comp_slug]),
company_data,
)
self.company.refresh_from_db()
self.assertEqual(response.status_code, 200)
self.assertContains(response, "987654321")
self.assertNotContains(response, "0123456789")
self.assertEqual(self.company.siret, "987654321")
这是为测试创建 'dummy' 公会的函数:
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'),
statut="SARL",
siret="0123456789",
address1="Rue des fauvettes",
zip_code="99456",
city='Somewhere',
host="smtp.gmail.com",
port=587,
hname="test@polls.com",
fax="toto",
)
在这种情况下,您需要使用 refresh_from_db
在视图和表单更新完您的对象后“刷新”您的对象。这意味着当您当前断言时,您正在使用 self.company
的“旧快照”因此断言失败,因此您需要更新它:
# Options update
response = self.client.post(
reverse("polls:adm_options", args=[self.company.comp_slug]),
{"siret": "987654321"}
)
...
self.company.refresh_from_db()
self.assertEqual(self.company.siret, "987654321")
编辑:
想出了一个方法来完成这项工作。由于该表单要求您输入所有数据,您只需将公司实例传递给相同的表单,并访问 initial (它将作为您的请求数据)。
然后您可以根据需要修改它,在本例中为 siret
和 logo
:
from django.core.files.uploadedfile import SimpleUploadedFile
def test(self):
company_data = CompanyForm(instance=self.company).initial
company_data['logo'] = SimpleUploadedFile(name='somefile', content=b'content', content_type='image/jpeg')
company_data['siret'] = "987654321"
response = self.client.post(
reverse("polls:adm_options", args=[self.company.comp_slug]),
company_data,
)
self.company.refresh_from_db()
self.assertEqual(self.company.siret, "987654321")
这在我这边有效并通过了与您拥有的完全相同的模型。
我不明白如何管理表单和相关单元测试的更新,非常感谢一些建议 =)
我有一个Company模型,相关的CompanyForm很简单:
class Company(models.Model):
"""
Company informations
- Detailed information for display purposes in the application
but also used in documents built and sent by the application
- Mail information to be able to send emails
"""
company_name = models.CharField("nom", max_length=200)
comp_slug = models.SlugField("slug")
logo = models.ImageField(upload_to="img/", null=True, blank=True)
use_groups = models.BooleanField("utilise les groupes", default=False) # Company uses groups or not
rules = [("MAJ", "Majorité"), ("PROP", "Proportionnelle")] # Default management rule
rule = models.CharField(
"mode de scrutin", max_length=5, choices=rules, default="MAJ"
)
upd_rule = models.BooleanField("choisir la règle de répartition pour chaque événement", default=False) # Event rule might change from one to another or always use default
statut = models.CharField("forme juridique", max_length=50)
siret = models.CharField("SIRET", max_length=50)
street_num = models.IntegerField("N° de rue", null=True, blank=True)
street_cplt = models.CharField("complément", max_length=50, null=True, blank=True)
address1 = models.CharField("adresse", max_length=300)
address2 = models.CharField(
"complément d'adresse", max_length=300, null=True, blank=True
)
zip_code = models.IntegerField("code postal")
city = models.CharField("ville", max_length=200)
host = models.CharField("serveur mail", max_length=50, null=True, blank=True)
port = models.IntegerField("port du serveur", null=True, blank=True)
hname = models.EmailField("utilisateur", max_length=100, null=True, blank=True)
fax = models.CharField("mot de passe", max_length=50, null=True, blank=True)
use_tls = models.BooleanField("authentification requise", default=True, blank=True)
class Meta:
verbose_name = "Société"
constraints = [
models.UniqueConstraint(fields=["comp_slug"], name="unique_comp_slug")
]
def __str__(self):
return self.company_name
@classmethod
def get_company(cls, slug):
""" Retreive company from its slug """
return cls.objects.get(comp_slug=slug)
class CompanyForm(forms.ModelForm):
company_name = forms.CharField(label="Société", disabled=True)
class Meta:
model = Company
exclude = []
视图也很简单:
@user_passes_test(lambda u: u.is_superuser or u.usercomp.is_admin)
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())
这个视图工作正常,我可以更新信息(它实际上不用于创建,这要归功于 Django 管理面板)。
不幸的是,我无法构建单元测试来确保更新有效!
我尝试了 2 种方法,但其中 none 有效。
我的第一次尝试如下:
class TestOptions(TestCase):
def setUp(self):
self.company = create_dummy_company("Société de test")
self.user_staff = create_dummy_user(self.company, "staff", admin=True)
self.client.force_login(self.user_staff.user)
def test_adm_options_update(self):
# Load company options page
url = reverse("polls:adm_options", args=[self.company.comp_slug])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "0123456789")
self.assertEqual(self.company.siret, "0123456789")
# Options update
response = self.client.post(
reverse("polls:adm_options", args=[self.company.comp_slug]),
{"siret": "987654321"}
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "987654321")
self.assertNotContains(response, "0123456789")
self.assertEqual(self.company.siret, "987654321")
在这种情况下,除了最新的断言外,一切正常。看起来更新没有保存,其实不然。我之前尝试读取数据库,密钥存储在上下文中,但它保持不变。
当我找到
这是我的第二次尝试(setUp()
保持不变):
def test_adm_options_update(self):
# Load company options page
url = reverse("polls:adm_options", args=[self.company.comp_slug])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "0123456789") # this is the default value in tests for this field
self.assertEqual(self.company.siret, "0123456789")
# Options update
self.company.siret = "987654321"
comp_form = CompanyForm(instance=self.company)
self.assertTrue(comp_form.is_valid())
comp_form.save()
company = Company.get_company(self.company.comp_slug)
self.assertEqual(company.siret, "987654321")
在这种情况下,表单只是空的!
我可以认为我的视图有效并继续,我的问题是我在另一个视图中有一个错误,我想确保我可以构建测试以找出错误!
非常感谢您的回答!
编辑 - 8 月 30 日
按照建议,我尝试使用 self.company.refresh_from_db()
但它并没有改变结果。
尝试传递 self.client.post()
中的所有字段,但一旦字段为空就会失败('Cannot encode None as POST data' 错误消息)
似乎我创建了一个 'dummy' 公司进行测试,必填字段为空……而且它仍然有效。测试环境的问题?我改变了这一点,但我想知道问题是否出在其他地方...
编辑 - 9 月 15 日
正在寻找可以为我提供新想法的人 =)
为了确保我理解最新的命题,这里是测试的完整代码:
def test_adm_options_update(self):
# Load company options page
url = reverse("polls:adm_options", args=[self.company.comp_slug])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "0123456789")
self.assertEqual(self.company.siret, "0123456789")
# Apply changes
company_data = copy.deepcopy(CompanyForm(instance=self.company).initial)
company_data['siret'] = "987654321"
response = self.client.post(
reverse("polls:adm_options", args=[self.company.comp_slug]),
company_data,
)
self.company.refresh_from_db()
self.assertEqual(response.status_code, 200)
self.assertContains(response, "987654321")
self.assertNotContains(response, "0123456789")
self.assertEqual(self.company.siret, "987654321")
这是为测试创建 'dummy' 公会的函数:
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'),
statut="SARL",
siret="0123456789",
address1="Rue des fauvettes",
zip_code="99456",
city='Somewhere',
host="smtp.gmail.com",
port=587,
hname="test@polls.com",
fax="toto",
)
在这种情况下,您需要使用 refresh_from_db
在视图和表单更新完您的对象后“刷新”您的对象。这意味着当您当前断言时,您正在使用 self.company
的“旧快照”因此断言失败,因此您需要更新它:
# Options update
response = self.client.post(
reverse("polls:adm_options", args=[self.company.comp_slug]),
{"siret": "987654321"}
)
...
self.company.refresh_from_db()
self.assertEqual(self.company.siret, "987654321")
编辑:
想出了一个方法来完成这项工作。由于该表单要求您输入所有数据,您只需将公司实例传递给相同的表单,并访问 initial (它将作为您的请求数据)。
然后您可以根据需要修改它,在本例中为 siret
和 logo
:
from django.core.files.uploadedfile import SimpleUploadedFile
def test(self):
company_data = CompanyForm(instance=self.company).initial
company_data['logo'] = SimpleUploadedFile(name='somefile', content=b'content', content_type='image/jpeg')
company_data['siret'] = "987654321"
response = self.client.post(
reverse("polls:adm_options", args=[self.company.comp_slug]),
company_data,
)
self.company.refresh_from_db()
self.assertEqual(self.company.siret, "987654321")
这在我这边有效并通过了与您拥有的完全相同的模型。