Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using the Unittest module in python to write non unit tests. Is it Bad practice?

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?

like image 801
doggo Avatar asked Oct 20 '25 19:10

doggo


1 Answers

On my opinion you're still in the realm of unit tests, but I have a suggestion for you.

The hint

Insert instances of the class A as arguments of the __init__() method of class bundle_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.

Changes to 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 methods in test_A_bundle.py

At the end I propose you a file test_A_bundle.py which contains 2 methods:

  • your method 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()
  • a second test method called 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 object

The 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.

like image 56
User051209 Avatar answered Oct 22 '25 08:10

User051209



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!