Как создать unit test создание экземпляра?

У меня есть класс Carpenter, который работает с использованием объектов Lathe и Wood.

class Carpenter
{
    function Work()
    {
        $tool = new Lathe();
        $material = new Wood();
        $tool->Apply($material);
    }
}

Lathe зависит от интерфейса с именем Material, поэтому я могу легко unit test Lathe дать ему подделку Material в моем unit test. Wood не зависит от чего-либо, поэтому его также можно легко протестировать.

interface Material {
    // Various methods...
}

interface Tool {
    function Apply(Material $m);
}

class Wood implements Material  {
    // Implementations of Material methods
}

class Lathe {
    function Apply(Material $m) {
        // Do processing
    }
}

Однако Carpenter зависит от конкретных классов Lathe и Wood, потому что он должен создавать экземпляры из них. Это означает, что, поскольку он в настоящее время стоит, я не могу unit test метод Work() без непреднамеренного тестирования Lathe и Wood.

Как мне изменить свой дизайн на unit test Carpenter?

Ответ 1

Здесь есть несколько разных направлений:

  • Используйте Инъекции конструктора и просто вставляйте инструмент и экземпляры материалов в плотника.
  • Если инъекции экземпляров не работают по какой-либо причине (возможно, потому, что вам нужно создавать новые экземпляры для каждого вызова метода Work), вы можете добавить Абстрактные фабрики.
  • Вы также можете использовать метод Factory Метод, описанный ctford, но для этого требуется, чтобы вы также создавали переопределения для тестирования, чтобы иметь возможность unit test, и хотя это полностью действительная вещь сделать это, это просто больше работы, и во многих случаях другие альтернативы лучше и гибче.

Ответ 2

Ключ состоит в том, чтобы отделить создание объекта от использования объекта в Carpenter.

class Carpenter
{
    function getTool() {
        return new Lathe();
    }
    function getMaterial() {
        return new Wood();
    }
    function Work()
    {
        $tool = getTool();
        $material = getMaterial();
        $tool->Apply($material);
    }
}

Таким образом, вы можете переопределить методы getTool() и getMaterial() в классе TestCarpenter и ввести свои подделки Material и Tool.

Методы getTool() и getMaterial() также могут быть протестированы отдельно.

Michael Feathers будет называть методы getTool() и getMaterial() "швами", потому что они являются точками, где подделки можно вставлять без изменение окружающего кода.