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
Post a Comment