0


测试工具coverage的高阶使用

  在文章Python之单元测试使用的一点心得中,笔者介绍了自己在使用Python测试工具

coverge

的一点心得,包括:

  1. 使用coverage模块计算代码测试覆盖率
  2. 使用coverage api计算代码测试覆盖率
  3. coverage配置文件的使用
  4. coverage badge的生成

  本文在此基础上,将会介绍coverage的高阶使用,包括:

  • Flask API测试
  • coverage多文件测试
  • coverage的Gitlab CI/CD集成
  • coverage badge生成

  本文中使用coverage的版本均为7.3.0。

Flask API测试

  在unittest测试框架如果对Flask API进行测试时使用HTTP请求,那么将无法得到代码覆盖率。
  我们有如下的示例Flask服务:

# -*- coding: utf-8 -*-from flask import Flask

app = Flask(__name__)@app.route('/')defindex():return"Hello index"@app.route('/test')deftest():return"Hello test"if __name__ =='__main__':
    app.run(host="0.0.0.0", port=5000, debug=True)

  正确的测试代码如下:

# -*- coding: utf-8 -*-import unittest

from flask_app import app

classAppTestCase(unittest.TestCase):defsetUp(self):
        self.ctx = app.app_context()
        self.ctx.push()
        self.client = app.test_client()deftearDown(self):
        self.ctx.pop()deftest_case1(self):
        response = self.client.get("/")
        self.assertEqual(response.status_code,200)
        self.assertEqual(response.text,"Hello index")deftest_case2(self):
        response = self.client.get("/test")
        self.assertEqual(response.status_code,200)
        self.assertEqual(response.text,"Hello test")if __name__ =="__main__":
    suite = unittest.TestSuite()
    suite.addTest(AppTestCase('test_case1'))
    suite.addTest(AppTestCase('test_case2'))
    run = unittest.TextTestRunner()
    run.run(suite)

coverage多文件测试

  我们有如下的实现两个变量相加的代码(

func_add.py

):

# -*- coding: utf-8 -*-defadd(a, b):ifisinstance(a,str)andisinstance(b,str):return a +'+'+ b
    elifisinstance(a,list)andisinstance(b,list):return a + b
    elifisinstance(a,(int,float))andisinstance(b,(int,float)):return a + b
    else:returnNone

  两个测试文件

test_func_add1.py

test_func_add2.py

,内容如下:

# -*- coding: utf-8 -*-import unittest

from func_add import add

classTestAdd(unittest.TestCase):defsetUp(self):passdeftest_add_case1(self):
        a ="Hello"
        b ="World"
        res = add(a, b)print(res)
        self.assertEqual(res,"Hello+World")deftest_add_case2(self):
        a =1
        b =2
        res = add(a, b)print(res)
        self.assertEqual(res,3)if __name__ =='__main__':# 部分用例测试# 构造一个容器用来存放我们的测试用例
    suite = unittest.TestSuite()# 添加类中的测试用例
    suite.addTest(TestAdd('test_add_case1'))
    suite.addTest(TestAdd('test_add_case2'))
    run = unittest.TextTestRunner()
    run.run(suite)
# -*- coding: utf-8 -*-import unittest

from func_add import add

classTestAdd(unittest.TestCase):defsetUp(self):passdeftest_add_case3(self):
        a =[1,2]
        b =[3]
        res = add(a, b)print(res)
        self.assertEqual(res,[1,2,3])deftest_add_case4(self):
        a =2
        b ="3"
        res = add(a, b)print(None)
        self.assertEqual(res,None)if __name__ =='__main__':# 部分用例测试# 构造一个容器用来存放我们的测试用例
    suite = unittest.TestSuite()# 添加类中的测试用例
    suite.addTest(TestAdd('test_add_case3'))
    suite.addTest(TestAdd('test_add_case4'))
    run = unittest.TextTestRunner()
    run.run(suite)

使用命令进行测试:

coverage run test_func_add1.py
coverage run test_func_add2.py
coverage report

生成的代码测试覆盖率如下:

Name          Stmts   Miss  Cover
---------------------------------
func_add.py       8275%
---------------------------------
TOTAL             8275%

这是不符合我们预期的,因为在这两个测试文件中我们对所有的代码都进行了测试,理论上测试覆盖率应该为100%,之所以这样,是因为

coverage run

命令运行时每一次都会覆盖掉之前的测试。正确的测试命令(以文件追加的形式)如下:

coverage run test_func_add1.py
coverage run --append test_func_add2.py
coverage report

此时代码覆盖率如下:

Name          Stmts   Miss  Cover
---------------------------------
func_add.py       80100%
---------------------------------
TOTAL             80100%

coverage的Gitlab CI/CD集成

  在文章Gitlab CI/CD入门(一)Python项目的CI演示中,笔者介绍了Gitlab CI/CD的入门。在此基础上,我们将集成coverage。
  首先我们的test目录如下:

.
├── __init__.py
├── func_add.py
└── test_func_add.py
func_add.py

为实现两个变量相加的代码,如前述。

test_func_add.py

为测试代码,如下:

# -*- coding: utf-8 -*-import unittest

from func_add import add

classTestAdd(unittest.TestCase):defsetUp(self):passdeftest_add_case1(self):
        a ="Hello"
        b ="World"
        res = add(a, b)print(res)
        self.assertEqual(res,"Hello+World")deftest_add_case2(self):
        a =1
        b =2
        res = add(a, b)print(res)
        self.assertEqual(res,3)deftest_add_case3(self):
        a =[1,2]
        b =[3]
        res = add(a, b)print(res)
        self.assertEqual(res,[1,2,3])deftest_add_case4(self):
        a =2
        b ="3"
        res = add(a, b)print(None)
        self.assertEqual(res,None)if __name__ =='__main__':# 部分用例测试# 构造一个容器用来存放我们的测试用例
    suite = unittest.TestSuite()# 添加类中的测试用例
    suite.addTest(TestAdd('test_add_case1'))
    suite.addTest(TestAdd('test_add_case2'))
    suite.addTest(TestAdd('test_add_case3'))
    suite.addTest(TestAdd('test_add_case4'))
    run = unittest.TextTestRunner()
    run.run(suite)

CI/CD依赖

.gitlab-ci.yml

,配置如下:

stages:- build
  - unittest

build-job:stage: build
  script:- echo `date`
    - echo "Hello, $GITLAB_USER_LOGIN!"
    - echo "This job deploys something from the $CI_COMMIT_BRANCH branch."

unit_test_job:stage: unittest
  image: python:3.9-alpine3.17
  script:- pip3 install coverage==7.3.0
    - coverage run test/test_func_add.py
    - coverage report
  coverage:'/TOTAL.*\s+(\d+%)$/'

  运行CI/CD,结果如下图:
unittest_job运行结果
  在Gitlab项目中的

Settings -> CI/CD -> General pipelines

中点击Expand,会显示CI/CD已内置

Pipeline status, Coverage report, Latest release

,其中

Coverage repor

如下图:
Coverage report
  最后我们要在项目中加入coverage badge(徽章),在Gitlab项目中的

Settings -> General -> Badge

中点击Expand,再点击Add badge,coverage徽章的配置如下:
Add badge
本项目中只有main分支,因此不需要设置变量,实际在使用过程中,需要配置变量如default_branch等。
  以上配置完毕后,项目徽章显示如下:

成功加入徽章!
  以上配置过程已开源,项目网址为:https://gitlab.com/jclian91/gitlab_ci_test

coverage badge生成

  coverage badge生成方式分为静态和动态。
  动态的话,可使用

coverage-badge

或者

genbadge

模块。
  静态的话,可使用网站:https://shields.io/badges/static-badge .
  比如我们生成编程语言的徽章,如下图:
示例徽章生成
之后我们就可以用该网址访问徽章了。

总结

  本文介绍了测试工具coverage的高阶使用,希望能对读者有所启发~

标签: 测试工具

本文转载自: https://blog.csdn.net/jclian91/article/details/132381626
版权归原作者 山阴少年 所有, 如有侵权,请联系我们删除。

“测试工具coverage的高阶使用”的评论:

还没有评论