pytest學習筆記(三)

接着上一篇的內容,這裏主要講下參數化,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的,有時間再來寫

相關文章
相關標籤/搜索