Silver Flash
Silver Flash

Reputation: 1071

Pytest how to call a function with fixture argument directly

I am building a test suite for a web app. I am using fixtures as below:

import pytest
from selenium import webdriver
from common import Common

@pytest.fixture(scope='module')
def driver(module, headless=True):
    opts = webdriver.FirefoxOptions()
    opts.add_argument('--headless') if headless else None
    driver = webdriver.Firefox(options=opts)
    driver.get('http://localhost:8080/request')
    yield driver
    driver.quit()

def test_title(driver):
    assert driver.title == 'abc'

if __name__ == '__main__':
    test_title() #what I need to execute to see if everything is fine

Suppose I need to see if my test_title function is doing what it needs to by running this module directly inside a if __name__ == '__main__':. How can I call test_title() with driver passed in as an argument?

calling test_title like below:

if __name__ == '__main__':
    test_title(driver(None, False))

python produces an error mentioned below:

(virtual) sflash@debian:~/Documents/php/ufj/ufj-test$ ./test_r*
Traceback (most recent call last):
  File "./test_request.py", line 30, in <module>
    test_empty_all(driver(None, headless=True))
  File "/home/sflash/Documents/php/ufj/ufj-test/virtual/lib/python3.7/site-packages/_pytest/fixtures.py", line 1176, in result
    fail(message, pytrace=False)
  File "/home/sflash/Documents/php/ufj/ufj-test/virtual/lib/python3.7/site-packages/_pytest/outcomes.py", line 153, in fail
    raise Failed(msg=msg, pytrace=pytrace)
Failed: Fixture "driver" called directly. Fixtures are not meant to be called directly,
but are created automatically when test functions request them as parameters.
See https://docs.pytest.org/en/stable/fixture.html for more information about fixtures, and
https://docs.pytest.org/en/stable/deprecations.html#calling-fixtures-directly about how to update your code.

Upvotes: 8

Views: 19802

Answers (1)

MrBean Bremen
MrBean Bremen

Reputation: 16815

As has been discussed in the comments, fixtures cannot be called directly - they only work together with pytest.

To invoke the test directly from your code, you can call pytest.main() in your __main__ section, which has the same effect as calling pytest on the command line. Any command line options can be added as arguments to the call (as a list), for example:

if __name__ == '__main__':
    pytest.main(['-vv', 'test.py::test_title'])

To use the driver without involving pytest (which was your intention) you have to extract the driver logic and call it separately both from the fixture and from main:


import pytest
from selenium import webdriver
from common import Common

def get_driver(headless=True):
    opts = webdriver.FirefoxOptions()
    opts.add_argument('--headless') if headless else None
    driver = webdriver.Firefox(options=opts)
    driver.get('http://localhost:8080/request')
    return driver
):

@pytest.fixture(scope='module')
def driver():
    yield get_driver()
    driver.quit()

def test_title(driver):
    assert driver.title == 'abc'

if __name__ == '__main__':
    driver = get_driver()
    test_title(driver) 
    driver.quit()

Note that this only works if the test function does not rely on any pytest-specific stuff (for example auto-applied fixtures).
Note also that you cannot use a parameter in your fixture as you did in your example, as you have no way to provide the parameter. Instead you can use parametrized fixtures.

Upvotes: 12

Related Questions