后端举例,接口测试在实际应用中编写最容易,编写速度最快的就是整合测试。
1)整合测试测试接口逻辑与数据库ORM等多个功能,可以用到web开发框架(如Django、Flask等)自带的测试客户端和测试类(如Django的TestCase)。
2)不管是接口测试用例还是单元测试用例,其主要测试方向都是集中测试指定的接口或函数功能上”做什么”。如,对于REST型接口而言,主要测试的方面包括:传入有效和无效参数、接口返回的预期的状态码、返回与前端约定的数据格式,数据库数据异常情况下的接口数据处理等。
代码举例如下
import json from django.test import TestCase from lists.models import List,
Itemfrom lists.forms import DUPLICATE_ITEM_ERROR, EMPTY_ITEM_ERROR class
ListAPITest(TestCase): base_url = "/api/lists/{}/" def test_get_returns_json_200
(self): ''' 测试get接口返回了200的json ''' list_ = List.objects.create() response =
self.client.get(self.base_url.format(list_.id))
self.assertEqual(response.status_code,200) self.assertEqual(response[
"content-type"], "application/json") def test_get_returns_items_for_correct_list
(self): ''' 测试api返回指定清单的所有待办事项 ''' other_list = List.objects.create()
Item.objects.create(list=other_list, text="待办事项1") our_list =
List.objects.create() item1 = Item.objects.create(list=our_list, text="待办事项1")
item2 = Item.objects.create(list=our_list, text="待办事项2") response =
self.client.get(self.base_url.format(our_list.id)) self.assertEqual(
json.loads(response.content.decode("utf8")), [ {"id": item1.id, "text":
item1.text}, {"id": item2.id, "text": item2.text} ] ) def
test_POSTing_a_new_item(self): ''' 测试通过post请求创建一个新的待办事项 ''' list_ =
List.objects.create() response = self.client.post(
self.base_url.format(list_.id), {"text": "新的待办事项"}, )
self.assertEqual(response.status_code,201) new_item = list_.item_set.get()
self.assertEqual(new_item.text,"新的待办事项") def post_empty_input(self): '''
post方法发送空数据 ''' list_ = List.objects.create() return self.client.post(
self.base_url.format(list_.id), data={"text": ""} ) def
test_for_invalid_input_nothing_saved_to_db(self): ''' 测试无效输入不会被保存 '''
self.post_empty_input() self.assertEqual(Item.objects.count(),0) def
test_for_invalid_input_return_error_code(self): ''' 测试无效输入返回错误状态码 ''' response
= self.post_empty_input() self.assertEqual(response.status_code,400)
self.assertEqual( json.loads(response.content.decode("utf8")), {"error":
EMPTY_ITEM_ERROR} )def test_duplicate_items_error(self): ''' 测试提交重复事项会返回错误码 '''
list_ = List.objects.create() self.client.post( self.base_url.format(list_.id),
data={"text": "thing"} ) response = self.client.post(
self.base_url.format(list_.id), data={"text": "thing"} )
self.assertEqual(response.status_code,400) self.assertEqual(
json.loads(response.content.decode("utf8")), {"error": DUPLICATE_ITEM_ERROR} )
后端单元测试,纯粹的单元测试与整合测试不同,只测函数本身的功能。在实际应用中更多的出现在视图层。需要用到模拟技术库(如Python的unittest.mock模块),用模拟技术库模拟所有依赖。模拟技术一般分为三种,如spy(侦件)、fake(伪件)、stub(桩件)。单元测试一般难以编写,需要对框架足够了解,持续的编写练习,并进入”神赐的心流状态”才可能写出不错的单元测试。而且耗费时间较长,但采用TDD驱动开发的情况下,编写单元测试往往可以写出简洁优雅的代码、导向系统的更优设计、易于BUG定位、且有单元测试保证的情况下,方便代码重构。
单元测试的主要方面包括:针对ORM的数据模型层测试,针对视图层的函数行为测试。
数据模型层测试主要测试的是:对于数据库操作的辅助函数,功能上是否满足应用的功能需求。比如登录功能,ORM层就应该可以校验空数据(当然,前端也可以校验)、重复数据、删改查、以及关联关系,唯一性约束等。
举例代码如下:
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): ''' 测试数据库约束不能保存空的待办事项 ''' list_ = List.objects.create() item =
Item(list=list_, text="") with self.assertRaises(ValidationError): item.save()
item.full_clean()def test_string_representation(self): ''' 测试待办事项的字符串呈现 '''
item = Item(text="some text") self.assertEqual(str(item), "some text")
视图层测试主要测试的是:页面重定向,页面渲染、数据完整性及合法性校验、请求参数验证、错误提示返回、cookie创建等。
举例代码如下:
@patch("lists.views.NewListForm") class NewListViewUnitTest(unittest.TestCase):
''' 新的列表视图测试类 ''' def setUp(self): self.request = HttpRequest()
self.request.POST["text"] = "新的待办事项" self.request.user = Mock() def
test_passes_POST_data_to_NewListForm(self, mockNewListForm): '''
测试列表视图传递POST数据到新列表表单 ''' new_list(self.request)
mockNewListForm.assert_called_once_with(data=self.request.POST)def
test_saves_form_with_owner_if_form_valid(self, mockNewListForm): '''
测试表单有效时对所有者的保存功能 ''' mock_form = mockNewListForm.return_value
mock_form.is_valid.return_value =True new_list(self.request)
mock_form.save.assert_called_once_with(owner=self.request.user)
@patch("lists.views.redirect") def
test_redirects_to_form_returned_object_if_form_valid( self, mock_redirect,
mockNewListForm ): ''' 测试表单有效时视图会重定向到一个显示刚刚提交的表单的页面 ''' mock_form =
mockNewListForm.return_value mock_form.is_valid.return_value =True response =
new_list(self.request) self.assertEqual(response, mock_redirect.return_value)
mock_redirect.assert_called_once_with(
str(mock_form.save().get_absolute_url.return_value))@patch("lists.views.render")
def test_renders_home_template_with_form_if_form_invalid( self, mock_render,
mockNewListForm ): ''' 测试如果表单无效,那么返回主页模版 ''' mock_form =
mockNewListForm.return_value mock_form.is_valid.return_value =False response =
new_list(self.request) self.assertEqual(response, mock_render.return_value)
mock_render.assert_called_once_with( self.request,"home.html", {"form":
mock_form} )def test_does_not_save_if_form_invalid(self, mockNewListForm): '''
测试form内容无效时不应该保存 ''' mock_form = mockNewListForm.return_value
mock_form.is_valid.return_value =False new_list(self.request)
self.assertFalse(mock_form.save.called)
热门工具 换一换