Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking __init__() for unittesting

People also ask

What is mocking in unit testing Python?

What is mocking in Python? Mocking in Python means the unittest. mock library is being utilized to replace parts of the system with mock objects, allowing easier and more efficient unit testing than would otherwise be possible.

What is mocking in Pytest?

In pytest , mocking can replace the return value of a function within a function. This is useful for testing the desired function and replacing the return value of a nested function within that desired function we are testing.

What does mock patch do?

patch() unittest. mock provides a powerful mechanism for mocking objects, called patch() , which looks up an object in a given module and replaces that object with a Mock . Usually, you use patch() as a decorator or a context manager to provide a scope in which you will mock the target object.


Instead of mocking, you could simply subclass the database class and test against that:

class TestingDatabaseThing(DatabaseThing):
     def __init__(self, connection):
          self.connection = connection

and instantiate that class instead of DatabaseThing for your tests. The methods are still the same, the behaviour will still be the same, but now all methods using self.connection use your test-supplied connection instead.


You should use mock package to mock the method __init__ of the class:

from mock import patch


def test_database_thing(self):
    def __init__(self, dbName, user, password):
        # do something else
    with patch.object(DatabaseThing, '__init__', __init__):
        # assert something


Method 1: Subclass

Please refer to @Martijn Pieters' answer.

Method 2: Inversion of Control

A long term solution is to have the client create the connection and hand it over to DatabaseThing to use. Using the Single Responsibility principle, I don't think DatabaseThing should be responsible for making the connection in this case.

This technique cuts dependencies and gives you a lot more flexibility e.g. you can setup a connection pool and give each new instance of DatabaseThing a connection from the pool, without changing anything in DatabaseThing.


Rather than try to replace the init function which is messy, fragile and hacky, try passing a function to your database constructor as shown below:

# Test connection creation
def connect_lite(dbName=None, user=None, password=None):
    connection = lite.connect(":memory:")
    cur = self.connection.cursor()
    cur.executescript ('''CREATE TABLE APPLE (VERSION INT, AMNT SMALLINT);
                          INSERT INTO APPLE VALUES(16,0);
                          INSERT INTO APPLE VALUES(17,5);
                          INSERT INTO APPLE VALUES(18,1);
                          INSERT INTO APPLE VALUES(19,15);
                          INSERT INTO APPLE VALUES(20,20);
                          INSERT INTO APPLE VALUES(21,25);''')
    return cur


# Production connection creation
def connect_ibm(dbName, user, password):
    return ibm_db_dbi.connect(dbName, user, password)

# Your DatabaseThing becomes:
class DatabaseThing():
    def __init__(self, connect, dbName, user, password):
        self.connection = connect(dbName, user, password)

# In your test create a DatabaseThing
t = DatabaseThing(connect_lite, dbName, user, password)

# In your production code create a DatabaseThing
p = DatabaseThing(connect_ibm, dbName, user, password)      

This has the side benefit of slightly decoupling your code from the database technology you are using.