When unit testing class one should test only against public interface of its collaborators. In most cases, this is easily achieved replacing collaborators with fake objects – Mocks. When using dependency injection properly, this should be easy most times.
However, things get complicated when trying to test factory class. Let us see example
Module wheel
class Wheel:
"""Cars wheel"""
def __init__(self, radius):
"""Create wheel with given radius"""
self._radius = radius #This is private property
Module engine
class Engine:
"""Cars engine"""
def __init(self, power):
"""Create engine with power in kWh"""
self._power = power #This is private property
Module car
class Car:
"""Car with four wheels and one engine"""
def __init__(self, engine, wheels):
"""Create car with given engine and list of wheels"""
self._engine = engine
self._wheels = wheels
Now let us have CarFactory
from wheel import Wheel
from engine import Engine
from car import Car
class CarFactory:
"""Factory that creates wheels, engine and put them into car"""
def create_car():
"""Creates new car"""
wheels = [Wheel(50), Wheel(50), Wheel(60), Wheel(60)]
engine = Engine(500)
return Car(engine, wheels)
Now I want to write an unit test for the CarFactory. I want to test, the factory creates objects correctly. However, I should not test the private properties of objects, because they can be changed in future and that would break my tests. Imagine, Wheel._radius replaced by Wheel._diameter or Engine._power replaced by Engine._horsepower.
So how to test the factory?
Fortunately in python testing factories is easy thanks to monkey_patching. You can replace not only instances of objects, but also whole classes. Let us see example
So we replace the classes and their constructors with Mock, that returns our fake instance. That enable us to check, the constructor was called with correct parametres and so we do not need to rely on real classes. We are really able in python to use not only fake instances, but also fake classes.
Also, I have to mention, the code above is not ideal one. For example, fake constructor should really create new Mock for each request, so we can check the car is called with correct wheels (correct order for example). This could be done, but the code would be longer and I wanted to keep the example as simple as possible.
In the example I have used Mock library for python http://www.voidspace.org.uk/python/mock/
But it is not necessary.