2017-10-16 52 views
1

所以我想實現的是用pytest自動模擬各個模塊中的函數。所以,我在conftest.py定義是:如何根據測試的燈具簽名動態地添加新的燈具到測試

import sys 
import __builtin__ 
from itertools import chain 


# Fixture factory magic START 
NORMAL_MOCKS = [ 
    "logger", "error", "logging", "base_error", "partial"] 
BUILTIN_MOCKS = ["exit"] 


def _mock_factory(name, builtin): 
    def _mock(monkeypatch, request): 
     module = __builtin__ if builtin else request.node.module.MODULE 
     ret = Mock() 
     monkeypatch.setattr(module, name, ret) 
     return ret 
    return _mock 


iterable = chain(
    ((el, False) for el in NORMAL_MOCKS), 
    ((el, True) for el in BUILTIN_MOCKS)) 
for name, builtin in iterable: 
    fname = "mock_{name}".format(name=name) 
    _tmp_fn = pytest.fixture(name=fname)(_mock_factory(name, builtin)) 
    _tmp_fn.__name__ = fname 
    setattr(
     sys.modules[__name__], 
     "mock_{name}".format(name=name), _tmp_fn) 
# Fixture normal factory magic END 

這個工作和一切,但我想省略NORMAL_MOCKSBUILTIN_MOCKS列表的使用。所以基本上在一個pytest鉤子我應該能夠看到,說有一個mock_foo夾具,但它尚未註冊,所以我創建一個工廠模擬並註冊它。我只是無法弄清楚如何做到這一點。基本上我正在研究pytest_runtest_setup函數,但無法弄清楚如何進行實際的燈具註冊。所以基本上我想知道哪個鉤子/調用可以從這個鉤子編程地註冊新的fixture函數。

+0

基於@謝爾蓋 - 瓦西里耶夫的意見,最終的解決方案可以在這裏找到 - > https://gist.github.com/dpapp-hortonworks/6224068ffc11d18c500b75a861941dfb –

回答

0

的方法之一是進行參數在收集/生成階段的試驗中,即在測試前開始執行:https://docs.pytest.org/en/latest/example/parametrize.html

# conftest.py 
import pytest 

def mock_factory(name): 
    return name 

def pytest_generate_tests(metafunc): 
    for name in metafunc.fixturenames: 
     if name.startswith('mock_'): 
      metafunc.parametrize(name, [mock_factory(name[5:])]) 

# test_me.py 
def test_me(request, mock_it): 
    print(mock_it) 

一個非常簡單的解決方案。但不足之處是作爲參數化的測試報告,而實際上並非如此:

$ pytest -s -v -ra 
====== test session starts ====== 

test_me.py::test_me[it] PASSED 

====== 1 passed in 0.01 seconds ====== 

要完全模擬函數參數不參數化,你可以做一個不太明顯的絕招:

# conftest.py 
import pytest 

def mock_factory(name): 
    return name 

@pytest.hookimpl(hookwrapper=True) 
def pytest_runtest_protocol(item, nextitem): 
    for name in item.fixturenames: 
     if name.startswith('mock_') and name not in item.funcargs: 
      item.funcargs[name] = mock_factory(name[5:]) 
    yield 

pytest_runtest_setup鉤也是一個很好的地方,只要我剛剛嘗試過。

注意你沒有註冊夾具的情況下。夾具登記太晚了,因爲所有的夾具都在收集/參數化階段早得多地收集和準備。在這個階段,您只能執行測試並提供值。您有責任計算燈具數值並在之後銷燬。

+0

這是一個非常好的解決方案。謝謝!(由於名譽低,無法使用此帳戶,因此我會以普通帳戶進行投票)。是否可以訪問'pytest_generate_tests'函數中的'request'對象。我想在一個特定的模塊中模擬函數,我在一個測試模塊中定義了一個變量。 –

+0

我意識到https://docs.pytest.org/en/latest/parametrize.html#the-metafunc-object有一個模塊成員,這是我需要從請求對象。謝謝您的幫助! –