如何在python中进行单元测试工厂?
当单元测试类1时,应该只针对其合作者的公共接口进行测试。在大多数情况下,用假对象(mock)替换协作者很容易实现这一点。当正确使用依赖项注入时,大多数情况下这应该很容易 然而,当尝试测试工厂类时,事情变得复杂了。让我们看一个例子如何在python中进行单元测试工厂?,python,unit-testing,dependency-injection,Python,Unit Testing,Dependency Injection,当单元测试类1时,应该只针对其合作者的公共接口进行测试。在大多数情况下,用假对象(mock)替换协作者很容易实现这一点。当正确使用依赖项注入时,大多数情况下这应该很容易 然而,当尝试测试工厂类时,事情变得复杂了。让我们看一个例子 import unittest import carfactory from mock import Mock def constructorMock(name): """Create fake constructor that returns Mock ob
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")
模块wheel
class Wheel:
"""Cars wheel"""
def __init__(self, radius):
"""Create wheel with given radius"""
self._radius = radius #This is private property
模块发动机
class Engine:
"""Cars engine"""
def __init(self, power):
"""Create engine with power in kWh"""
self._power = power #This is private property
模块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
现在让我们来看看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)
现在我想为CarFactory
编写一个单元测试。我想测试,工厂是否正确创建对象。但是,我不应该测试对象的私有属性,因为它们将来可以更改,这会破坏我的测试。想象一下,Wheel.\u radius
被Wheel.\u diameter
或引擎所取代。\u power
被引擎所取代。\u horspower
那么如何测试工厂呢?幸亏有了monkey_补丁,python测试工厂很容易。不仅可以替换对象的实例,还可以替换整个类。让我们看一个例子
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")
所以我们用Mock替换类及其构造函数,Mock返回我们的假实例。这使我们能够检查,构造函数是用正确的参数调用的,因此我们不需要依赖于真实的类。在python中,我们不仅可以使用假实例,还可以使用假类
另外,我必须提到,上面的代码不是理想的代码。例如,假构造函数应该为每个请求创建新的Mock,这样我们就可以检查调用的汽车是否有正确的轮子(例如,正确的顺序)。这是可以做到的,但代码会更长,我希望示例尽可能简单
在这个示例中,我使用了python的模拟库
但这不是必须的。为什么要编写Java并将其称为Python?不需要CarFactory类:create\u car
应该是一个独立的函数。在Python中我们没有私有变量。@Daniel Roseman当我说私有时,我的意思是私有作为内部变量(不是api的一部分)。我并不是说作为语言结构的私有性。关于汽车工厂是一门课,你能解释一下为什么它是错的吗?我是python新手,所以很抱歉我不够pythonic。如果不封装任何数据,就不需要创建类。类不仅仅是放置函数的地方:这就是模块的用途。将create_car
作为模块级独立功能。还要注意的是,没有必要将车轮
、汽车
和发动机
放在单独的模块中。@Daniel Roseman:我想你可能把问题中使用的琐碎示例代码与问题本身混淆了。OP的方法论IMHO没有本质上的错误。对于非常简单的模块和类,它可能是不必要的,但在应用于大型复杂问题时,它可能会被证明是有用的。值得一提的是,Mock
不是一个标准库(以及从哪里获得它)。否则+1。