w3resource

What is Mocking in Python testing and how does It work?

Mocking in Python testing

In Python - testing, mocking is a technique used to isolate dependencies in unit tests. It involves creating "mock" objects that simulate the behavior of real objects or components that the code being tested interacts with. During testing, developers can replace actual dependencies with controlled and predictable substitutes by using mock objects.

Purpose of Mocking in Unit Tests: Mocking is used to isolate code under test from external dependencies, such as databases, web services, APIs, or other complex components. When unit testing, you want to focus on testing the specific functionality of a single unit (e.g., a function or class) in isolation, without being affected by the behavior of external components. You can test code behavior under controlled conditions by mocking, which helps you achieve this isolation.

How Mocking Is Used in Unit Tests:

Here's how mocking is typically used in unit tests to isolate dependencies:

  • Creating Mock Objects: In Python, you can use "mocking" libraries like unittest.mock or third-party libraries like pytest-mock to create mock objects. These mock objects simulate the behavior of real objects and allow you to control their responses to method calls.
  • Replacing Dependencies: In the test setup, you replace the actual dependencies (e.g., database access, network requests) with mock objects. This is typically done by "patching" or "mocking" the dependencies with the corresponding mock objects.
  • Defining Mock Behaviors: For each mock object, you can specify the expected behavior, return values, or side effects that the code under test should encounter during the test.
  • Testing the Code: With the dependencies mocked, you can now execute the code under test in a controlled environment. The code interacts with the mock objects as if they were real dependencies.
  • Asserting Interactions: After running the code, you can use assertions to verify that the code correctly interacted with the mock objects. Check if specific methods were called, how many times they were called, and with what arguments.

Example of Mocking in Unit Tests: Consider a simple example where you have a function that retrieves data from a database and processes it:

Code:

# test.py
import sqlite3
def connect_to_database():
    # Connect to the SQLite database (replace 'your_database.db' with your database file path)
    conn = sqlite3.connect('your_database.db')
    return conn
def fetch_data_from_db():
    conn = connect_to_database()
    cursor = conn.cursor()
    # Assuming you have a table named 'your_table' with an 'integer_data' column
    cursor.execute('SELECT integer_data FROM your_table')
    data = cursor.fetchone()
    conn.close()
    return data[0] if data else None
def process_data_from_db():
    data = fetch_data_from_db()
    if data is not None:
        # Process the data and return the result (multiply by 2 in this example)
        result = data * 2
        return result
    else:
        return None

In this completion, we added the following components:

  • connect_to_database(): This function establishes a connection to the SQLite database. You should replace 'mydatabase.db' with the actual path to your SQLite database file.
  • fetch_data_from_db(): This function connects to the database, executes an SQL query to fetch the integer_data from the your_table, and returns the fetched data. The data is fetched using the fetchone() method, which retrieves the first row of the query result. If the data is not found or the table is empty, it returns None.
  • Processing Data: In the process_data_from_db() function, we first call fetch_data_from_db() to get the data from the database. In this example, we multiply the data by 2 if it is not None. Otherwise, we return None.

In the unit test, you want to isolate the function process_data_from_db() from the actual database access to ensure a controlled test environment. Here's how mocking can achieve this:

Code:

# test.py
import unittest
from unittest.mock import patch
import test  # Import the correct module
class TestProcessDataFromDB(unittest.TestCase):
    @patch('test.fetch_data_from_db')  # Patch the correct function
    def process_data_from_db(self, mock_fetch_data):
        mock_fetch_data.return_value = 5
        result = test.process_data_from_db()  # Call the correct function
        self.assertEqual(result, 10)
if __name__ == '__main__':
    unittest.main()

Output:

----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK

In this example, we use patch() from unittest.mock to create a mock object for the database.fetch_data() function. We then specify that the mock should return 5 when called. By doing this, we isolate the process_data_from_db() function from the actual database access and control the data it receives during testing.

As we have not inserted any record in 'your_table' it returns the above output.



Follow us on Facebook and Twitter for latest update.