web-dev-qa-db-ja.com

pytest->クラスのテストメソッドでフィクスチャの戻り値を使用する方法

次のような値を返すフィクスチャがあります。

import pytest

@pytest.yield_fixture(scope="module")
def oneTimeSetUp(browser):
    print("Running one time setUp")
    if browser == 'firefox':
        driver = webdriver.Firefox()
        print("Running tests on FF")
    else:
        driver = webdriver.Chrome()
        print("Running tests on chrome")
    yield driver
    print("Running one time tearDown")

このフィクスチャは、コマンドラインオプションを読み取っている別のフィクスチャからブラウザ値を取得します。

次に、複数のテストメソッドがあり、それらすべてが同じ戻り値ドライバーを使用してテストを続行するテストクラスがあります。

import pytest

@pytest.mark.usefixtures("oneTimeSetUp")
class TestClassDemo():

    def test_methodA(self):
        # I would like to use the driver value here
        # How could I do this?
        # Something like this
        self.driver.get("https://www.google.com")
        self.driver.find_element(By.ID, "some id")
        print("Running method A")

    def test_methodB(self):
        print("Running method B")

Self.driverの使用はエラーメッセージで失敗します

self = <test_class_demo.TestClassDemo object at 0x102fb6c18>

    def test_methodA(self):
>       self.driver.get("https://www.google.com")
E           AttributeError: 'TestClassDemo' object has no attribute 'driver'

フィクスチャを使用するすべてのメソッドに引数として渡すことができることは承知していますが、すべてのメソッドでこれが必要であり、クラスに渡してから使用できるはずなので、これは最善の方法ではありません。すべてのテスト方法でそれ。

ドライバーオブジェクトをメソッドで使用できるようにするための最良の方法は何ですか?

編集1:

提案されているように、conftest.pyにこのようなフィクスチャを作成しました

@pytest.yield_fixture(scope="class") # <-- note class scope
def oneTimeSetUp(request, browser): # <-- note the additional `request` param
    print("Running one time setUp")
    if browser == 'firefox':
        driver = webdriver.Firefox()
        driver.maximize_window()
        driver.implicitly_wait(3)
        print("Running tests on FF")
    else:
        driver = webdriver.Chrome()
        print("Running tests on chrome")

    ## add `driver` attribute to the class under test -->
    if request.cls is not None:
        request.cls.driver = driver
    ## <--

    yield driver
    print("Running one time tearDown")

TestClassDemoで必要なオブジェクトがもう1つあり、同じドライバーインスタンスをクラスに渡す必要があります。クラスABCと見なしてください

class ABC():

    def __init(self, driver):
        self.driver = driver

    def enterName(self):
        # Do something with driver instance

次に、TestClassDemoで

@pytest.mark.usefixtures("oneTimeSetUp", "setUp")
class TestClassDemo(unittest.TestCase):

    # I need to create an object of class ABC, so that I can use it here
    # abc = ABC(self.driver)

    @pytest.fixture(scope="class", autouse=True)
    def setup(self):
        self.abc = ABC(self.driver)
    # I tried this, but it's not working
    # This error message shows up
    # AttributeError: 'TestClassDemo' object has no attribute 'driver'

    def setup_module(self):
    self.abc = ABC(self.driver)
    # This also does not work
    # Error message ->  AttributeError: 'TestClassDemo' object has no attribute 'abc'


    def test_methodA(self):
        self.driver.get("https://google.com")
        self.abc.enterName("test")
        print("Running method A")

    def test_methodB(self):
        self.abc.enterName("test")
        print("Running method B")

このabcオブジェクトは、他のtest_メソッドでも使用できるはずです。

これらのクラスはすべて別々のモジュールにあります。つまり、別々の.pyファイルにあります。

また、yieldドライバーインスタンスの代わりに使用する最良の方法を回答で説明してください。

編集2:

歩留まりのないこの例では、oneTimeTearDownを実行するための最良の方法は何でしょうか?イールド後にtearDownステップを実行していました

@pytest.fixture(scope="class")
def oneTimeSetUp(request, browser):
    print("Running one time setUp")
    if browser == 'firefox':
        driver = webdriver.Firefox()
        driver.maximize_window()
        driver.implicitly_wait(3)
        print("Running tests on FF")
    else:
        driver = webdriver.Chrome()
        print("Running tests on chrome")

    if request.cls is not None:
        request.cls.driver = driver

また、UnitTestクラスを使用しようとしましたが、def setUpClass(cls)を使用すると、test_メソッドでインスタンス化されたオブジェクトを使用できませんでした。だから私はそれを達成する方法を理解することができませんでした。

また、コマンドラインからブラウザーのようなコマンドライン引数を提供したかったので、unittestを試したときに、すべてのクラスでコマンドライン引数を作成する必要がありました。テストスイートのように、1か所だけで提供したかったのです。だから、conftestはここで私を助けました。

Stackoverflowについて質問がありましたが、応答がありませんでした。それも見ていただけませんか? Pythonユニットテストが親テストクラスに引数を渡す

ありがとう

ありがとう

7
Sun Shine

py.text unittest統合ドキュメント に概説されているテクニックがあります。これは、組み込みのrequestフィクスチャを使用すると役立つ場合があります。それ以外の場合、メソッドparamとして名前付きフィクスチャを指定せずに、フィクスチャの戻り値にアクセスする方法がわかりません。

@pytest.yield_fixture(scope="class") # <-- note class scope
def oneTimeSetUp(request, browser): # <-- note the additional `request` param
    print("Running one time setUp")
    if browser == 'firefox':
        driver = webdriver.Firefox()
        print("Running tests on FF")
    else:
        driver = webdriver.Chrome()
        print("Running tests on chrome")

    ## add `driver` attribute to the class under test -->
    if request.cls is not None:
        request.cls.driver = driver
    ## <--

    yield driver
    print("Running one time tearDown")

これで、例のように、driverのクラス属性としてTestClassDemoにアクセスできます(つまり、self.driverが機能するはずです)。

注意点は、フィクスチャがscope='class'を使用する必要があることです。そうしないと、requestオブジェクトはcls属性を持たなくなります。

お役に立てば幸いです。


[〜#〜]更新[〜#〜]

TestClassDemoで必要なオブジェクトがもう1つあり、同じドライバーインスタンスをクラスに渡す必要があります。クラスABCと見なしてください

これ以上のコンテキストがないと知るのは難しいですが、ABCフィクスチャでdriver...をインスタンス化すると同時にoneTimeSetUpオブジェクトをインスタンス化することでおそらく逃げることができるようです。例えば ​​...

@pytest.yield_fixture(scope="class")
def oneTimeSetUp(request, browser):
    print("Running one time setUp")
    if browser == 'firefox':
        driver = webdriver.Firefox()
        driver.maximize_window()
        driver.implicitly_wait(3)
        print("Running tests on FF")
    else:
        driver = webdriver.Chrome()
        print("Running tests on chrome")

    if request.cls is not None:
        request.cls.driver = driver
        request.cls.abc = ABC(driver) # <-- here

    yield driver
    print("Running one time tearDown")

ただし、テストクラスにABCインスタンスのみが必要な場合は、クラス定義内でフィクスチャを使用する方法を次に示します...

@pytest.mark.usefixtures("oneTimeSetUp", "setUp")
class TestClassDemo(unittest.TestCase):
    @pytest.fixture(autouse=True)
    def build_abc(self, oneTimeSetUp): # <-- note the oneTimeSetup reference here
        self.abc = ABC(self.driver)

    def test_methodA(self):
        self.driver.get("https://google.com")
        self.abc.enterName("test")
        print("Running method A")

    def test_methodB(self):
        self.abc.enterName("test")
        print("Running method B")

2番目の例には特に満足していません。 3番目のオプションは、oneTimeSetUpとは完全に分離され、ドライバーが既にラップされているABCインスタンスを返す別のyield_fixtureなどを用意することです。

どちらの方法があなたに最適ですか?わからない。何を扱っているかに基づいて決定する必要があります。

後世のために、最も魅力的な備品は単なる砂糖とちょっとした魔法であることに注意するのが適切です。難しい場合は、使用する必要はありません。 pytestは、Vanilla unittestTestCasesを実行できます。


また、yieldドライバーインスタンスの代わりに使用する最良の方法を回答で説明してください。

これが私が考えていたものです...

@pytest.fixture(scope="class")
def oneTimeSetUp(request, browser):
    print("Running one time setUp")
    if browser == 'firefox':
        driver = webdriver.Firefox()
        driver.maximize_window()
        driver.implicitly_wait(3)
        print("Running tests on FF")
    else:
        driver = webdriver.Chrome()
        print("Running tests on chrome")

    if request.cls is not None:
        request.cls.driver = driver

...これはドライバーオブジェクトを返さない(または生成しない)ことに注意してください。つまり、このフィクスチャを名前付きパラメーターとして関数/メソッドに提供することはもはや有用ではありません。これは、すべてのテストケースがクラスとして書かれています(あなたの例によって提案されています)。

ただし、フィクスチャを名前付きパラメータとして使用する場合は、これを行わないでください。

6
Marty