Suppose I design a class, called A, in its own module. Consider an abbreviation of what the code might look like.
# file: A.py
class A:
# define some useful behaviour for A class
pass
Then I go on to perform some unit tests for this new class. The tests would be roughly be carried out as shown below.
The reason for defining the create_test_A function outside of the TestA class will become apparent below.
# file test_A.py
import A
import unittest
def create_test_A():
# initialize into a useful test state before returning
return A.A()
class TestA(unittest.TestCase):
def test(self):
new_A = create_test_A()
# write some clever assertions about behaviour of new_A
pass
Now suppose I build a new class, which is designed to work directly with instantiations of the A class.
class A_bundle:
def __init__(self):
# define some fields that work with objects of type <class A>
pass
When I go to write unit tests for this new class, my first instinct would be to create an instance of it, and then create a few objects of type A to interact with it. Perhaps it would go like this.
#file test_A_bundle.py
import test_A # this is the part I feel weird doing
import A_bundle
import unittest
class TestABundle(unittest.TestCase):
def test_a_bundle(self):
# create a couple of test objects of type A
new_A1 = test_A.create_test_A()
new_A2 = test_A.create_test_A()
# make a new A_bundle to perform tests on
new_bundle = A_bundle.A_bundle()
# make some assertions about the way new_bundle
# should interact with new_A1 and new_A2
Have I now gone outside the scope of unit testing and into the world of integration testing, since I'm not just testing the behaviour of the A_bundle class independently?
And if so, it seems that I could still use the unittest module in this way and run some useful tests. Is this considered a bad practice? That is, using the unittest module to write and perform tests that are not, in fact, unit tests?
On my opinion you're still in the realm of unit tests, but I have a suggestion for you.
Insert instances of the class
A
as arguments of the__init__()
method of classbundle_A
.
Following this hint I have modified your file A_bundle.py
as following:
class A_bundle:
def __init__(self, a1, a2):
self.a1 = a1
self.a2 = a2
# define some fields that work with objects of type <class A>
def sum(self):
return self.a1.m_a() + self.a2.m_a()
I have also defined the method sum()
in the class A_bundle
: this is an example of field that works with objects of type class A.
In the file A.py
I have define the attribute x=10
and the method m_a()
as followed:
# file: A.py
class A:
x = 10
def m_a(self):
return self.x
test_A_bundle.py
At the end I propose you a file test_A_bundle.py
which contains 2 methods:
test_a_bundle()
which creates 2 real instances of class A
, an instance of class A_bundle
and verifies the result of the method sum()
test_a_bundle_with_mock()
which substitutes the real instance of A
by 2 Mock object and decide the return value of the method m_a()
of this 2 Mock objectThe code of test_A_bundle.py
is the followed:
#file test_A_bundle.py
import test_A # this is the part I feel weird doing
import A_bundle
import unittest
from unittest import mock
import A
class TestABundle(unittest.TestCase):
#+++++ YOUR TEST METHOD +++++
def test_a_bundle(self):
# create a couple of test objects of type A
new_A1 = test_A.create_test_A()
new_A2 = test_A.create_test_A()
# make a new A_bundle to perform tests on (passes 2 instances of
# A to the __init__ method of A_bundle)
new_bundle = A_bundle.A_bundle(new_A1, new_A2)
self.assertEqual(20, new_bundle.sum())
def test_a_bundle_with_mock(self):
mock_a1 = mock.Mock()
mock_a2 = mock.Mock()
mock_a1.m_a.return_value = 20
mock_a2.m_a.return_value = 30
new_bundle = A_bundle.A_bundle(mock_a1, mock_a2)
self.assertEqual(50, new_bundle.sum())
if __name__ == '__main__':
unittest.main()
The second method shows you how to test the interaction between objects of class A
and object of class A_bundle
by unit test. On my opinion this test is not an integration test, it remains a unit test with tests the cooperation between classes.