简体   繁体   English

如何在python中测试工厂?

[英]How to unit test factory in python?

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 模块wheel

class Wheel:
    """Cars wheel"""

     def __init__(self, radius):
         """Create wheel with given radius"""

         self._radius = radius #This is private property

Module engine 模块engine

 class Engine:
     """Cars engine"""

     def __init(self, power):
     """Create engine with power in kWh"""

         self._power = power #This is private property

Module car 模块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 现在让我们拥有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 . 现在我想为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 . 试想一下, Wheel._radius替换Wheel._diameterEngine._power替换Engine._horsepower

So how to test the factory? 那么如何测试工厂?

Fortunately in python testing factories is easy thanks to monkey_patching. 幸运的是,在python测试中,工作很容易归功于monkey_patching。 You can replace not only instances of objects, but also whole classes. 您不仅可以替换对象的实例,还可以替换整个类。 Let us see example 让我们看看例子

import unittest
import carfactory
from mock import Mock

def constructorMock(name):
    """Create fake constructor that returns Mock object when invoked"""
    instance = Mock()
    instance._name_of_parent_class = name
    constructor = Mock(return_value=instance)
    return constructor

class CarFactoryTest(unittest.TestCase):

    def setUp():
        """Replace classes Wheel, Engine and Car with mock objects"""

        carfactory.Wheel = constructorMock("Wheel")
        carfactory.Engine = constructorMock("Engine")
        carfactory.Car = constructorMock("Car")

    def test_factory_creates_car():
        """Create car and check it has correct properties"""

        factory = carfactory.CarFactory()
        car_created = factory.create_car()

        # Check the wheels are created with correct radii
        carfactory.Wheel.assert_called_with(radius=50)
        carfactory.Wheel.assert_called_with(radius=50)
        carfactory.Wheel.assert_called_with(radius=60)
        carfactory.Wheel.assert_called_with(radius=60)

        # Check the engine is created with correct power
        carfactory.Engine.assert_called_once_with(power=500)

        # Check the car is created with correct engine and wheels
        wheel = carfactory.Wheel.return_value
        engine = carfactory.Engine.return_value
        carfactory.Car.assert_called_once_with(engine, [wheel, wheel, wheel, wheel])

        # Check the returned value is the car created
        self.assertEqual(car_created._name_of_parent_class, "Car")

So we replace the classes and their constructors with Mock, that returns our fake instance. 所以我们用Mock替换类及其构造函数,返回我们的假实例。 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. 我们真的能够在python中不仅使用假实例,而且还使用假类。

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). 例如,假构造函数应该为每个请求创建新的Mock,因此我们可以检查汽车是否使用正确的轮子调用(例如正确的顺序)。 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/ 在示例中,我使用了Mock库来进行python http://www.voidspace.org.uk/python/mock/

But it is not necessary. 但这没有必要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM