I have a following simplified code example:
from urllib.parse import urlparse, parse_qs
from selenium import webdriver
import pytest
#pytest.fixture(scope="module")
def driver():
options = webdriver.ChromeOptions()
_driver = webdriver.Chrome(options=options)
yield _driver
_driver.close()
#pytest.mark.parametrize("instance_name", ["instance1", "instance2"])
class TestInstance:
#pytest.fixture
def authorization_code(self, instance_name, driver):
driver.get(f"https://{instance_name}.com")
...do some UI actions here
authorization_code = parse_qs(urlparse(redirected_url).query)["code"][0]
#pytest.fixture
def access_token(self, authorization_code):
...obtain access_token here using authorization code
return "access_token"
def test_case_1(self, access_token):
...do some API calls using access_token
def test_case_2(self, access_token):
...do some API calls using access_token
What I would like to do is to execute UI actions in the authorization_code function once and obtain one access_token per instance.
Currently my UI actions are executed for every test case, leading to the fact that UI actions actually execute 2 * 2 = 4 times.
Is it possible to do with pytest?
Or maybe I am missing something in my setup?
In general I would just change the fixture scope: currently it gets recreated every time it is called, hence the reuse of ui actions. This is by design to ensure fixtures are clean. If your fixture didn't depend on the function-level fixture instance you could just put scope="class". (See the docs on scopes).
In this case I'd be tempted to handle the caching myself:
import pytest
from datetime import datetime
#pytest.mark.parametrize("instance", ("instance1", "instance2"))
class TestClass:
#pytest.fixture()
def authcode(self, instance, authcodes={}, which=[0]):
if not instance in authcodes:
authcodes[
instance
] = f"authcode {which[0]} for {instance} at {datetime.now()}"
which[0] += 1
return authcodes[instance]
def test1(self, authcode):
print(authcode)
def test2(self, authcode):
print(authcode)
(which is just used to prove that we don't regenerate the fixture).
This feels inelegant and I'm open to better ways of doing it.
Related
I'm working on unit tests for a service I made that uses confluent-kafka. The goal is to test successful function calls, exception errors, etc. The problem I'm running into is since I'm instantiating the client in the constructor of my service the tests are failing since I'm unsure how to patch a constructor. My question is how do I mock my service in order to properly test its functionality.
Example_Service.py:
from confluent_kafka.schema_registry import SchemaRegistryClient
class ExampleService:
def __init__(self, config):
self.service = SchemaRegistryClient(config)
def get_schema(self):
return self.service.get_schema()
Example_Service_tests.py
from unittest import mock
#mock.patch.object(SchemaRegistryClient, "get_schema")
def test_get_schema_success(mock_client):
schema_Id = ExampleService.get_schema()
mock_service.assert_called()
The problem is that you aren't creating an instance of ExampleService; __init__ never gets called.
You can avoid patching anything by allowing your class to accept a client maker as an argument (which can default to SchemaRegistryClient:
class ExampleService:
def __init__(self, config, *, client_factory=SchemaRegistryClient):
self.service = client_factory(config)
...
Then in your test, you can simply pass an appropriate stub as an argument:
def test_get_schema_success():
mock_client = Mock()
schema_Id = ExampleService(some_config, client_factory=mock_client)
mock_client.assert_called()
Two ways
mock entire class using #mock.patch(SchemaRegistryClient) OR
replace #mock.patch.object(SchemaRegistryClient, "get_schema") with
#mock.patch.object(SchemaRegistryClient, "__init__")
#mock.patch.object(SchemaRegistryClient, "get_schema")
I'm trying to create a test suite with pytest and Selenium using Page Object Models for pattern designing. For using my page classes on my tests, I just imported them inside my TestClass __init__ method, since they need to instanced with a driver.
I know that, by default, pytest ignores classes with an __init__ method. I also know, by reading here that it's possible to configure where pytest collects tests. Is it also possible to make it consider class tests with __init__, instead of returning an "Empty Suite" error?
#pytest.fixture(scope="session")
def driver_init(request):
from selenium import webdriver
driver = webdriver.Chrome()
session = request.node
page = PageFunctions(driver)
login_page = LoginPage(driver)
registration_page = RegistrationPage(driver)
for item in session.items:
cls = item.getparent(pytest.Class)
setattr(cls.obj, "driver", driver)
setattr(cls.obj, "page", page)
setattr(cls.obj, "login", login_page)
setattr(cls.obj, "registration", registration_page)
Pytest and Unittest have some different conventions. Mixing the two in the same test function is usually worth avoiding.
If you're working exclusively with Pytest, you would pass in your fixtures as arguments to your test function, e.g.:
import pytest
from selenium import webdriver
#pytest.fixture
def driver():
driver = webdriver.Chrome()
return driver
def test_func(driver):
# `driver` is found by pytest in the fixture above and
# automatically passed in
request = ... # Instantiate your request (not in your included code)
session = request.node
page = PageFunctions(driver)
login_page = LoginPage(driver)
registration_page = RegistrationPage(driver)
# Make some assertions about your data, e.g.:
assert page is not None
You haven't included all of the object definitions/imports so it's hard to see what you're trying to accomplish with the test, but hopefully that gives you an idea of the pytest conventions.
I need to create a class that uses a fixture from conftest.py, and this class has a fixture that I need to use only once per test session.
I have two test classes that depend on this class that has a fixture.
Sample code in test_app.py:
#pytest.mark.usefixtures("driver_get")
class TestBase:
#pytest.fixture(scope="module", autouse=True)
def set_up(self):
# set up code.
# web page will load if this fixture is called
# uses self.driver, where driver was set in driver_get
class TestOne(TestBase):
def test_1(self):
# test code
# uses self.driver also in the test
class TestTwo(TestBase):
def test_2(self):
# test code
# uses self.driver also in the test
Sample code in conftest.py (follows https://dzone.com/articles/improve-your-selenium-webdriver-tests-with-pytest):
#pytest.fixture(scope="session")
def driver_get(request):
from selenium import webdriver
web_driver = webdriver.Chrome()
session = request.node
for item in session.items:
cls = item.getparent(pytest.Class)
setattr(cls.obj,"driver",web_driver)
yield
web_driver.close()
As you can see, conftest.py sets the driver as a class attribute, which is why I am applying the driver_get fixture to class TestBase, since I need to use the driver inside the class.
The problem is, once TestOne finishes, the web page will load again, and execute TestTwo, which means that the fixture set_up was executed again, which is not what I want (since I set set_up scope to module, so it should only really happen once).
I know there is a similar question asked here (py.test method to be executed only once per run), but the asker didn't have the constraint of needing TestBase to have a fixture applied to it as well.
I have thought of putting the fixture inside conftest.py, but I am not sure if it is possible given my constraint of the fixture needing to be inside a class, and executed only once.
Any help will be appreciated. Thank you!
In your code, your set_up fixture which is module scope is called before your TestBase resolves the driver_get fixture. Because of this, at set_up trying self.driver will raise an AttributeError: object has no attribute 'driver'.
One quick way to fix this in your example code is to refer to the driver_get fixture in your module set_up fixture like so:
class TestBase:
#pytest.fixture(scope="module", autouse=True)
def set_up(self, driver_get): # <-------------
self.driver.get("https://www.yahoo.com")
Another way to refer to fixtures is just by including the fixture name as an argument.
Personally I am not a huge fan of the approach you copied from that blog of setting a class attribute on the request node. You'd get an IDE warning about referring to self.driver. To me, it would be clearer to yield the driver out of driver_get and then within the test classes either set it to self in a setup style fixture or use it directly. Similar to something below.
#pytest.fixture(scope="session")
def driver_get():
from selenium import webdriver
web_driver = webdriver.Chrome()
yield web_driver
web_driver.close()
class TestClass:
#pytest.fixture(autouse=True)
def setup(self, driver_get):
self.driver = driver_get
def test_something(self):
self.driver.get("https://www.google.com")
But depending on what else needs to be setup, if you want to control it happening just once per session or module you would need to modify the approach a bit.
I have a class that I try to mock in tests. The class is located in server/cache.py and looks like:
class Storage(object):
def __init__(self, host, port):
# set up connection to a storage engine
def store_element(self, element, num_of_seconds):
# store something
def remove_element(self, element):
# remove something
This class is used in server/app.py similar to this one:
import cache
STORAGE = cache.Storage('host', 'port')
STORAGE.store_element(1, 5)
Now the problem arise when I try to mock it in the tests:
import unittest, mock
import server.app as application
class SomeTest(unittest.TestCase):
# part1
def setUp(self):
# part2
self.app = application.app.test_client()
This clearly does not work during the test, if I can't connect to a storage. So I have to mock it somehow by writing things in 'part1, part2'.
I tried to achieve it with
#mock.patch('server.app.cache') # part 1
mock.side_effect = ... # hoping to overwriting the init function to do nothing
But it still tries to connect to a real host. So how can I mock a full class here correctly? P.S. I reviewed many many questions which look similar to me, but in vain.
I am trying to test the following class using unittest and the mock library:
class Connection(object):
def __init__(self, cookie):
self.connect = None
self.session = Session()
self.session.load(cookie)
# do some stuff with self.session
self.some_info = self.session.data['the_info']
How could I test if when I create an instance of Connection, depending on the return of the Session instance, I assert if self.some_info is with the value I am expecting?
I wish to use the mock library. In its documentation I have an example of mocking chained calls (http://www.voidspace.org.uk/python/mock/examples.html#mocking-chained-calls), but it isn't very clear of how I can adapt it to my problem.
The Session.load(cookie) method sets some attributes in the Session instance. I would like to set this values fixed for my tests for every value of cookie.
Assume Connection is located in module package.module.connection
The following code should be how you would test your session:
import mock
class TestConnection(unittest.TestCase):
#mock.patch('package.module.connection.Session')
def test_some_info_on_session_is_set(self, fake_session):
fake_session.data = {'the_info': 'blahblah'}
cookie = Cookie()
connection = Connection(cookie)
self.assertEqual(connection.some_info, 'blahblah')
fake_session.load.assert_called_once_with(cookie)