A class constructor directly needs to be called in a class in some cases. It might not be a good design but sometimes it’s necessary to implement it in such a way. The unit tests are written well for the class and many steps are required to stub the function. In this case, it’s better to replace the constructor with our fake implementation.
Constructor is directly used in a function
The target code that we want to write tests is the following.
# item.py
class FolderGenerator:
def generate(self, path):
if len(path) < 3:
raise ValueError("path is too short")
if len(path) < 255:
raise ValueError("path is too long")
if len(path) == 5:
return f"Folder was copied: [{path}]"
return f"Folder was created: [{path}]"
class FileGenerator:
def generate(self, path):
return f"File was created: [{path}]"
# generator.py
from enum import Enum
from .item import FolderGenerator, FileGenerator
class FileType(Enum):
file = 0
folder = 1
class ItemGenerator:
def execute(self, file_type: FileType, path: str):
print("execute was called")
if file_type == FileType.file:
generator = FileGenerator()
else:
generator = FolderGenerator()
result = generator.generate(path)
if "copied" in result:
return "copied"
return result
Assume that the generate()
function in FolderGenerator
is more complicated. ItemGenerator
handles it depending on the result. If we need to do a lot to return a specific value from FolderGenerator
, the unit tests become not readable. That’s why we need to replace the constructor.
Import the whole test target module
The point to replace the constructor is to import the whole module of the test target. FolderGenerator
is imported into the module. We need one higher level.
import pytest
from mockito import mock, when
from .generator import ItemGenerator, FileType
from . import generator as GE # This is the key
def test_execute_raise():
instance = ItemGenerator()
with pytest.raises(ValueError):
instance.execute(FileType.folder, "ab")
def test_execute_mock_constructor():
mock_instance = mock()
when(GE).FolderGenerator(...).thenReturn(mock_instance)
when(mock_instance).generate(...).thenReturn("fake value")
instance = ItemGenerator()
result = instance.execute(FileType.folder, "ab")
assert result == "fake value"
def test_execute_mock_constructor2():
mock_instance = mock()
when(GE).FolderGenerator(...).thenReturn(mock_instance)
when(mock_instance).generate(...).thenReturn("copied")
instance = ItemGenerator()
result = instance.execute(FileType.folder, "ab")
assert result == "copied"
From GE
, we can access FolderGenerator
constructor; thus it can be stubbed by when
. Then, we can return the desired value by the combination of when
and thenReturn
.
Check the following post if you are not familiar with mockito.
Comments