6

ユニットテストクラスの場合、共同作業者のパブリックインターフェイスに対してのみテストする必要があります。ほとんどの場合、これは共同作業者を偽のオブジェクト(モック)に置き換えることで簡単に実現できます。依存性注入を適切に使用する場合、これはほとんどの場合簡単です。

ただし、ファクトリクラスをテストしようとすると、事態は複雑になります。例を見てみましょう

モジュールwheel

class Wheel:
    """Cars wheel"""

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

         self._radius = radius #This is private property

モジュールengine

 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._radiusWheel._diameterEngine._powerくださいEngine._horsepower

では、工場をテストする方法は?

4

1 に答える 1

11

幸いなことに、Pythonのテストファクトリーはmonkey_patchingのおかげで簡単です。オブジェクトのインスタンスだけでなく、クラス全体を置き換えることができます。例を見てみましょう

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に置き換えます。これにより、偽のインスタンスが返されます。これにより、コンストラクターが正しいパラメーターで呼び出されたことを確認できるため、実際のクラスに依存する必要はありません。Pythonでは、偽のインスタンスだけでなく、偽のクラスも使用できます。

また、上記のコードは理想的なものではありません。たとえば、偽のコンストラクターは、リクエストごとに新しいモックを実際に作成する必要があるため、車が正しいホイールで呼び出されていることを確認できます(たとえば、正しい順序)。これは可能ですが、コードが長くなるため、例をできるだけ単純にしたいと思いました。

この例では、Python用のモックライブラリを使用しましたhttp://www.voidspace.org.uk/python/mock/

しかし、それは必要ではありません。

于 2012-09-02T16:00:19.233 に答える