[파이썬] unittest mock 객체 사용법

Introduction

Unit testing is an essential part of software development, allowing us to verify the correctness of our code. In Python, the unittest module provides a robust framework for writing and running tests. One powerful feature of unittest is the ability to use mock objects to simulate dependencies and control the behavior of external resources. In this blog post, we will explore the usage of mock objects in unittest to improve the testing of our Python code.

Mock Objects

A mock object is a dummy object that mimics the behavior of a real object in controlled ways. Using a mock object allows us to isolate the code under test and write more focused and reliable unit tests. The unittest.mock module in Python’s standard library provides the Mock class, which we can use to create mock objects.

Installing the mock module

Before using the unittest.mock module, we need to ensure that it is installed. If you are using Python 3.3 or above, unittest.mock is included in the standard library, so no additional installation is required. For older versions of Python, you can install the mock module using pip:

pip install mock

Basic Usage

Let’s start by looking at some basic usage examples of mock objects in unittest.

Consider the following function, get_random_number(), which generates a random number:

import random

def get_random_number():
    return random.randint(1, 100)

To test this function, we can use a mock object to control the behavior of the random.randint() function:

import unittest
from unittest.mock import patch

class RandomNumberTestCase(unittest.TestCase):
    @patch('random.randint')
    def test_get_random_number(self, mock_randint):
        mock_randint.return_value = 42  # Set the mocked return value
        result = get_random_number()
        self.assertEqual(result, 42)
        mock_randint.assert_called_once_with(1, 100)  # Verify the mock was called with the correct arguments
        
if __name__ == '__main__':
    unittest.main()

In the example above, we used the @patch decorator from unittest.mock to patch the random.randint function with a mock object. Inside the test function, we set the return value of the mock object to 42, which allows us to control the behavior of the get_random_number() function. We then assert that the result of get_random_number() is equal to 42 and verify that the mock object was called once with the expected arguments.

Advanced Usage

In addition to controlling return values, mock objects can also be used to simulate exceptions, control method calls, and track usage. Here are some advanced usage examples:

Simulating Exceptions

@patch('requests.get')
def test_fetch_data(self, mock_get):
    mock_get.side_effect = ConnectionError("Unable to connect")
    with self.assertRaises(ConnectionError):
        fetch_data()  # Calls requests.get and raises ConnectionError

Controlling Method Calls

mock_obj = MagicMock()
mock_obj.sub_method.return_value = 10

# Test that the `main_method` calls the `sub_method` with the correct arguments
main_method(mock_obj)
mock_obj.sub_method.assert_called_once_with(42, "test")

Tracking Method Calls

mock_obj = MagicMock()
mock_obj.some_method(1)
mock_obj.some_method(2)
mock_obj.some_method(3)

# Assert the `some_method` was called three times with different arguments
self.assertEqual(mock_obj.some_method.call_count, 3)
mock_obj.some_method.assert_has_calls([call(1), call(2), call(3)])

Conclusion

Mock objects in unittest provide a powerful way to simulate dependencies, control external resources, and improve the reliability and focus of our unit tests. By using unittest.mock, we can write tests with more precision and confidence, ensuring the correctness of our Python code.