前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python 编程必不可少的测试框架「unittest 篇」

Python 编程必不可少的测试框架「unittest 篇」

作者头像
keinYe
修改2020-01-03 14:52:05
6330
修改2020-01-03 14:52:05
举报
文章被收录于专栏:keinYekeinYe

没有经过测试的代码是不可靠的代码。

unittest 是一个单元测试框架,单元测试完成对一个模块、一个类或一个函数的运行结果进行检验的测试工作。单元测试是对一个程序最基础的组成部分进行正确性验证,只有所有的单元测试不存在问题才能保证整体程序的正确性。

一个优秀的程序不仅能够写出优秀的功能代码,也要能够写一手优秀的测试代码。

测试驱动开发:要求在开始编写功能代码之前,先编写测试代码,然后编写能够通过测试的代码,通过测试来推动整个开发过程进行。

通过编写测试代码,不仅使当前的代码设计能够满足预期的功能需要,在将来对代码进行修改时,也能够保证修改后代码的输出结果是正确的。

unittest 是 python 自带的单元测试框架,test fixture「测试框架」、test case「测试用例」、test suite「测试集合」、test runner「测试运行器」是 unittest 的四个核心概念。

  • test fixture:测试框架,在测试开始前进行一些必要的准备工作,或在测试结束时进行相关的清理工作。
  • test case:测试用例,作为一个单独的测试单元,提供实际的测试逻辑,检测特定的输入所对应的输出结果是否与预期一致。
  • test suite:测试集合,将多个测试用例组合起来,统一进行测试。既可以由多个测试用例组成,也可以由多个测试集合组成,还可以是测试用例和测试集合共同组成。
  • test runner:测试运行器,用于执行测试和输出测试结果。

编写测试代码时,我们需要编写一个继承自 unittest.TestCase 的测试类,在该类中以 test 开头的方法就是测试方便,在测试过程中会被执行,不以 test 开头的方法在测试时会被跳过。在测试类中有两个特殊的方法 setUp 和 tearDown,这两个方法分别完成资源的创建和销毁,unittest 在调用测试方法之前会先执行 setUp 方法,在测试方法执行完成后会执行 tearDwon 方法,这样将资源的创建与销毁统一起来,不必在测试方法中编写重复的相同代码。

以下是一个测试 math 类中 fabs 函数和 isfinite 函数的单元测试,文件命名为 testMath.py

代码语言:javascript
复制
mport unittest
import math

class TestMath(unittest.TestCase):
    def setUp(self):
      print("setup Func ...")

 def test_abs(self):
        self.assertEqual(3, math.fabs(1 - 4))
        self.assertNotEqual(1, math.fabs(1 - 2))

    def test_isfinite(self):
        self.assertTrue(math.isfinite(456789))
        self.assertFalse(math.isfinite(float('inf')))
        self.assertFalse(math.isfinite(float('nan')))

    def tearDown(self):
        print("tearDown Func ...")

if __name__ == '__main__':
    unittest.main()

可以通过以下任意一个命令来运行该单元测试

代码语言:javascript
复制
python testMath.py或python -m unittest testMath

如果单元测试文件中未实现上面的最后两行代码,则只能使用 python-m unittest testMath 来启动测试。

该单元测试的输出结果如下:

代码语言:javascript
复制
(.venv) ➜  study python -m unittest testMathsetup Func ...tearDown Func ...Fsetup Func ...tearDown Func ....======================================================================FAIL: test_abs (testMath.TestMath)----------------------------------------------------------------------Traceback (most recent call last):  File "/path/testMath.py", line 12, in test_abs    self.assertNotEqual(1, math.fabs(1 - 2))AssertionError: 1 == 1.0
----------------------------------------------------------------------Ran 2 tests in 0.001s
FAILED (failures=1)

从以上测试结果中可以看出 setUp 和 tearDown 函数分别被执行了两次,共进行了两个单元测试,其中有一个出现了错误,在错误提示信息中有错误的语句,错误的位置,以及错误出现的原因。我们共有两个单元测试,因此需要进行两个资源的创建和释放,所以 setUp 和 taerDown 函数各被执行了两次。在每个单元测试运行之前均进行了资源的创建「setUp 函数被执行」,在单元测试运行之后均进行了资源的释放「tearDown 函数被执行」。


unittest 不仅能够实现对基本函数的测试,同样还能够对复杂的应用进行测试,接下来我们共同来看下如何使用 unittest 来测试 Flask 应用的代码。

代码语言:javascript
复制
import unittestimport osfrom server import create_appfrom server.module import dbfrom server.models.user import User, Permissionimport tempfileimport json

DEFAULT_USERNAME = 'test'DEFAULT_PASSWORD = 'test'
class ServerTestUser(unittest.TestCase):    def setUp(self):        self.db_fd, self.db_file= tempfile.mkstemp()        app = create_app()        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + self.db_file        self.app = app        self.clinet = app.test_client()        with app.app_context():            db.drop_all()            db.create_all()            user = User.create(                name=DEFAULT_USERNAME,                password=DEFAULT_PASSWORD,                permission=Permission.ADMINISTRATOR,                active=True)            self.headers = self.login()

    def login(self):        rv = self.clinet.post('/api/v01/user/login',                               data=json.dumps(dict(user_name='test', password='test')),                               content_type='application/json')        data = json.loads(rv.data)        token = data['token']        headers = {"Authorization":"Bearer "+token, 'Content-Type': 'application/json'}        return headers
    def test_login(self):        rv = self.clinet.post('/api/v01/user/login',                               data=json.dumps(dict(user_name=DEFAULT_PASSWORD, password=DEFAULT_PASSWORD)),                               content_type='application/json')        data = json.loads(rv.data)        self.assertEqual(rv.status_code, 200)        self.assertEqual(data['status'], 1)        self.assertEqual(data['name'], 'test')        self.assertIsNotNone(data['token'])        self.assertIsNotNone(data['admin'])        self.assertIsNotNone(data['expire'])
    def test_add_user(self):
        rv = self.clinet.post('/api/v01/user',                               data=json.dumps(dict(user_name='123', password='123', admin=False)),                               headers=self.headers)        data = json.loads(rv.data)        self.assertEqual(data['status'], 1)


    def tearDown(self):        with self.app.app_context():            db.session.remove()            db.drop_all()        os.close(self.db_fd)        os.unlink(self.db_file)

以上代码完成了使用 RESTful API 登录以及登录后添加新用户的 API 的测试。

在 setUp 函数中创建了 Flask 对象,通过 tempfile 创建临时文件用于数据存储,在 Flask 的运行环境中生成数据表、加入默认的用户,同时获取登录 Token 用户后面的 API 测试认证。在 tearDwon 函数中完成测试后的资源清理工作,删除数据表并删除创建的临时文件。

在 testlogin 和 testadd_user 函数中完成了对登录 API 和用户添加 API 的测试,并检测返回结果的正确性。

使用命令 python-m unittest discover-v-s tests 启动测试,测试结果如下:

代码语言:javascript
复制
(.venv) ➜  server git:(master) ✗ python -m unittest discover -v -s tests
test_add_user (test_user.ServerTestUser) ... ok
test_login (test_user.ServerTestUser) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.741s

OK
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 keinYe 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
Serverless HTTP 服务
Serverless HTTP 服务基于腾讯云 API 网关 和 Web Cloud Function(以下简称“Web Function”)建站云函数(云函数的一种类型)的产品能力,可以支持各种类型的 HTTP 服务开发,实现了 Serverless 与 Web 服务最优雅的结合。用户可以快速构建 Web 原生框架,把本地的 Express、Koa、Nextjs、Nuxtjs 等框架项目快速迁移到云端,同时也支持 Wordpress、Discuz Q 等现有应用模版一键快速创建。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档