本节可以跳过 因为是测试方面的学习 其实也不用太深入
真正想弄 把selenium玩一下就好 这个框架更加直观 更贴近客户端
经过前面的学习 我们已经把网站主体整完了 剩下的无非就是测试以及少许美化的问题
虽然我们只是个很小的应用 但是页面也已经超过50张以上(根据你录入书的数量)
那么手动的来debug 恐怕低效 无趣 而且容易漏掉
这时 自动化测试派上用场
本篇内容将会帮助你学习…
基本的几种测试方式是:
另外 对于测试的框架而言 Django 默认使用unitest
当然你可以和别的现有测试框架集成在一起玩 比如经典的selenium
对这个感兴趣的可以看我这篇文章简单入门:
python 应用(六)—— 爬虫(一)selenium 概述
谷歌还有别的测试框架 是基于js的 这超出目前讨论范围
当然期待我们学习NodeJS的时候 就可以玩玩puppeteer了
我们当然不用测试Django已经给我们封装好的应用啦
关键是我们写的代码
另外不可以想当然 比如 我在model规定 last_name max_length = 100
你可能觉得 这不是很清楚吗?为啥要测试?
总之 编写测试规则尽量 详尽
首先说明 这里我们是运用Django内置的unitest库进行测试的
unitest提供了很多测试的套路 封装成类
如 SimpleTestCase, TransactionTestCase, TestCase, LiveServerTestCase
我们实际测试我们具体的model等 就可以继承之 然后运用
catalog/
/tests/
__init__.py
test_models.py
test_forms.py
test_views.py
我们是另外创建tests文件来放测试规则文件的
test就是“测试工具包” 你看到__init__
就知道了
注意 工具包内的文件 命名有规范的 不能随便写 按这上面来就行
setUpTestData()
setUp()
python manage.py test
python3 manage.py test catalog.tests.test_models
>python3 manage.py test
Creating test database for alias 'default'...
setUpTestData: Run once to set up non-modified data for all class methods.
setUp: Run once for every test method to setup clean data.
Method: test_false_is_false.
.setUp: Run once for every test method to setup clean data.
Method: test_false_is_true.
FsetUp: Run once for every test method to setup clean data.
Method: test_one_plus_one_equals_two.
.
======================================================================
FAIL: test_false_is_true (catalog.tests.tests_models.YourTestClass)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\Github\django_tmp\library_w_t_2\locallibrary\catalog\tests\tests_models.py", line 22, in test_false_is_true
self.assertTrue(False)
AssertionError: False is not true
----------------------------------------------------------------------
Ran 3 tests in 0.075s
FAILED (failures=1)
Destroying test database for alias 'default'...
就两句话重点:
不期待你能完全理解测试过程 但得有个大致印象
下面我们来实战:
class Author(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('Died', null=True, blank=True)
def get_absolute_url(self):
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
return '%s, %s' % (self.last_name, self.first_name)
看看原来模型有什么限制(约束restrain)
然后 我们编写test_model 弄一个 AuthorModelTest 采用testCase:
from django.test import TestCase
from catalog.models import Author
class AuthorModelTest(TestCase):
@classmethod
def setUpTestData(cls):
#Set up non-modified objects used by all test methods
Author.objects.create(first_name='Big', last_name='Bob')
def test_first_name_label(self):
author=Author.objects.get(id=1)
field_label = author._meta.get_field('first_name').verbose_name
self.assertEquals(field_label,'first name')
def test_date_of_death_label(self):
author=Author.objects.get(id=1)
field_label = author._meta.get_field('date_of_death').verbose_name
self.assertEquals(field_label,'died')
def test_first_name_max_length(self):
author=Author.objects.get(id=1)
max_length = author._meta.get_field('first_name').max_length
self.assertEquals(max_length,100)
def test_object_name_is_last_name_comma_first_name(self):
author=Author.objects.get(id=1)
expected_object_name = '%s, %s' % (author.last_name, author.first_name)
self.assertEquals(expected_object_name,str(author))
def test_get_absolute_url(self):
author=Author.objects.get(id=1)
#This will also fail if the urlconf is not defined.
self.assertEquals(author.get_absolute_url(),'/catalog/author/1')
这里注意几点:
test_first_name_label(self):
也就是第二个函数末尾 我们其实有两种写法:其实与测试模型差不多
我们图书馆应用主要还是续借日期这块需要测试 原来forms.py 代码:
class RenewBookForm(forms.Form):
"""
Form for a librarian to renew books.
"""
renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
def clean_renewal_date(self):
data = self.cleaned_data['renewal_date']
#Check date is not in past.
if data < datetime.date.today():
raise ValidationError(_('Invalid date - renewal in past'))
#Check date is in range librarian allowed to change (+4 weeks)
if data > datetime.date.today() + datetime.timedelta(weeks=4):
raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))
# Remember to always return the cleaned data.
return data
针对于此 我们设计了下面的测试代码
包含:
/catalog/tests/test_forms.py
from django.test import TestCase
# Create your tests here.
import datetime
from django.utils import timezone
from catalog.forms import RenewBookForm
class RenewBookFormTest(TestCase):
def test_renew_form_date_field_label(self):
form = RenewBookForm()
self.assertTrue(form.fields['renewal_date'].label == None or form.fields['renewal_date'].label == 'renewal date')
def test_renew_form_date_field_help_text(self):
form = RenewBookForm()
self.assertEqual(form.fields['renewal_date'].help_text,'Enter a date between now and 4 weeks (default 3).')
def test_renew_form_date_in_past(self):
date = datetime.date.today() - datetime.timedelta(days=1)
form_data = {'renewal_date': date}
form = RenewBookForm(data=form_data)
self.assertFalse(form.is_valid())
def test_renew_form_date_too_far_in_future(self):
date = datetime.date.today() + datetime.timedelta(weeks=4) + datetime.timedelta(days=1)
form_data = {'renewal_date': date}
form = RenewBookForm(data=form_data)
self.assertFalse(form.is_valid())
def test_renew_form_date_today(self):
date = datetime.date.today()
form_data = {'renewal_date': date}
form = RenewBookForm(data=form_data)
self.assertTrue(form.is_valid())
def test_renew_form_date_max(self):
date = timezone.now() + datetime.timedelta(weeks=4)
form_data = {'renewal_date': date}
form = RenewBookForm(data=form_data)
self.assertTrue(form.is_valid())
关注这里:我们传数据采用键值对的形式 而且这个“renewal_date” 是的确是来源于类属性的
然后我们调用类实例的is_valid()
来断言测试
date = datetime.date.today() - datetime.timedelta(days=1)
form_data = {'renewal_date': date}
form = RenewBookForm(data=form_data)
self.assertFalse(form.is_valid())
这里我们拿author验证为例
实际上我们大多数工作给django做了 一般不需要验证太多
这里只是学习之用 举个栗子
/catalog/tests/test_views.py
from django.test import TestCase
# Create your tests here.
from catalog.models import Author
from django.urls import reverse
class AuthorListViewTest(TestCase):
@classmethod
def setUpTestData(cls):
#Create 13 authors for pagination tests
number_of_authors = 13
for author_num in range(number_of_authors):
Author.objects.create(first_name='Christian %s' % author_num, last_name = 'Surname %s' % author_num,)
def test_view_url_exists_at_desired_location(self):
resp = self.client.get('/catalog/authors/')
self.assertEqual(resp.status_code, 200)
def test_view_url_accessible_by_name(self):
resp = self.client.get(reverse('authors'))
self.assertEqual(resp.status_code, 200)
def test_view_uses_correct_template(self):
resp = self.client.get(reverse('authors'))
self.assertEqual(resp.status_code, 200)
self.assertTemplateUsed(resp, 'catalog/author_list.html')
def test_pagination_is_ten(self):
resp = self.client.get(reverse('authors'))
self.assertEqual(resp.status_code, 200)
self.assertTrue('is_paginated' in resp.context)
self.assertTrue(resp.context['is_paginated'] == True)
self.assertTrue( len(resp.context['author_list']) == 10)
def test_lists_all_authors(self):
#Get second page and confirm it has (exactly) remaining 3 items
resp = self.client.get(reverse('authors')+'?page=2')
self.assertEqual(resp.status_code, 200)
self.assertTrue('is_paginated' in resp.context)
self.assertTrue(resp.context['is_paginated'] == True)
self.assertTrue( len(resp.context['author_list']) == 3)
这里 我们先验证他是否分页 (paginate)于是创造13个数据
number_of_authors = 13
for author_num in range(number_of_authors):
Author.objects.create(first_name='Christian %s' % author_num, last_name = 'Surname %s' % author_num,)
然后后面再断言
self.assertTrue('is_paginated' in resp.context)
self.assertTrue(resp.context['is_paginated'] == True)
self.assertTrue( len(resp.context['author_list']) == 10)
另外很重要的是测试url映射与reverse反向映射 我们用client.get来实现:)
def test_view_url_exists_at_desired_location(self):
resp = self.client.get('/catalog/authors/')
self.assertEqual(resp.status_code, 200)
def test_view_url_accessible_by_name(self):
resp = self.client.get(reverse('authors'))
self.assertEqual(resp.status_code, 200)
自动化测试又是一个大学问 个人是先不着急学这一块 最多用selenium来简单测试一波