测试驱动开发(Django)15

第15章 高级表单

15.1 针对重复待办事项的功能测试

functional_tests/test_list_item_validation.py增加一个测试方法


    def test_cannot_add_duplicate_items(self):
        #乔伊访问首页,新建一个清单
        self.browser.get(self.live_server_url)
        self.get_item_input_box().send_keys('Buy wellies')
        self.get_item_input_box().send_keys(Keys.ENTER)
        self.wait_for_row_in_list_table('1: Buy wellies')

        #她不小心输入一个重复的待办事项
        self.get_item_input_box().send_keys('Buy wellies')
        self.get_item_input_box().send_keys(Keys.ENTER)

        #她看到一条有帮助的错误消息
        self.wait_for(lambda :self.assertEqual(
            self.browser.find_element_by_css_selector('.has-error').text,
            "You've already got this in your list"
        ))

 

 python manage.py test functional_tests.test_list_item_validation

selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: .has-error

15.1.1 在模型层禁止重复

lists/tests/test_models.py

    def test_duplicate_items_are_invalid(self):
        list_ = List.objects.create()
        Item.objects.create(list=list_,text='bla')
        with self.assertRaises(ValidationError):
            item = Item(list=list_,text='bla')
            item.full_clean()
            
    def test_CAN_save_same_item_to_diffrernt_lists(self):
        list1 = List.objects.create()
        list2 = List.objects.create()
        Item.objects.create(list=list1,text='bla')
        item = Item(list=list2,text='bla')
        item.full_clean()

lists/models.py

class Item(models.Model):
    text = models.TextField(default='')
    list = models.ForeignKey(List, default=None)
    class Meta:
        unique_together = ('list', 'text')

单元测试通过。ps:如果失败的话,可能是唯一约束影响了查询排序,class Meta增加ordering = ('id',)

15.1.3重写旧模型测试

删除test_saving_and_retrieving_items,增加新方法,分为两个测试类

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author  : Marvin King
# Date     : 2019-02-22 
from django.test import TestCase
from lists.models import Item, List
from django.core.exceptions import ValidationError


class ItemModelTest(TestCase):

    def test_default_text(self):
        item = Item()
        self.assertEqual(item.text, '')

    def test_item_is_related_to_list(self):
        list_ = List.objects.create()
        item = Item()
        item.list = list_
        item.save()
        self.assertIn(item, list_.item_set.all())

    def test_cannot_save_empty_list_items(self):
        [...]

    def test_duplicate_items_are_invalid(self):
        [...]

    def test_CAN_save_same_item_to_diffrernt_lists(self):
       [...]

class ListModelTest(TestCase):

    def test_get_absolute_url(self):
        [...]

15.1.4 保存时确实会显示完整性错误

python manage.py makemigrations

Migrations for 'lists':

  lists/migrations/0004_auto_20190225_1537.py

    - Alter unique_together for item (1 constraint(s))

测试报错:IntegrityError

Ran 27 tests in 0.088s

OK

修改迁移文件名字:mv lists/migrations/0004_auto* lists/migrations/0004_list_item_unique_together.py

提交:git commit -am 'Implement duplicate item validation at model layer'

15.2 在视图层试验待办事项重复验证

 def test_duplicate_item_validation_errors_and_up_on_lists_page(self):
        list1 = List.objects.create()
        item1 = Item.objects.create(list=list1, text='textey')
        response = self.client.post(
            f'/lists/{list1.id}/',
            data={'text': 'textey'}
        )
        expected_error = escape("You've already got this in your list")
        self.assertContains(response, expected_error)
        self.assertTemplateUsed(response, 'list.html')
        self.assertEqual(Item.objects.all().count(), 1)

15.3 处理唯一性验证的复杂表单

lists/tests/test_forms.py

class ExistingListItemFormTest(TestCase):

    def test_form_renders_item_text_input(self):
        list_ = List.objects.create()
        form = ExistingListItemForm(for_list=list_)
        self.assertIn('placeholder="Enter a to-do item"', form.as_p())

    def test_form_validation_for_blank_items(self):
        list_ = List.objects.create()
        form = ExistingListItemForm(for_list=list_, data={'text': ''})
        self.assertFalse(form.is_valid())
        self.assertEqual(
            form.errors['text'],
            [EMPTY_ITEM_ERROR]
        )

    def test_form_validation_for_duplicate_items(self):
        list_ = List.objects.create()
        Item.objects.create(list=list_, text='no twins!')
        form = ExistingListItemForm(for_list=list_, data={'text': 'no twins!'})
        self.assertFalse(form.is_valid())
        self.assertEqual(form.errors['text'], [DUPLICATE_ITEM_ERROR])

lists/forms.py


class ExistingListItemForm(forms.models.ModelForm):
    def __init__(self, for_list, *args, **kwargs):
        super().__init__(*args, **kwargs)

 

ValueError: ModelForm has no model class specified.

修改继续:

class ExistingListItemForm(ItemForm):

AssertionError: True is not false

Django在表单和模型中都会调用validate_unique.

class ExistingListItemForm(ItemForm):
    def __init__(self, for_list, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.instance.list = for_list

    def validate_unique(self):
        try:
            self.instance.validate_unique()
        except ValidationError as e:
            e.error_dict = {'text':[DUPLICATE_ITEM_ERROR]}
            self._update_errors(e)

15.4 在清单视图中使用ExistingListItemForm

lists/tests/test_views.py清理常量测试

expected_error = escape(DUPLICATE_ITEM_ERROR)

修改

def test_displays_item_form(self):
    list_ = List.objects.create()
    response = self.client.get(f'/lists/{list_.id}/')
    self.assertIsInstance(response.context['form'], ExistingListItemForm)
    self.assertContains(response, 'name="text"')
def test_for_invalid_input_passes_form_to_template(self):
    response = self.post_invalid_input()
    self.assertIsInstance(response.context['form'], ExistingListItemForm)

单元测试:AssertionError: is not an instance of

修改视图:

def view_list(request, list_id):
    list_ = List.objects.get(id=list_id)
    form = ExistingListItemForm(for_list=list_)
    if request.method == 'POST':
        form = ExistingListItemForm(for_list=list_,data=request.POST)
        if form.is_valid():
            form.save()
            return redirect(list_)

TypeError: save() missing 1 required positional argument: 'for_list'

lists/tests/test_models.py增加一个测试

def test_form_save(self):
    list_ = List.objects.create()
    form = ExistingListItemForm(for_list=list_,data={'text':'hi'})
    new_item = form.save()
    self.assertEqual(new_item,Item.objects.all()[0])

lists/forms.py

def save(self):
    return forms.models.ModelForm.save(self)

python manage.py test lists

Ran 32 tests in 0.102s

OK

 python manage.py test functional_tests

Ran 5 tests in 60.305s

OK

提交:git commit -am 'Finish 15th'

你可能感兴趣的:(测试驱动开发)