pytest学习笔记---3
接着上一篇的内容,这里主要讲下参数化,pytest很好的支持了测试函数中变量的参数化
一、pytest的参数化
1、通过命令行来实现参数化
文档中给了一个简单的例子,
test_compute.py 的测试函数如下:
# content of test_compute.py def test_compute(param1): assert param1<4
在conftest.py中添加两个函数,一个是添加参数,一个是根据参数生成测试
# content of conftest.py def pytest_addoption(parser): parser.addoption("--all", action="store_true",help="run all combinations") def pytest_generate_tests(metafunc):if'param1'inmetafunc.fixturenames:ifmetafunc.config.option.all: end=5else: end=2metafunc.parametrize("param1", range(end))
通过在命令行添加--all的option来实现参数化,执行py.test -q test_compute.py 会发现只有2个case,而执行 py.test -q test_compute.py --all 会执行5个case
2、不同test IDs的参数化
在pytest会为每一组参数集合建立一个ID,可以试用-k来select匹配的名字子串,所以可以为不同的测试数据建立ID来区分不同的case,这个是经常使用的变量参数化,注意pytest.mark.parametrize()的括号中的顺序,(变量名称,对应的(参数化元组)的数组,ID的数组) , 这样很好的解决了代码重复编写,减少了维护,可以很好的实现数据与代码想分离
# content of test_time.py import pytestfromdatetime import datetime, timedelta testdata=[ (datetime(2001,12,12), datetime(2001,12,11), timedelta(1)), (datetime(2001,12,11), datetime(2001,12,12), timedelta(-1)), ] @pytest.mark.parametrize("a,b,expected", testdata) def test_timedistance_v0(a, b, expected): diff= a -b assert diff==expected @pytest.mark.parametrize("a,b,expected", testdata, ids=["forward","backward"]) def test_timedistance_v1(a, b, expected): diff= a -b assert diff== expected
3、重要的资源参数化,这里面给了个case,是关于db的,觉得没太多可说的,就是一个简单的工厂,上代码了
# content of conftest.py import pytest def pytest_generate_tests(metafunc):if'db'inmetafunc.fixturenames: metafunc.parametrize("db", ['d1','d2'], indirect=True)classDB1:"one database object"classDB2:"alternative database object"@pytest.fixture def db(request):ifrequest.param =="d1":returnDB1() elif request.param=="d2":returnDB2()else: raise ValueError("invalid internal test config")
4、通过类来实现测试函数的参数化,这个还是很有意义的,自己理解下吧,没什么难度
# content of ./test_parametrize.py import pytest def pytest_generate_tests(metafunc): # called once per each test function funcarglist= metafunc.cls.params[metafunc.function.__name__] argnames= list(funcarglist[0]) metafunc.parametrize(argnames, [[funcargs[name]fornameinargnames]forfuncargsinfuncarglist])classTestClass: # a map specifying multiple argument setsfora test methodparams={'test_equals': [dict(a=1, b=2), dict(a=3, b=3), ],'test_zerodivision': [dict(a=1, b=0), ], } def test_equals(self, a, b): assert a==b def test_zerodivision(self, a, b): pytest.raises(ZeroDivisionError,"a/b")
5、通过multiple fixtures来实现间接的参数化,文档中给了使用不同版本的python编译器的代码,有需求的自己看下吧
"""module containing a parametrized tests testing cross-python serialization via the pickle module."""import py import pytest import _pytest._code pythonlist= ['python2.6','python2.7','python3.3'] @pytest.fixture(params=pythonlist) def python1(request, tmpdir): picklefile= tmpdir.join("data.pickle")returnPython(request.param, picklefile) @pytest.fixture(params=pythonlist) def python2(request, python1):returnPython(request.param, python1.picklefile)classPython: def __init__(self, version, picklefile): self.pythonpath=py.path.local.sysfind(version)ifnot self.pythonpath: pytest.skip("%r not found"%(version,)) self.picklefile=picklefile def dumps(self, obj): dumpfile= self.picklefile.dirpath("dump.py") dumpfile.write(_pytest._code.Source("""import pickle f= open(%r,'wb') s= pickle.dump(%r, f, protocol=2) f.close()"""% (str(self.picklefile), obj)))py.process.cmdexec("%s %s"%(self.pythonpath, dumpfile)) def load_and_is_true(self, expression): loadfile= self.picklefile.dirpath("load.py") loadfile.write(_pytest._code.Source("""import pickle f= open(%r,'rb') obj=pickle.load(f) f.close() res= eval(%r)ifnot res: raise SystemExit(1)"""% (str(self.picklefile), expression)))print (loadfile) py.process.cmdexec("%s %s"%(self.pythonpath, loadfile)) @pytest.mark.parametrize("obj", [42, {}, {1:3},]) def test_basic_objects(python1, python2, obj): python1.dumps(obj) python2.load_and_is_true("obj == %s"% obj)
二、使用自定义的markers
1、自定义一个mark,如下,然后 py.test -v -m webtest 只运行标记了webtest的函数, py.test -v -m "not webtest" 来运行未标记webtest的
# content of test_server.py import pytest @pytest.mark.webtest def test_send_http(): pass # perform. some webtest testforyour app def test_something_quick(): pass def test_another(): passclassTestClass: def test_method(self): pass
2、还可以通过-v 指定的函数ID, py.test -v test_server.py::TestClass::test_method 来运行指定的函数
3、使用-k 来匹配名字子串, py.test -v -k http , py.test -k "not send_http" -v
4、在pytest.ini中**markers
# content of pytest.ini [pytest] markers=webtest: mark a testasa webtest. addopts= --pyargs
好了,就这么多吧,下面的一些关于mocking的,有时间再来写
TAG: