Pytest Fixtures



Here, we will cover a very powerful feature of the pytest framework called fixtures.

Let’s  make a sample fake database class for demonstration purposes and there is mydb.py and test_mydb.py. This has a function, cursor, that subsequently has the ability to execute your database queries.

 

Class MyDB:

                Def __init__(self):

                                Self.connection = Connection()

                Def connect(self, connection_string):

                                Return self.connection

Class Connection:

                Def __init__(self):

                                Self.cur = Cursor()

                Def cursor(self):

                                Return self.cur

                Def close(self):

                                Pass

Class Cursor():

                Def execute(self, query):

                                If query == “select id from employee_db where name == John”:

                                                Return 123

                                Elif query == “select id from employee_db where name = Tom”:

                                                Return 789

                                Else:

                                                Return -1

                Def close(self):

                                Pass

 

Now, I want to write 2 tests: One to verify John’s employee id, and the other to verify Tom’s employee id. 

Let’s first write a unit test for John and tom, which are run as follows:

From fixtures.mydb import MyDB

                Def test_johns_id():

                                Db = MyDB()

                                Conn = db.connect(“server”)

                                Cur = conn.cursor()

                                Id = cur.execute(“select id from employee_db where name = Tom”)

                                Assert id == 123

                Def test_toms_id():

                                Db = MyDB()

                                Conn = db.connect(“server”)

                                Cur = conn.cursor()

                                Id = cur.execute(“select id from employee_db where name = Tom)

                                Assert id == 789

 

Now, what’s the problem with this approach? There are 2 main issues and one is we be writing the first 3 lines a thousand times. The second thing is we are create expensive Database connections in every test case.

The first thing we can try is the setup and teardown test methods. This means we initialize whatever we need for the test cases in the very beginning. But fixtures are much better than traditional setup and teardown.

First, we make the connection and cursor object remover and write down a setup module method. To do this, we basically first set all the variables to global. There is also a teardown method in which after test cases are finished we want to do some cleanup so we will do cursor close, followed by connection close.

 

From fixtures.mydb import MyDB

Conn = None

Cur = None

                Def setup_module(module):

                                Global conn

                                Global curr

                                Db = MyDB()

                                Conn = db.connect(“server”)

                                Cur = conn.cursor()

                Def teardown_module(module):

                                Cur.close()

                                Conn.close()

                Def test_johns_id():

                                Id = cur.execute(“select id from employee_db where name = Tom”)

                                Assert id == 123

                Def test_toms_id():

                                Id = cur.execute(“select id from employee_db where name = Tom)

                                Assert id == 789

It works if you execute pytest -v in the document. However, fixtures are still better. We first define the fixture, and what we need for each of these tests is the cursor and uses it as a fixture. In order to use this fixture, all we would do is call cursor as one of the parameters. It will call this function and return the objects on the bottom.

Import pytest

@pytest.fixture

Def cur():

                Db = MyDB()

                Conn = db.connect(“server”)

                Curs = conn.cursor()

                Return curs

Def test_johns_id(cur):

                Id = cur.execute(“select id from employee_db where name = John”)

                Assert id == 123

Def test_toms_id(cur):

                Id = cur.execute(“select id from employee_db where name = Tom”)

                Asser id == 789

This passes the test. Fixtures leverage a concept of a dependency injection. What I’m doing here is injecting the dependency cur using this particular fixture, and then we have to close the connection at the end. When we run tests on the fixtures and print on the fixture, we have to show things on STD out.

Pytest -v –capture=no

However, if we set as follows:

@pytest.fixture(scope = “module”) then the scope of the test is the module and everything is setup only once.

Let’s say we want to create the cursor object only once. We do the following to achieve this (use the yield keyword):

Def cur():

                Print(“setting up”)

                Db = MyDB()

                Conn = db.connect(“server”)

                Curs = conn.cursor()

                Yield curs

                Curs.close()

                Conn.close()

The test cases will close after all the tests are performed. Fixtures are recommended over using setup and teardown since they are much better than the setup and teardown methods. Please use these fixtures.


Comments

Popular Posts