Is it possible to inherit setup() and tearDown() methods? - python-3.x

I work with automation primarily in C# / Java and have been looking into Python for its speed.
In C#, I can write a class that implements a WebDriver instance, along with [SetUp] and [TearDown] methods. Then, every class containing test cases can inherit from this class, so I do not need to write my SetUp and TearDown for every test class I write.
The other benefit of SetUp / TearDown fixture is that I can use same WebDriver instance throughout all tests. SetUp will create the WebDriver instance, and pass it into test case class, where test case can use to initialize PageObjects & perform clicks etc. When test is finished, WebDriver instance gets passed back into TearDown for cleanup. This is highly efficient and easy to use.
My issue: I do not understand the Python best-practice on how to implement this functionality.
I have read through Python unittest docs here and read up on Python multiple inheritance here with little luck. I have also read this SO discussion here, but it is 10+ years old and contains many polarizing opinions. I did use discussion as guidance to get started, but I do not want to blindly write code without understanding it.
I am hung up on the part about how to actually inherit setUp(), tearDown(), and my webdriver instance. I don't want to declare a new webdriver instance, and re-write setUp() and tearDown() methods for every single test class, as this seems inefficient.
Here's what I've tried:
This is SetUp / TearDown fixture which is meant to handle setup and teardown for ALL test cases, and also keep track of singleton WebDriver instance.
Project Directory Structure:
base_test_fixture.py
from selenium import webdriver
import unittest
class BaseTestFixture(unittest.TestCase):
driver = None
def setUp(self):
print("Running SetUp")
self.driver = webdriver.Chrome()
def tearDown(self):
print("Running Teardown")
self.driver.close()
self.driver.quit()
Here is test_webdriver.py:
import unittest
import BaseTestFixture
class TestWebDriver(BaseTestFixture.SetUpTearDownFixture):
def test_should_start_webdriver(self):
super.setUp()
print("Running test 1")
super.driver.get("https://www.google.com")
assert "Google" in self.driver.title
super.tearDown()
def test_should_navigate_to_stackoverflow(self):
super.setUp()
print("Running test 2")
super.driver.get("https://www.stackoverflow.com")
assert "Stack Overflow" in self.driver.title
super.teardown()
if __name__ == '__main__':
unittest.main()
Here's the error my class declaration is showing: AttributeError: module 'BaseTestFixture' has no attribute 'SetUpTearDownFixture'
Is it possible to implement a single WebDriver, setUp(), and tearDown() for all Python test cases?

You are very close. the Python convention is that your module should be named with underscores, so I would rename BaseTestFixture.py to base_test_fixture.py, and the class in the module would be the camelcase version of the module name.
that would give us, base_test_fixture.py:
from selenium import webdriver
from unittest import TestCase
class BaseTestFixture(TestCase):
and test_web_driver.py:
import unittest
from base_test_fixture import BaseTestFixture
class TestWebDriver(BaseTestFixture):
If you're still having trouble, the problem may be in the directory structure of your package, so share that with us by editing your question above to indicate the structure of your directory and files.
Also, within your test, since the test class inherits self.driver, you just have to refer to it as self.driver (no super.).
Also, setUp() and tearDown() are automatically called by unittest, so you don't have to call them explicitly.

Related

Class wide mock in pytest (for all methods in the whole TestClass)

I am unittesting my new librabry, which is basically database interface. Our apps use it to access our database. that means, I want to test all methods, but I do not want DB commands to be called for real. I only check if they are called with correct arguemnts.
For that purpose, I am mocking the database library. this is the actual code that DOES work:
import pytest
from unittests.conftest import PyTestConfig as Tconf
from my_lib.influx_interface import InfluxInterface
class TestInfluxInterface:
def test_connect(self, mocker):
"""
Create InfluxConnector object and call connect()
check if __init__() arguments are passed / used correctly
"""
influx_client = mocker.patch('my_lib.influx_interface.InfluxDBClient', autospec=True)
test_connector = InfluxInterface(Tconf.test_id)
# Call connect with no input (influx_client - should be called with no arguemnts
test_connector.connect()
influx_client.assert_called_once()
influx_client.reset_mock()
# Call connect with custom correct input (influx_client - should be called with custom values
test_connector.connect(Tconf.custom_conf)
influx_client.assert_called_once_with(url=Tconf.custom_conf["url"], token=Tconf.custom_conf["token"],
org=Tconf.custom_conf["org"], timeout=Tconf.custom_conf["timeout"],
debug=Tconf.custom_conf["debug"])
influx_client.reset_mock()
# Call connect with incorrect input (influx_client - should be called with default values
test_connector.connect(Tconf.default_conf)
influx_client.assert_called_once_with(url=Tconf.default_conf["url"], token=Tconf.default_conf["token"],
org=Tconf.default_conf["org"], timeout=Tconf.default_conf["timeout"],
debug=Tconf.default_conf["debug"])
Now, what I do next, is to add more methods into TestInfluxInterface class, which will be testing rest of the code. One test method for each method in my library. Thats how I usually do it.
The problem is, that there is a part of the code:
influx_client = mocker.patch('my_lib.influx_interface.InfluxDBClient', autospec=True)
test_connector = InfluxInterface(Tconf.test_id)
That will be same for every method. Thus I will be copy-pasting it over and over. As you can already see, thats not good solution.
In unittest, I would do this:
import unittest
import unittest.mock as mock
from unittests.conftest import PyTestConfig as Tconf
from my_lib.influx_interface import InfluxInterface
#mock.patch('my_lib.influx_interface.InfluxDBClient', autospec=True)
class TestInfluxInterface:
def setUp(self):
self.test_connector = InfluxInterface(Tconf.test_id)
def test_connect(self, influx_client):
"""
Create InfluxConnector object and call connect()
check if __init__() arguments are passed / used correctly
"""
# Call connect with no input (influx_client - should be called with no arguemnts
self.test_connector.connect()
influx_client.assert_called_once()
Than in each method, I would use self.test_connector to call whatever method I want to test, and check if it called correct influx_client method, with correct parameters.
However, we are moving from unittest to pytest. And while I am wrapping my head around pytest docs, reading stuff here and there, fixtures, mockers, etc..I cannot find out how to do this correctly.

Pytest -- class inheritance as a variable for setup

I have a setup class that has setup and teardown functions among other things. My test class inherits this setup class currently so that all tests are ran using those functions as well as access to some variables.
Here's what my setup class looks like:
class SetupClass1:
def setup(self):
...
def teardown(self):
...
Here's what my test class looks like:
class MyTestClass(SetupClass1):
def test_1(self):
...
def test_2(self):
...
I have another setup class, SetupClass2 with its own setup, teardown functions among other variables that are setup for tests to use. I was hoping to have it so that I could parameterize MyTestClass so take in both setup classes but haven't had much luck.
I was hoping to do something like this but it didn't work:
#pytest.mark.parameterize('setup_class', [SetupClass1, SetupClass2])
class MyTestClass(setup_class):
def test_1(self):
...
def test_2(self):
...
I'm not sure how to accomplish this or if I'm going about this the complete wrong way. There's a lot of tests that I'm working with so I was hoping to do minimal changes to accomplish this. I was reading into pytest.fixtures but I couldn't get them to work. Thanks in advance.

monkeypatch in python, leaking mocks to other tests causing them to fail

I am monkeypatching the other function calls while writing pytest unittests as below:
from _pytest.monkeypatch import MonkeyPatch
from third_party import ThirdParty
def test_my_func():
resp1= "resp1"
monkeypatch = MonkeyPatch()
def mock_other_method(*args, **kwargs):
return resp1
monkeypatch.setattr(ThirdParty, "other_method", mock_other_method)
assert ThirdParty().other_method() == "resp1"
# Some assertions
def test_my_func2():
monkeypatch = MonkeyPatch()
expected_result = "This is expected"
result_third_party = ThirdParty().other_method()
assert result_third_party == expected_result
where,
third_party.py has:
class ThirdParty:
def other_method(self):
return "This is expected"
These tests when ran independently run fine (I just wrote it, so there might be some syntax error). But when I run it as pytest -v, 2nd test will fail. The reason is that on calling other_method, it will return the mocked method: mock_other_method, and since the response is different it will fail. Please suggest some solution to this
monkeypatch is a pytest fixture and as such
not supposed to be imported. Instead, you have to provide it as an argument in the test functions. Pytest loads all fixtures at test start and looks them up by name, so the correct usage would be:
from third_party import ThirdParty
# no import from pytest internal module!
def test_my_func(monkeypatch):
resp1 = "resp1"
def mock_other_method(*args, **kwargs):
return resp1
monkeypatch.setattr(ThirdParty, "other_method", mock_other_method)
assert ThirdParty().other_method() == resp1
The monkeypatch fixture has function scope, meaning that the patching will be reverted after each test function automatically.
Note that using the internal pytest API (e.g. importing _pytest) is discouraged, both because it may change with a new version, and because there are more convenient and secure methods to use the features (and not last because these are documented). You should never bother about fixture cleanup yourself, if you use a fixture provided by pytest or a pytest plugin - it would be too easy to forget the cleanup and get unwanted side effects.
I found the solution by adding monkeypatch.undo() at the end of every test. This will prevent the monkeypatch() from leaking into other functions

Calling parent class function before executing every child class function in Python

Here is an idea of the code
File 1
class MyParentClass(unittest.TestCase):
def setUpClass(self):
# SuperClass is already defined in the file
obj = SuperClass()
self.exp_fn = obj.create_exp_fn()
# This doesn't work
def setUp(self):
# sets up app to open the screen on which tests run (~100 lines)
File 2
from File1 import MyParentClass
class MyChildClass(MyParentClass):
def test_func_1(self):
# Start execution of its test
self.exp_fn.run_an_attribute()
def test_func_2(self):
# Start execution of its test
self.exp_fn.run_another_attribute()
I have several functions in MyChildClass and several files containing classes like MyChildClass which inherits unittest.TestCase
I want setUp to be called before executing any function of classes like MyChildClass but I don't want to individually decorate each function of MyChildClass or use the same 100 line setUp() function of unittest in all the files of MyChildClass. It should be present in MyParentClass as shown above
To run the tests I need to setUp the app such as open a specific screen, login and open another set of windows before a test can start. Or else the test won't find the element its looking for coz the app wouldn't be on the desired screen.
And the tests start from outside the app always
Is there a way to achieve the same functionality of setUp() following the above conditions. I went through the source code of run() in unittest where they use a contextmanager to implement setUp() but didn't quite get it
Would be highly obliged if anyone can suggest a solution... Thanks!!

patching boto3.resource and resource.Table method for unit testing

Here is my class:
# Production.py
import boto3
class Production(object):
resource = boto3.resource('dynamodb', 'us-west-2')
def __init__(self):
self.table = Production.resource.Table('employee')
I am trying to test that resource.Table is called with arg 'employee'. I wrote a unit test for it
def test_init():
with patch('Production.boto3.resource') as resource:
mock_resource = MagicMock()
resource.return_value = mock_resource
pd = Production()
resource.assert_called_with('dynamodb', 'us-west-2')
table = resource.return_value.Table.return_value
table.assert_called_with('employee')
test_init()
But it doesn't seem to work... Can some one help me how to test this?
When you patch an object it mocks all of its methods for you. So (I didn't test the code but) I think just:
def test_resource_is_called_with_correct_params():
with patch('Production.boto3') as mock_boto:
Production()
mock_boto.resource.assert_called_once_with('dynamodb', 'us-west-2')
will do the first part of your test. I would then test the init function separately in another test, which is clearer easier and more simple (generally aim to test one thing per test):
def test_table_is_called_with_correct_params():
with patch('Production.boto3') as mock_boto:
Production()
mock_resource = mock_boto.resource.return_value
mock_resource.Table.assert_called_once_with('employee')
I would say a couple of things about this though:
It's nice to group your tests into a class which organises your tests. Also when you get subclass TestsCase you get a bunch of methods that come with it such as self.assertDictEqual that will provide good meaningful output and work well will test runners like nose2. So do something like:
class TestProduction(unittest.TestCase):
def test1():
pass
def test2():
pass
The stuff you are testing there is basically hard coded so these tests are not really meaningful. You are just testing that the language works. I would learn to test behaviour rather than implementation ... So what do you want you class to do? Sit down and think about it first before you write the class. Then you can write the specs out and use those specs to design your tests before you even start coding.

Resources