美文网首页软件测试
pytest学习笔记---3

pytest学习笔记---3

作者: A丶咔咔 | 来源:发表于2020-07-25 15:33 被阅读0次

接着上一篇的内容,这里主要讲下参数化,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' in metafunc.fixturenames:

        if metafunc.config.option.all:   

            end = 5

        else:

            end = 2

        metafunc.parametrize("param1", range(end))  1 C:\Python27\python.exe E:/test/testwith.py

2 __enter__

3 test

4 __exit__

通过在命令行添加--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 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

3、重要的资源参数化,这里面给了个case,是关于db的,觉得没太多可说的,就是一个简单的工厂,上代码了

# 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")

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] 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")

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")

    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)


二、使用自定义的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 test for your app

def test_something_quick():

    pass

def test_another():

    pass

class TestClass:

    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 test as a webtest.

    addopts = --pyargs

好了,就这么多吧,下面的一些关于mocking的,有时间再来写

软件测试技术交流群:1125760266

相关文章

网友评论

    本文标题:pytest学习笔记---3

    本文链接:https://www.haomeiwen.com/subject/amfmlktx.html