5.2单元测试
为什么要测试?
Web程序开发过程一般包括以下几个阶段:[需求分析,设计阶段,实现阶段,测试阶段]。其中测试阶段通过人工或自动来运行测试某个系统的功能。目的是检验其是否满足需求,并得出特定的结果,以达到弄清楚预期结果和实际结果之间的差别的最终目的。
测试的分类:
测试从软件开发过程可以分为:单元测试、集成测试、系统测试等。在众多的测试中,与程序开发人员最密切的就是单元测试,因为单元测试是由开发人员进行的,而其他测试都由专业的测试人员来完成。所以我们主要学习单元测试。
什么是单元测试?
程序开发过程中,写代码是为了实现需求。当我们的代码通过了编译,只是说明它的语法正确,功能能否实现则不能保证。 因此,当我们的某些功能代码完成后,为了检验其是否满足程序的需求。可以通过编写测试代码,模拟程序运行的过程,检验功能代码是否符合预期。
单元测试就是开发者编写一小段代码,检验目标代码的功能是否符合预期。通常情况下,单元测试主要面向一些功能单一的模块进行。
举个例子:一部手机有许多零部件组成,在正式组装一部手机前,手机内部的各个零部件,CPU、内存、电池、摄像头等,都要进行测试,这就是单元测试。
在Web开发过程中,单元测试实际上就是一些“断言”(assert)代码。
断言就是判断一个函数或对象的一个方法所产生的结果是否符合你期望的那个结果。 python中assert断言是声明布尔值为真的判定,如果表达式为假会发生异常。单元测试中,一般使用assert来断言结果。
断言方法的使用:
断言语句类似于:
if not expression: raise AssertionError
常用的断言方法:
assertEqual 如果两个值相等,则pass assertNotEqual 如果两个值不相等,则pass assertTrue 判断bool值为True,则pass assertFalse 判断bool值为False,则pass assertIsNone 不存在,则pass assertIsNotNone 存在,则pass
单元测试的基本写法:
首先,定义一个类,继承自unittest.TestCase
import unittest class TestClass(unitest.TestCase): pass
其次,在测试类中,定义两个测试方法
import unittest class TestClass(unittest.TestCase): #该方法会首先执行,方法名为固定写法 def setUp(self): pass #该方法会在测试代码执行完后执行,方法名为固定写法 def tearDown(self): pass
最后,在测试类中,编写测试代码
import unittest class TestClass(unittest.TestCase): #该方法会首先执行,相当于做测试前的准备工作 def setUp(self): pass #该方法会在测试代码执行完后执行,相当于做测试后的扫尾工作 def tearDown(self): pass #测试代码 def test_app_exists(self): pass
简单测试案例:
def divide(num1, num2): """除法""" # assert断言 后面是一个表达式,如果表示返回真,则断言成功,程序能够继续往下执行 # 如果表达式返回追假,则断言失败,assert会抛出异常AssertionError,终止程序继续往下执行 assert isinstance(num1, int) assert isinstance(num2, int) assert num2 != 0 print num1 / num2 if __name__ == '__main__': divide(100, 50) divide("a", 50)
# coding:utf-8 import unittest from login import app import json class TestLogin(unittest.TestCase): """定义测试案例""" def setUp(self): """在执行具体的测试方法前,先被调用""" # 可以使用python的http标准客户端进行测试 # urllib urllib2 requests # 使用flask提供的测试客户端进行测试 self.client = app.test_client() def test_empty_name_password(self): """测试模拟场景,用户名或密码不完整""" # 使用客户端向后端发送post请求, data指明发送的数据,会返回一个响应对象 response = self.client.post("/login", data={}) # respoonse.data是响应体数据 resp_json = response.data # 按照json解析 resp_dict = json.loads(resp_json) # 使用断言进行验证 self.assertIn("code", resp_dict) code = resp_dict.get("code") self.assertEqual(code, 1) # 测试只传name response = self.client.post("/login", data={"name": "admin"}) # respoonse.data是响应体数据 resp_json = response.data # 按照json解析 resp_dict = json.loads(resp_json) # 使用断言进行验证 self.assertIn("code", resp_dict) code = resp_dict.get("code") self.assertEqual(code, 1) def test_wrong_name_password(self): """测试用户名或密码错误""" # 使用客户端向后端发送post请求, data指明发送的数据,会返回一个响应对象 response = self.client.post("/login", data={"name": "admin", "password": "itcast"}) # respoonse.data是响应体数据 resp_json = response.data # 按照json解析 resp_dict = json.loads(resp_json) # 使用断言进行验证 self.assertIn("code", resp_dict) code = resp_dict.get("code") self.assertEqual(code, 2) if __name__ == '__main__': unittest.main()
# coding:utf-8 import unittest from author_book import Author, db, app class TestDatabase(unittest.TestCase): """测试数据库的案例""" def setUp(self): app.config["SQLALCHEMY_DATABASE_URI"] = "mysql://root:mysql@127.0.0.1:3306/flask_test" db.drop_all() db.create_all() def test_author(self): """测试添加作者的案例""" author = Author(name="itcast", email="itcast@itcast.cn") db.session.add(author) db.session.commit() ret_author = Author.query.filter_by(name="itcast").first() self.assertIsNotNone(ret_author) self.assertEqual(ret_author.name, "itcast") def tearDown(self): """在所有测试方法执行后,被调用""" # 清除记录的测试任务 db.session.remove() # 清除数据库数据 db.drop_all() if __name__ == '__main__': unittest.main()