pytest fixtures: explicit, modular, scalable 中文版

随着编程生涯的增长,会越来越发现,基本出现在中文相关资料的相关技术文章,靠谱的太少,老外的相反,真的是有种月亮还是外国的圆的感觉,最近想闲下来事情不多的时候,翻译一些,自己用到的,特别有用处的一些技术文章。前边的一篇JAVA NIO是第一篇,这将是第二篇。

python是一门特别容易上手,使用的语言,并且得益于其庞大的第三方库,使其具有其他脚本语言不具有的更多能力。或许这是众多软件使用python作为主要语言,比如open stack,或者提供python版本的相关接口,比如Docker。

pytest是python的一个单元测试框架,相较于unittest, 有诸多的有点,本篇文章简述pytest的一些相关特性,希望对自己对大家都有帮助。


原文地址

test fixtures的目的是提供一个可靠的基准,使测试可以稳定重复的执行。pytest fixtures相比于传统的xUnit的setup/teardown提供了巨大的改进:

  • fixtures有一个明确的名字,可以通过在测试方法,模块,类,或者整个工程上通过声明来激活。
  • fixtures使用模块化的方式实现,每一个fixture名字可以触发一个fixture函数,这个函数本身可以使用其他的fixtures.
  • fixtures管理从简单的单元扩展到复杂的功能测试,允许根据配置和组件选项参数化夹具和测试,或者在类,模块或整个测试会话范围内重新使用夹具。

除此之外,pytest继续支持经典的xunit风格的setup。可以混合使用两种分割,也可以逐渐多的从经典的风格向新风格转变。可以从现有的unitest.TestCase或者基于nose的风格开始学起。

Note:
pytest-2.4为了更简单的上下文整合以及更线性的书写teardown代码引入了实验性质的yield类型的fixture的机制。

Fixtures作为函数形参

测试函数可以通过命名形参的形式来接收fixture对象。对每一个参数名字,一个对应的相同名字的fixture函数提供fixture对象。Fixture函数通过使用@pytest.fixture来注册。让我们看一个简单的包含fixture和测试函数的例子:

# content of ./test_smtpsimple.py
import pytest

@pytest.fixture
def smtp():
    import smtplib
    return smtplib.SMTP("smtp.gmail.com")

def test_ehlo(smtp):
    response, msg = smtp.ehlo()
    assert response == 250
    assert 0 # for demo purposes

在这个例子中,test_ehlo需要一个smtp的fixture,pytest会找到并调用有@pytest.fixture修饰的名字为smtp的fixture函数,测试函数运行的结果如下:

$ py.test test_smtpsimple.py
=========================== test session starts ============================
platform linux -- Python 3.4.1 -- py-1.4.27 -- pytest-2.7.1
rootdir: /tmp/doc-exec-98, inifile:
collected 1 items

test_smtpsimple.py F

================================= FAILURES =================================
________________________________ test_ehlo _________________________________

smtp = <smtplib.SMTP object at 0x7f9d45764c88>

    def test_ehlo(smtp):
        response, msg = smtp.ehlo()
        assert response == 250
>       assert 0 # for demo purposes
E       assert 0

test_smtpsimple.py:11: AssertionError
========================= 1 failed in 1.07 seconds =========================

在输出的结果中可以看到测试函数在有smtp形参的情况下被调用,fixture函数创建了smtplib.SMTP()实例。测试函数在我们故意写错的assert 0处出错。下边是pytest使用的调用测试函数的确切策略:

  1. pytest通过test_前缀找到test_ehlo测试函数。这个测试函数需要一个名字为smtp的函数形参。一个匹配的fixture函数被发现。
  2. smtp()被调用并创建了一个实例。
  3. test_ehlo()被调用,并且在测试函数的最后一行失败。
    注意,假设你拼写错了函数形参,或者想使用一个不存在的fixture,你会在一系列的可访问函数形参中发现一个错误。

注意:
你可以一直使用下边的命令:
py.test –fixtures test_simlefactory.py
来查看一切可用的fixtures。
在早于2.3的版本中,没有@pytest.fixture标签,你必须对fixture工厂使用一个pytest_funcarg__NAME的魔法前缀。这个会在后续的版本中持续支持,但是不再建议使用这种作为首选声明fixture的方式。

“Funcargs”依赖注入的入门例子

在向测试函数注入fixtures中,pytest-2.0引入了术语”funcargs”或者“funcarg mechanism”,这些术语依然存在于今天的相关文档中。它现在指的是将夹具值注入测试函数的具体情况。在pytest-2.3中有更多的方式注入fixtures,但是”funcargs”仍然是主要的方式,因为它们允许直接声明测试函数的依赖关系。
正如下面的例子更详细地展示的,funcargs允许测试函数轻松接收和处理特定的预先初始化的应用程序对象,而不必关心导入/设置/清除细节。这是依赖注入的一个主要例子,fixture函数扮演着注入器的角色,而测试功能则是fixture对象的消费者。

在跨模块(或者类/会话)之间共享fixture

Fixtures需要的网络访问会依赖连接性,并且通常创建的代价比较大,我们可以为前边的例子为@pytest.fixture加上scope=’module’参数,可以让装饰的smtp fixture函数在每次测试模块执行时执行一次,。在同一个测试模块中的测试函数将会收到同一个smtp fixture实例。接下来的例子,我们会将fixture函数放到一个单独的conftest.py文件。这样可以让同一个目录下的多个测试模块中的所有测试函数都可以访问到。

# content of conftest.py
import pytest
import smtplib

@pytest.fixture(scope="module")
def smtp():
    return smtplib.SMTP("smtp.gmail.com")

fixture的名字已然是smtp,你可以通过将smtp作为测试函数或者其他fixture函数(需要和conftest.py在同一个目录或者子目录下)的输入形参来访问它的执行结果。

# content of test_module.py

def test_ehlo(smtp):
    response, msg = smtp.ehlo()
    assert response == 250
    assert "smtp.gmail.com" in str(msg, 'ascii')
    assert 0  # for demo purposes

def test_noop(smtp):
    response, msg = smtp.noop()
    assert response == 250
    assert 0  # for demo purposes

我们故意插入失败的assert 0语句,来更方便的查看执行这个测试发生了什么,下边是运行的结果:

$ py.test test_module.py
=========================== test session starts ============================
platform linux -- Python 3.4.1 -- py-1.4.27 -- pytest-2.7.1
rootdir: /tmp/doc-exec-98, inifile:
collected 2 items

test_module.py FF

================================= FAILURES =================================
________________________________ test_ehlo _________________________________

smtp = <smtplib.SMTP object at 0x7fb558b12240>

    def test_ehlo(smtp):
        response = smtp.ehlo()
        assert response[0] == 250
>       assert "merlinux" in response[1]
E       TypeError: Type str doesn't support the buffer API

test_module.py:5: TypeError
________________________________ test_noop _________________________________

smtp = <smtplib.SMTP object at 0x7fb558b12240>

    def test_noop(smtp):
        response = smtp.noop()
        assert response[0] == 250
>       assert 0  # for demo purposes
E       assert 0

test_module.py:11: AssertionError
========================= 2 failed in 0.82 seconds =========================

你可以观察到两个assert 0失败,更重要的是你可以发现smtp在两个测试函数中是同一个实例(module作用域)。结果是,两个测试函数重用了smtp,是测试运行更快。
除了module作用域,也可以通过下述方法,使用seesion作用域。

@pytest.fixture(scope="session")
def smtp(...):
    # the returned fixture value will be shared for
    # all tests needing it

fixture finalization/执行teardown代码

pytest支持fixture在离开作用域的时候执行特定的清理代码。通过在你自己的fixture中接受一个request对象,又可以调用request.addfinalizer一次或者多次:

# content of conftest.py

import smtplib
import pytest

@pytest.fixture(scope="module")
def smtp(request):
    smtp = smtplib.SMTP("smtp.gmail.com")
    def fin():
        print ("teardown smtp")
        smtp.close()
    request.addfinalizer(fin)
    return smtp  # provide the fixture value

fin函数会在模块中最后一个使用fixture的测试函数结束的时候执行。
我们试着执行一下,看看会发生什么。

$ py.test -s -q –tb=no
FFteardown smtp

2 failed in 1.44 seconds

我们可以注意到smtp在两个测试函数执行之后进行了清理。注意,如果我们用function作用域来修饰我们的fixture,那么fixture setup和cleanup会包围每一个单独的测试用例。在这两种情况中测试模块自身都不需要作改动,也不需要知道fixture setup的细节。

Fixture可以响应测试上下文的请求

Fixuture可以接受request对象,request可以用来反映测试函数、类或者模块上下文的的请求。我们来进一步扩展smtp的fixture例子,我们在使用我们fixture的测试模块中读入一个可选的服务器URL。

# content of conftest.py
import pytest
import smtplib

@pytest.fixture(scope="module")
def smtp(request):
    server = getattr(request.module, "smtpserver", "smtp.gmail.com")
    smtp = smtplib.SMTP(server)

    def fin():
        print ("finalizing %s (%s)" % (smtp, server))
        smtp.close()

    return smtp

我们使用request.module属性来从测试模块获取一个可选的smtpserver。我们再一次执行这个例子,和上边的结果相同:

$ py.test -s -q –tb=no
FF
2 failed in 0.62 seconds

接下来我们快速创建另外一个测试模块,在这个测试模块中设置了服务器URL:

# content of test_anothersmtp.py

smtpserver = "mail.python.org"  # will be read by smtp fixture

def test_showhelo(smtp):
    assert 0, smtp.helo()

运行它:

$ py.test -qq --tb=short test_anothersmtp.py
F
================================= FAILURES =================================
______________________________ test_showhelo _______________________________
test_anothersmtp.py:5: in test_showhelo
    assert 0, smtp.helo()
E   AssertionError: (250, b'mail.python.org')
E   assert 0

哇,smtp fixture函数从我们测试模块命名空间中找到了我们邮件服务器的名字。

参数化fixture

fiture函数可以参数化,在这种情况下,它们会被调用多次,每次执行一组相关测试,即依赖于这个fixture的测试。测试函数通常不需要关注他们的重新执行。fixture参数化有助于为多种方式配置的组件进行详尽的功能测试。

# content of conftest.py
import pytest
import smtplib

@pytest.fixture(scope="module",
                params=["smtp.gmail.com", "mail.python.org"])
def smtp(request):
    smtp = smtplib.SMTP(request.param)
    def fin():
        print ("finalizing %s" % smtp)
        smtp.close()
    request.addfinalizer(fin)
    return smtp

和前述最大的差别是@pytest.fixture中的params参数,在列表中每个值,fixture执行的时候可以通过request.param来访问到。测试函数不需要更改。让我们再运行一次我们的代码:

$ py.test -q test_module.py
FFFF
================================= FAILURES =================================
__________________________ test_ehlo[merlinux.eu] __________________________

smtp = <smtplib.SMTP object at 0x7f4eecf92080>

    def test_ehlo(smtp):
        response = smtp.ehlo()
        assert response[0] == 250
>       assert "merlinux" in response[1]
E       TypeError: Type str doesn't support the buffer API

test_module.py:5: TypeError
__________________________ test_noop[merlinux.eu] __________________________

smtp = <smtplib.SMTP object at 0x7f4eecf92080>

    def test_noop(smtp):
        response = smtp.noop()
        assert response[0] == 250
>       assert 0  # for demo purposes
E       assert 0

test_module.py:11: AssertionError
________________________ test_ehlo[mail.python.org] ________________________

smtp = <smtplib.SMTP object at 0x7f4eecf92048>

    def test_ehlo(smtp):
        response = smtp.ehlo()
        assert response[0] == 250
>       assert "merlinux" in response[1]
E       TypeError: Type str doesn't support the buffer API

test_module.py:5: TypeError
-------------------------- Captured stdout setup ---------------------------
finalizing <smtplib.SMTP object at 0x7f4eecf92080>
________________________ test_noop[mail.python.org] ________________________

smtp = <smtplib.SMTP object at 0x7f4eecf92048>

    def test_noop(smtp):
        response = smtp.noop()
        assert response[0] == 250
>       assert 0  # for demo purposes
E       assert 0

test_module.py:11: AssertionError
4 failed in 1.75 seconds

我们可以发现我们的两个测试每个均执行了两次,每次使用一个不同的smtp实例。另外,在参数是mail.python.org的情况下,test_ehlo第二次失败的原因是期望的服务器字符串和实际的不一样。

pytest会为每一个参数化的fixture创建测试ID,比如为前一个例子可以创建test_ehlo[smtp.gmail.com]和test_ehlo[mail.python.org]。这些ID可以通过在命令行中用-k进行限定运行特定的用例。也用来标定运行失败的特定用例。运行pytest的时候加上–collect-only可以显示生成的ID。

数字,字符串,布尔以及None在测试ID中使用惯常的表现形式。对于其他的对象,pytest会根据参数的名字构建一个字符串表示。也可以通过ids的关键字参数来自定义测试ID的字符串表示形式:

# content of test_ids.py
import pytest

@pytest.fixture(params=[0, 1], ids=["spam", "ham"])
def a(request):
    return request.param

def test_a(a):
    pass

def idfn(fixture_value):
    if fixture_value == 0:
        return "eggs"
    else:
        return None

@pytest.fixture(params=[0, 1], ids=idfn)
def b(request):
    return request.param

def test_b(b):
    pass

上边的例子展示了ids可以使用一个字符串列表,也可以使用一个被调用的返回值是字符串的一个函数。在后一种情况中,如果函数返回值是None,那么pytest会自动产生一个ID来使用。

再次运行上边的测试:

$ py.test --collect-only test_ids.py
=========================== test session starts ============================
platform linux -- Python 3.4.1 -- py-1.4.27 -- pytest-2.7.1
rootdir: /tmp/doc-exec-98, inifile:
collected 6 items
<Module 'test_anothersmtp.py'>
  <Function 'test_showhelo[merlinux.eu]'>
  <Function 'test_showhelo[mail.python.org]'>
<Module 'test_module.py'>
  <Function 'test_ehlo[merlinux.eu]'>
  <Function 'test_noop[merlinux.eu]'>
  <Function 'test_ehlo[mail.python.org]'>
  <Function 'test_noop[mail.python.org]'>

=============================  in 0.02 seconds =============================

模块化:在fixture中使用fixture

我们不仅可以在测试函数中使用fixture,fixture可以使用其他的fixture。这有助于实现fixture的模块化设计,并且可以在跨许多项目重用特定框架的fixtures。作为一个简单的例子,我们可以扩展前一个例子,实例化一个app对象,在这个对象中可以直接使用我们之前定义的smtp资源:

# content of test_appsetup.py

import pytest

class App:
    def __init__(self, smtp):
        self.smtp = smtp

@pytest.fixture(scope="module")
def app(smtp):
    return App(smtp)

def test_smtp_exists(app):
    assert app.smtp

我们声明了一个app fixture,这个fixture使用了之前定义的smtp fixture。然后实例化了一个App对象。
让我们运行这个测试用例:

$ py.test -v test_appsetup.py
=========================== test session starts ============================
platform linux -- Python 3.4.1 -- py-1.4.27 -- pytest-2.7.1 -- /tmp/sandbox/pytest/.tox/regen/bin/python3.4
rootdir: /tmp/doc-exec-98, inifile:
collecting ... collected 2 items

test_appsetup.py::test_smtp_exists[merlinux.eu] PASSED
test_appsetup.py::test_smtp_exists[mail.python.org] PASSED

========================= 2 passed in 1.09 seconds =========================

由于smtp使用了参数化,所以本测试会使用两个不同的App实例运行两次。我们不用在app这个实例中关心smtp的参数化,这部分工作pytest已经自动完成。

注意,app fixture是模块作用域,其使用的smtp也是模块作用域。如果smtp是会话作用域,这个例子还会正常运行:一个fixture可以使用一个比自身作用域大的fixture,反之是不可以的:一个会话作用域的fixture不可以使用一个模块作用于的fixture。

通过fixture实例自动分组测试

pytest在测试运行期间会最小化活跃的fixture数目。如果你有一个参数化的fixture,那么所有使用这个参数化fixture的测试会首先使用一个实例运行,然后清理,之后再创建下一个fixture实例。考虑到其他一些情况,这种方式的应用测试不需要考虑全局状态。

接下来的例子使用两个参数化的funcargs,一个是模块作用域,所有的函数打印可以清晰看到setup/teardown的流程。

# content of test_module.py
import pytest

@pytest.fixture(scope="module", params=["mod1", "mod2"])
def modarg(request):
    param = request.param
    print ("create", param)
    def fin():
        print ("fin %s" % param)
    return param

@pytest.fixture(scope="function", params=[1,2])
def otherarg(request):
    return request.param

def test_0(otherarg):
    print ("  test0", otherarg)
def test_1(modarg):
    print ("  test1", modarg)
def test_2(otherarg, modarg):
    print ("  test2", otherarg, modarg)

我们使用详细打印的方式运行上述的测试:

$ py.test -v -s test_module.py
=========================== test session starts ============================
platform linux -- Python 3.4.1 -- py-1.4.27 -- pytest-2.7.1 -- /tmp/sandbox/pytest/.tox/regen/bin/python3.4
rootdir: /tmp/doc-exec-98, inifile:
collecting ... collected 8 items

test_module.py::test_0[1]   test0 1
PASSED
test_module.py::test_0[2]   test0 2
PASSED
test_module.py::test_1[mod1] create mod1
  test1 mod1
PASSED
test_module.py::test_2[1-mod1]   test2 1 mod1
PASSED
test_module.py::test_2[2-mod1]   test2 2 mod1
PASSED
test_module.py::test_1[mod2] create mod2
  test1 mod2
PASSED
test_module.py::test_2[1-mod2]   test2 1 mod2
PASSED
test_module.py::test_2[2-mod2]   test2 2 mod2
PASSED

========================= 8 passed in 0.02 seconds =========================

我们可以发现参数化的模块作用域的参数化资源导致了一种测试执行顺序,这种顺序可以产生最少的活跃资源,mod1的清理在mod2之前完成。

从类、模块、项目引用fixture

一些时候测试函数不需要直接访问fixture对象。例如,测试有时候需要一个空的目录作为当前的工作目录,有时候又不需要。这个时候你可以使用标准的临时目录以及pytest的fixture来达到这个目标。我们把fixture创建在conftest.py文件中:

# content of conftest.py

import pytest
import tempfile
import os

@pytest.fixture()
def cleandir():
    newpath = tempfile.mkdtemp()
    os.chdir(newpath)

通过usefixtures标记来声明其在测试模块中的使用:

# content of test_setenv.py
import os
import pytest

@pytest.mark.usefixtures("cleandir")
class TestDirectoryInit:
    def test_cwd_starts_empty(self):
        assert os.listdir(os.getcwd()) == []
        with open("myfile", "w") as f:
            f.write("hello")

    def test_cwd_again_starts_empty(self):
        assert os.listdir(os.getcwd()) == []

由于usefixtures标记,cleandix fixture会再每次测试方法执行的时候执行,就像在每次测试函数执行之前执行了cleandir函数一样。让我们运行一下来验证fixture激活了,测试可以通过:

$ py.test -q
..
2 passed in 0.01 seconds

你可以声明使用多个fixture

@pytest.mark.usefixtures(“cleandir”, “anotherfixture”)

你可能需要将fixture声明在测试模块的级别,使用一个泛型的标记机制:

pytestmark = pytest.mark.usefixtures(“cleandir”)
你可以将你的工程使用到的所有fixture放到一个ini文件中:

# content of pytest.ini

[pytest]
usefixtures = cleandir

自动使用fixtures(类似xUnit的setup)

有时,你需要在没有usefixtures和funcargs的情况下自动执行fixture。作为一个实际的例子,想象一下我们有一个数据库的fixture,这个fixture有begin/roolback/commit的架构,我们想自动把事物和回滚包装在测试的周围。下边是一个例子:

# content of test_db_transact.py

import pytest

class DB:
    def __init__(self):
        self.intransaction = []
    def begin(self, name):
        self.intransaction.append(name)
    def rollback(self):
        self.intransaction.pop()

@pytest.fixture(scope="module")
def db():
    return DB()

class TestClass:
    @pytest.fixture(autouse=True)
    def transact(self, request, db):
        db.begin(request.function.__name__)
        request.addfinalizer(db.rollback)

    def test_method1(self, db):
        assert db.intransaction == ["test_method1"]

    def test_method2(self, db):
        assert db.intransaction == ["test_method2"]

类级别的transact fixture标注了autouse=True,这意味着这个类里面的所有测试在不需要传入形参或者使用类级别usefixtures修饰符的情况下自动使用这个fixture。

我们运行这个测试:

$ py.test -q
..
2 passed in 0.01 seconds

接下里解释一下autouse的fixture如何在其他作用域工作:

  1. 如果一个autouse的fixture定义在测试模块,所有测试模块中的测试函数会自动的使用它。
  2. 如果一个autouse的fixture定义在conftest.py文件,那么,在其目录下的所有测试模块中的测试函数会自动使用。
  3. 最后,在使用的时候请注意:如果你在一个插件中定义了一个autouse的fixture,那么在安装这个插件工程的所有测试均会自动执行。如果一个fixture的执行依赖ini-file(配置文件)中的一个设置。这个全局的fixture可以做一些工作,然后也可能避免做一些耗费巨大的impoerts活着计算工作。作为一个全局的配置工具。

你可能觉得上述的transact fixture是定义良好的fixture,想在整个项目中使用它而不是广泛的激活,那么将这个fixture去掉autouse并放入conftest.py是一个很好的办法:

# content of conftest.py
@pytest.fixture()
def transact(self, request, db):
    db.begin()
    request.addfinalizer(db.rollback)

然后创建一个TestClass来按需使用它:

@pytest.mark.usefixtures("transact")
class TestClass:
    def test_method1(self):
        ...

在这个TestClass中的所有测试方法会使用transaction fixture,同时在同一个测试模块中其他的测试类或者测试函数只能在加入transact引用的情况下才能使用它。

fixture函数的可见性

在实现你测试函数的过程中意识到你想在很多测试函数中使用一个fixture,你可以把这个fixture移到conftest.py文件中或者直接在不修改代码的情况下独立的安装一个插件。fixture的发现顺序是首先测试类、然后测试模块、最后是conftest.py文件,最后是内嵌和第三方的插件。

可以在多个级别覆写fixtures

在一个相对大的测试套装中,你需要覆写一个全局的fixture为一个本地定义版本,并且保证代码的可读性以及更好的维护性。

在目录级别(conftest.py)覆写fixture

给定测试文件的目录层级如下:

tests/
    __init__.py

    conftest.py
        # content of tests/conftest.py
        import pytest

        @pytest.fixture
        def username():
            return 'username'

    test_something.py
        # content of tests/test_something.py
        def test_username(username):
            assert username == 'username'

    subfolder/
        __init__.py

        conftest.py
            # content of tests/subfolder/conftest.py
            import pytest

            @pytest.fixture
            def username(username):
                return 'overridden-' + username

        test_something.py
            # content of tests/subfolder/test_something.py
            def test_username(username):
                assert username == 'overridden-username'

你可以看到,可以在特定测试目录的级别,一个有相同名字的fixture可以被覆写。正如上边例子演示的。

在测试模块级别覆写fixture

给定测试文件的目录层级如下:

tests/
    __init__.py

    conftest.py
        # content of tests/conftest.py
        @pytest.fixture
        def username():
            return 'username'

    test_something.py
        # content of tests/test_something.py
        import pytest

        @pytest.fixture
        def username(username):
            return 'overridden-' + username

        def test_username(username):
            assert username == 'overridden-username'

    test_something_else.py
        # content of tests/test_something_else.py
        import pytest

        @pytest.fixture
        def username(username):
            return 'overridden-else-' + username

        def test_username(username):
            assert username == 'overridden-else-username'

在上边的例子中,同名的fixture可以在测试模块级别进行覆写。

Override a fixture with direct test parametrization

给定测试文件的目录层级如下:

tests/
    __init__.py

    conftest.py
        # content of tests/conftest.py
        import pytest

        @pytest.fixture
        def username():
            return 'username'

        @pytest.fixture
        def other_username(username):
            return 'other-' + username

    test_something.py
        # content of tests/test_something.py
        import pytest

        @pytest.mark.parametrize('username', ['directly-overridden-username'])
        def test_username(username):
            assert username == 'directly-overridden-username'

        @pytest.mark.parametrize('username', ['directly-overridden-username-other'])
        def test_username_other(other_username):
            assert username == 'other-directly-overridden-username-other'

在上边的例子中,fixture的值用测试的参数值进行了覆写。注意,fixture的值就算是没有被直接使用,也可以使用这种方法进行覆写。

用一个非参数化的fixture覆写参数化的fixture,或者反之亦可

例子如下:

tests/
    __init__.py

    conftest.py
        # content of tests/conftest.py
        import pytest

        @pytest.fixture(params=['one', 'two', 'three'])
        def parametrized_username(request):
            return request.param

        @pytest.fixture
        def non_parametrized_username(request):
            return 'username'

    test_something.py
        # content of tests/test_something.py
        import pytest

        @pytest.fixture
        def parametrized_username():
            return 'overridden-username'

        @pytest.fixture(params=['one', 'two', 'three'])
        def non_parametrized_username(request):
            return request.param

        def test_username(parametrized_username):
            assert parametrized_username == 'overridden-username'

        def test_parametrized_username(non_parametrized_username):
            assert non_parametrized_username in ['one', 'two', 'three']

    test_something_else.py
        # content of tests/test_something_else.py
        def test_username(parametrized_username):
            assert parametrized_username in ['one', 'two', 'three']

        def test_username(non_parametrized_username):
            assert non_parametrized_username == 'username'

在上边的例子中,在特定的测试模块中,一个参数化的fixture被一个非参数化的版本进行了覆写,一个非参数化的fixture被一个参数化的fixture进行了覆写,这个规则同样适用于测试目录级别的覆写。

最终,虽然略显粗糙,还是翻译完毕了,给自己规定了新的任务,希望自己一直做自己喜欢做的,有意义的事情。祝福大家一直开心。虽然,我们生活在一个或许并不开心的现实里。
面对着许多不开心的事情,睁开眼想着的,闭上眼梦着的。或许所有的这些不是现实强加给我们的,或许这是自己强加给自己的,因了那些数不清的贪念欲望,生活可以是这样,也可以是另外一种样子,人不应该攀比,保持一颗平常心,该是多么开心的一件事。
不是,自己高看了自己。而是,自己低看了自己。低看了这些年的经验所带给我们的财富,我们自己却不知道自己有多少的财富在聚集着,只是我们自己选择忽略不见而已。
一篇技术翻译博客,写的有点多了,是不是这锅汤的味道也会改变,管它呢,自己的世界何须别人置喙,我开心我自己就够了吧。啦啦啦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值