Pytest的fixture(三)
在单元测试的组件中,主要分为测试用例,测试固件,测试套件,测试执行以及测试报告,看过我书的
同学对这些应该很清晰。测试固件也是不难理解,也就是在测试用例执行前需要做的动作和测试执行后需要
做的事情。比如在UI的自动化测试中,我们更加关注的是对页面的操作,而不是关心打开浏览器和关闭浏览器,
在数据库的操作中,更加关注的是对MySQL的基本操作,而不怎么关心连接数据库和数据库断开连接这部分。
所以打开浏览器和关闭浏览器,连接数据库和关闭数据库部分,可以让测试固件去干,测试用例的层面更加
关心测试用例的执行结果以及断言结果。在pytest的测试框架中,测试固件有各种形式的表现,比如除了刚才
说的初始化与清理外,还有它强大的参数化的部分。下面还是通过具体的案例来说明这部分的应用。
fixture是在测试函数运行前后,由pytest执行的外壳函数。首先来看fixture的函数返回值,也就是返回数值。
先看如下的案例代码:
#!/usr/bin/python3 #coding:utf-8 import pytest @pytest.fixture() def login(): return 'safghj43567dsafg' def test_info_setting(login): '''查看用户个人设置''' assert login=='safghj43567dsafg'
在如上的函数中,可以看到login的函数返回了token的值,但是它的装饰器是@pytest.fixtuer,在测试函数中,
传入login的,也就是函数的形式函数也可以是函数,然后在测试函数中进行断言验证,执行的结果会显示通过,
见执行的结果信息:
============================= test session starts ============================== platform darwin -- Python 3.7.4, pytest-4.0.2, py-1.8.0, pluggy-0.12.0 -- /usr/local/bin/python3.7 cachedir: .pytest_cache rootdir: /Applications/code/stack/study/xunit/fixtureTest, inifile: plugins: allure-adaptor-1.7.10 collecting ... collected 1 item test_001.py::test_info_setting PASSED [100%] =========================== 1 passed in 0.01 seconds =========================== Process finished with exit code 0
我们有必要进行断言的验证,打断点来调试程序执行的顺序,在开始之前,先写一个函数的形式参数是函数的案例
代码,见如下:
def f1(): print('hello') def f2(f1): return f1 f2(f1())
在如上函数执行后,可以看到输出的结果是f1函数的结果,这就是函数的形式参数是函数的案例应用,当然下来是装饰器,
关于装饰器我就不详细的介绍了,在博客的其他文章有专门介绍装饰器的文章。现在来看上面的测试函数执行的顺序,见
如下:
继续来解释装饰器@pytest.fixture(),它是声明一个函数是fixture,如果测试函数的参数列表中包含了fixture名,那么pytest执行的时候,
就会检测到,并且在测试函数运行之前执行该fixture,fixture可以完成任务,也可以返回数据给测试函数。
接着来看另外一个场景,也就是数据的初始化和清理,我们还是依据还是的案例来实战,结合fixture来实战,假设要测试一个接口的查询,
前提是我添加数据,这就是初始化干的事,查询完后,要删除数据,这就是清理,来看在fixture的案例应用,先来看被测试的代码,也就是flask
写的api,见如下:
#!/usr/bin/env python # -*-coding:utf-8 -*- from flask import Flask,request,jsonify,abort,make_response from flask_restful import Resource,Api app=Flask(__name__) api=Api(app=app) books=[ { 'id':1, 'author':'无涯', 'name':'Python自动化测试实战', "done":True } ] class BooksApi(Resource): def get(self): return jsonify(books) def post(self): if not request.json or not 'author' in request.json: abort(400) book = { 'id': books[-1]['id'] + 1, 'author': request.json.get('author'), 'name': request.json.get('name'), 'done': False } books.append(book) return jsonify({'status':1002,'msg':'添加成功','datas':book},201) class BookApi(Resource): def get(self,book_id): book=list(filter(lambda t:t['id']==book_id,books)) print(book) if len(book)==0: abort(400) else: return jsonify(book) def delete(self,book_id): book = list(filter(lambda t: t['id'] == book_id, books)) if len(book)==0: abort(404) books.remove(book[0]) return jsonify({'status':1001,'msg':'删除成功'}) api.add_resource(BooksApi,'/v1/api/books',endpoint='/v1/api/books') api.add_resource(BookApi,'/v1/api/book/<int:book_id>') if __name__ == '__main__': app.run(debug=True)
下来结合pytest的fixture来测试刚才说的业务,实现的测试代码如下:
#!/usr/bin/python3 #coding:utf-8 import pytest import requests def addBook(): '''添加书籍''' dict1={"author":"无涯","name":"无涯课堂","done":True} r=requests.post(url='http://127.0.0.1:5000/v1/api/books',json=dict1) with open('bookID','w') as f: f.write(str(r.json()[0]['datas']['id'])) def getBookID(): with open('bookID', 'r') as f: return f.read() def delBook(): '''删除书籍''' r=requests.delete(url='http://127.0.0.1:5000/v1/api/book/{0}'.format(getBookID())) print(r.json()) @pytest.fixture() def api(): addBook() yield delBook() def test_query_book(api): r=requests.get(url='http://127.0.0.1:5000/v1/api/book/{0}'.format(getBookID())) assert r.json()[0]['id']==int(getBookID())