Pytest setup generate product for all test - python

I am working on pytest API automation project, and I need to get a random product from the DB. Is there a way that I could use the same random product for all the test cases in my class? I am using a setup class method but it generates a different product every test. Thank you.
class TestCreateOrdersSmoke:
#classmethod
def setup(cls):
cls.products_db = ProductsDao()
cls.orders_db = OrdersDao()
cls.orders_helper = OrdersHelper()
#pytest.mark.tcid48
def test_create_order_as_guest(self):
random_product = self.products_db.select_random_product_from_db()
random_product_id = random_product[0]['ID']
UPDATE:
So i used a pytest session fixture like seggested and it works so thank you! but i want to make sure that this is right practice so here is the updated code:
class TestCreateOrdersSmoke:
#pytest.fixture(scope="session")
def helpers(self):
products_db = ProductsDao()
orders_db = OrdersDao()
orders_helper = OrdersHelper()
random_product = products_db.select_random_product_from_db()
yield {'products_db':products_db,
'orders_db':orders_db,
'orders_helper':orders_helper,
'random_product':random_product}
#pytest.mark.tcid48
def test_create_order_as_guest(self, helpers):
random_product = helpers['random_product']
random_product_id = random_product[0]['ID']
#pytest.mark.tcid88
def test_create_order_with_new_user(self, helpers):
random_product = helpers['random_product']
random_product_id = random_product[0]['ID']

As you say, you need a fixture for all methods in class, so you can use "class" or "session" scope fixture #pytest.fixture(scope="class").
Here some ways to organize the code with the "class" or "session" fixtures.
First way: fixture with "class" scope can be inside a class, example is the same as you did with "session" fixture:
class TestCreateOrdersSmoke:
#pytest.fixture(scope="class")
Second way: fixture with "class" scope can be outside of the class, so then you can use it in different classes, example:
import logging
#pytest.fixture(scope="class")
def h():
logging.info('h')
class TestOne:
def test_one_one(self, h):
logging.info('test_one_one')
def test_one_two(self, h):
logging.info('test_one_two')
class TestTwo:
def test_two_one(self, h):
logging.info('test_two_one')
Third way: you can annotate class with a fixture by marking #pytest.mark.usefixtrures('fixture_name') and you don't need to pass fixture to each method of the class, it will be passed automatically. Example:
import logging
#pytest.fixture(scope="class")
def h():
logging.info('h')
#pytest.mark.usefixtures('h')
class TestOne:
def test_one_one(self):
logging.info('test_one_one')
def test_one_two(self):
logging.info('test_one_two')
And you can try to use 'autouse' fixture param, so you don't need to pass fixture inside methods, or annotate class with it. Example:
#pytest.fixture(scope="class", autouse=True)
def h():
logging.info('h')
class TestOne:
def test_one_one(self):
logging.info('test_one_one')
def test_one_two(self):
logging.info('test_one_two')
But i don't recommend to use autouse, be aware from use it.

Related

Shorter option names for pytest fixture variants with long names

I am testing a function with several incoming datasets defined as fixtures, but the fixture names get quite cumbersome to distinguish them from one another.
#pytest.fixture()
def dataset_with_foo():
pass
#pytest.fixture()
def dataset_with_bar():
pass
#pytest.fixture()
def dataset_with_foo_and_bar():
pass
def test_something(dataset_with_foo_and_bar):
pass
Is there a way to define some kind of alias for the option name to be shorter? For instance, something like:
#usesfixture("dataset_with_foo_and_bar", option_name="dataset")
def test_something(dataset):
pass
Create a super fixture and helper function to get desired fixture with one fixture.
import pytest
#pytest.fixture
def super_fixture(fixture1,fixture2,fixture3):
local_vars = locals()
def helper(fixture_name):
return local_vars.get(fixture_name)
return helper
def test_a(super_fixture):
# getting fixture1
assert super_fixture("fixture1")
Ok, the best way I could find to do it is by using deferred parametrized fixtures:
#pytest.fixture()
def dataset(request):
mapping = {
"with-foo": create_dataset_with_foo(),
"with-bar": create_dataset_with_bar(),
"with-foo-and-bar": create_dataset_with_foo_and_bar(),
}
return mapping[request.param]
def create_dataset_with_foo():
pass
def create_dataset_with_bar():
pass
def create_dataset_with_foo_and_bar():
pass
#pytest.mark.parametrize("dataset", ["with-foo"], indirect=True)
def test_something(dataset):
pass
#pytest.mark.parametrize("dataset", ["with-foo-and-bar"], indirect=True)
def test_something(dataset):
pass
There has been other attempts using pytest-lazy-fixture or specialized decorator, but I find it a bit too hacky..
https://gist.github.com/wcooley/7472b8de6edb1e8ceda560843c0519a8

How do I share fixtures between classes in pytest?

I am trying to share class scope and method scope fixtures across different classes using pytest. Currently, I get a syntax error if I try to add a fixture from another class as shown below.
I am aware that one way to work around this is to not have the fixture enclosed by a class. However, enclosing the fixtures makes sense for what I am trying to do.
import pytest
class Test_example1(object):
#classmethod
#pytest.fixture(scope='class')
def example1_fixture(self):
print("example1_fixture setup")
yield
print("example1_fixture teardown")
def test_1(self, example1_fixture):
print("class example1::test_1")
# This works if I comment out the code causing errors
#pytest.fixture(scope='class')
def global_example_fixture():
print("global_example_fixture setup")
yield
print("global_example_fixture teardown")
class Test_example2(object):
#pytest.fixture(scope='class')
def example2_fixture(self):
print("example2_fixture setup")
yield
print("example2_fixture teardown")
# Results in fixture not found error
def test_1(self, example1_fixture):
print("class example2::test_1")
# Results in syntax error
def test_2(self, Test_example1.example1_fixture, global_example_fixture):
print("class example2::test_1")
# This works...
def test_2(self, example2_fixture, global_example_fixture):
print("class example2::test_2")
I expect to be able to call the class level and method level fixtures across classes.

How to share object from fixture to all tests using pytest?

What is the best way to define an object in a fixture with session scope and autouse=True, so it will be available to all tests?
#pytest.fixture(scope='session', autouse=True)
def setup_func(request):
obj = SomeObj()
Next thing, I want some magic that previously created obj will appear in each test context without the need of each test to define the setup_func fixture.
def test_one():
obj.do_something_fancy()
My recommendation would to add the fixture to conftest.py and make sure to return the object you want to produce from the fixture.
As noted, this makes "autouse" kind of useless.
In the root directory for your tests, add the fixture to a file named conftest.py:
#pytest.fixture(scope='session', autouse=True)
def someobj(request):
return SomeObj()
Any test file beneath the root file will have access to this fixture (for example test_foo.py):
def test_foo(someobj):
assert isinstance(someobj, SomeObj)
Another approach, would be to use a global variable defined in the same test or imported from a module.
For example in conftest.py:
someobj = None
#pytest.fixture(scope='session', autouse=True)
def prep_someobj(request):
someobj = SomeObj()
Then in your test:
from . import conftest
def test_foo():
assert isinstance(conftest.someobj, SomeObj)
In my opinion this is less readable and more cumbersome than the first method.
A more general pattern for this is to return locals() at the end of your conftest and you'll be able to easily reference anything created in the fixture.
conftest.py
#pytest.fixture(scope='session')
def setup_func(request):
obj1 = SomeObj()
obj2 = SomeObj()
return locals()
test_stuff.py
def test_one(setup_func):
setup_func['obj1'].do_something_fancy()
def test_two(setup_func):
setup_func['obj2'].do_something_fancy()
Another possibility is to wrap your tests in a class and use class variables to only define the object instance once. This assumes you are able to wrap all tests in a single class and so this answer may address a less general, but similar use case. For example,
class SomeObj():
"""This object definition may exist in another module and be imported."""
def __init__(self):
self.x = 5
def do_something_fancy(self, y):
return self.x * y
class TestX():
# Object instance to share across tests
someobj = SomeObj()
def test_x(self):
assert TestX.someobj.x == 5
def test_fancy(self):
fancy_factor = 10
result = TestX.someobj.do_something_fancy(fancy_factor)
assert result == 50

pytest 2.3 adding teardowns within the class

I'm researching new version of pytest (2.3) and getting very excited about the new functionality where you
"can precisely control teardown by registering one or multiple
teardown functions as soon as they have performed some actions which
need undoing, eliminating the no need for a separate “teardown”
decorator"
from here
It's all pretty clear when it's used as function, but how to use it in the class?
class Test(object):
#pytest.setup(scope='class')
def stp(self):
self.propty = "something"
def test_something(self):
... # some code
# need to add something to the teardown
def test_something_else(self):
... # some code
# need to add even more to the teardown
Ok, I got it working by having a 'session'-wide funcarg finalizer:
#pytest.fixture(scope = "session")
def finalizer():
return Finalizer()
class Finalizer(object):
def __init__(self):
self.fin_funcs = []
def add_fin_func(self, func):
self.fin_funcs.append(func)
def remove_fin_func(self, func):
try:
self.fin_funcs.remove(func)
except:
pass
def execute(self):
for func in reversed(self.fin_funcs):
func()
self.fin_funcs = []
class TestSomething(object):
#classmethod
#pytest.fixture(scope = "class", autouse = True)
def setup(self, request, finalizer):
self.finalizer = finalizer
request.addfinalizer(self.finalizer.execute)
self.finalizer.add_fin_func(lambda: some_teardown())
def test_with_teardown(self):
#some test
self.finalizer.add_fin_func(self.additional_teardown)
def additional_teardown(self):
#additional teardown
Thanks #hpk42 for answering e-mails and helping me get the final version.
NOTE: together with xfailing the rest of the steps and improved scenarios this now makes a pretty good Test-Step structure
Indeed, there are no good examples for teardown yet. The request object has a addfinalizer method. Here is an example usage:
#pytest.setup(scope=...)
def mysetup(request):
...
request.addfinalizer(finalizerfunction)
...
The finalizerfunction will be called when all tests withing the scope finished execution.

How to give default return value to a method that is called during a unit test? - Python

Here's an example class that simplifies what I have:
class.py
class MyClass(object):
#staticmethod
def getDictionary():
#some calculations, returns a dictionary
def checkConfiguration(self):
#some code
self.getDictionary()
#some other code
return value
And now I am making a unit test for checkConfiguration:
classTest.py
import class
import unittest
class TestMyClass(unittest.TestCase):
def setUp(self):
self.classTest = class.MyClass()
def test_CheckConfiguration(self):
#What to put here?
The original CheckConfiguration calls getDictionary. Is there a way to tell the test_CheckConfiguration(self) that if getDictionary is called, it should automatically return a dictionary I can type in?
Something like:
def test_CheckConfiguration(self):
if method getDictionary is called:
return {'a':123, 'b':22}
checkValue = self.classTest.checkConfiguration()
I know this is possible in Java, though I don't have enough experience in that nor this.
Thank you.
I think you need a mocking framework. I suggest PyMock.
Here's how you could use it:
classTest.py
import class
import pymock
import unittest
class TestMyClass(pymock.PyMockTestCase):
def setUp(self):
self.classTest = class.MyClass()
def test_CheckConfiguration(self):
self.override(self.classTest, 'getDictionary')
pymock.expectAndReturn(self.classTest.getDictionary(), {'a':123, 'b':22})
self.replay()
checkValue = self.classTest.checkConfiguration()
self.verify()
https://groups.google.com/forum/?fromgroups#!topic/comp.lang.python/WBhc1xAc8Hw suggests subclassing your class under test and overriding __getattribute__ to record each call in whatever manner you need. Not sure what else would work...

Categories