接着上一篇的內容,這裏主要講下參數化,pytest很好的支持了測試函數中變量的參數化python
1、pytest的參數化web
一、經過命令行來實現參數化express
文檔中給了一個簡單的例子,數組
test_compute.py 的測試函數以下:
# content of test_compute.py def test_compute(param1): assert param1 < 4
在conftest.py中添加兩個函數,一個是添加參數,一個是根據參數生成測試app
# 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' in metafunc.fixturenames: if metafunc.config.option.all: end = 5 else: end = 2 metafunc.parametrize("param1", range(end))
經過在命令行添加--all的option來實現參數化,執行py.test -q test_compute.py 會發現只有2個case,而執行 py.test -q test_compute.py --all 會執行5個casepython2.7
二、不一樣test IDs的參數化函數
在pytest會爲每一組參數集合創建一個ID,能夠試用-k來select匹配的名字子串,因此能夠爲不一樣的測試數據創建ID來區分不一樣的case,這個是常常使用的變量參數化,注意pytest.mark.parametrize()的括號中的順序,(變量名稱,對應的(參數化元組)的數組,ID的數組) , 這樣很好的解決了代碼重複編寫,減小了維護,能夠很好的實現數據與代碼想分離測試
# content of test_time.py import pytest from datetime 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
三、重要的資源參數化,這裏面給了個case,是關於db的,以爲沒太多可說的,就是一個簡單的工廠,上代碼了ui
# content of conftest.py import pytest def pytest_generate_tests(metafunc): if 'db' in metafunc.fixturenames: metafunc.parametrize("db", ['d1', 'd2'], indirect=True) class DB1: "one database object" class DB2: "alternative database object" @pytest.fixture def db(request): if request.param == "d1": return DB1() elif request.param == "d2": return DB2() else: raise ValueError("invalid internal test config")
四、經過類來實現測試函數的參數化,這個仍是頗有意義的,本身理解下吧,沒什麼難度spa
# 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] for name in argnames] for funcargs in funcarglist]) class TestClass: # a map specifying multiple argument sets for a test method params = { '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")
五、經過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") return Python(request.param, picklefile) @pytest.fixture(params=pythonlist) def python2(request, python1): return Python(request.param, python1.picklefile) class Python: def __init__(self, version, picklefile): self.pythonpath = py.path.local.sysfind(version) if not 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) if not 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)
2、使用自定義的markers
一、自定義一個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 test for your app def test_something_quick(): pass def test_another(): pass class TestClass: def test_method(self): pass
二、還能夠經過-v 指定的函數ID, py.test -v test_server.py::TestClass::test_method 來運行指定的函數
三、使用-k 來匹配名字子串, py.test -v -k http , py.test -k "not send_http" -v
四、在pytest.ini中註冊markers
# content of pytest.ini [pytest] markers = webtest: mark a test as a webtest. addopts = --pyargs
好了,就這麼多吧,下面的一些關於mocking的,有時間再來寫