Модульное тестирование абстрактной фабрики, которая принимает параметры - PullRequest
8 голосов
/ 03 февраля 2012

Учитывая абстрактную фабричную реализацию:

public class FooFactory : IFooFactory {
   public IFoo Create(object param1, object param2) {
      return new Foo(param1, param2);
   }
}

Какие модульные тесты будут написаны для этого класса?Как я могу проверить, что param1 и param2 были перенаправлены на создание Foo?Должен ли я сделать эти публичные свойства Foo?Разве это не нарушает инкапсуляцию?Или я должен оставить это для интеграционного тестирования?

Ответы [ 5 ]

11 голосов
/ 03 февраля 2012

Вот как я бы написал один из нескольких модульных тестов для такой фабрики (с xUnit.net ):

[Fact]
public void CreateReturnsInstanceWithCorrectParam1()
{
    var sut = new FooFactory();
    var expected = new object();
    var actual = sut.Create(expected, new object());
    var concrete = Assert.IsAssignableFrom<Foo>(actual);
    Assert.Equal(expected, concrete.Object1);
}

Это нарушает инкапсуляцию?Да и нет ... немного.Инкапсуляция - это не только сокрытие данных - что более важно, это защита инвариантов объектов .

Предположим, что Foo предоставляет этот публичный API:

public class Foo : IFoo
{
    public Foo(object param1, object param2);

    public void MethodDefinedByInterface();

    public object Object1 { get; }
}

Хотясвойство Object1 слегка нарушает закон Деметры , оно не мешает инвариантам класса, поскольку доступно только для чтения.

Кроме того, свойство Object1 является частьюконкретного класса Foo, а не интерфейса IFoo:

public interface IFoo
{
    void MethodDefinedByInterface();
}

Как только вы поймете, что в слабосвязанном API, конкретные элементы являются деталями реализации , такие конкретные только для чтения свойстваимеют очень низкое влияние на инкапсуляцию.Подумайте об этом следующим образом:

Публичный конструктор Foo также является частью API конкретного класса Foo, поэтому, изучая общедоступный API, мы узнаем, что param1 иparam2 являются частью класса.В некотором смысле это уже «нарушает инкапсуляцию», поэтому предоставление каждого параметра в качестве свойств, доступных только для чтения, для конкретного класса практически не меняется.

Такие свойства обеспечивают то преимущество, что теперь мы можем проводить модульное тестирование структурного элемента.Форма класса Foo, возвращаемая фабрикой.

Это гораздо проще, чем повторять набор тестов поведенческих модулей, которые, как мы должны предположить, уже охватывают конкретный класс Foo.Это почти как логическое доказательство:

  1. Из тестов конкретного класса Foo мы знаем, что он правильно использует / взаимодействует с параметрами своего конструктора.
  2. Из этих тестов мы также знаем, чтоПараметр конструктора предоставляется как свойство только для чтения.
  3. Из теста FooFactory мы знаем, что он возвращает экземпляр конкретного класса Foo.
  4. Кроме того, мы знаем из тестовМетод Create, чтобы его параметры правильно передавались в конструктор Foo.
  5. QED
2 голосов
/ 03 февраля 2012

Ну, предположительно, эти параметры заставляют возвращенные IFoo иметь что-то правдивое.Проверьте, что это правда о возвращенном экземпляре.

0 голосов
/ 03 февраля 2012

Это зависит от того, что Foo делает с двумя входными объектами.Проверьте, что Foo делает с ними, и к чему можно легко обратиться.Например, если к объектам вызываются методы (включая методы получения или установки), предоставьте имитации или заглушки, которые вы можете проверить, вызвав ожидаемые вещи.Если в IFoo есть что-то, что можно проверить, вы можете передать известные значения через фиктивные объекты для проверки после создания.Сами объекты не должны быть общедоступными, чтобы убедиться, что вы их пропустили.

Я мог бы также проверить, что ожидаемый тип (Foo) возвращается, в зависимости от того, зависят ли другие протестированные поведения от разницымежду реализациями IFoo.

Если есть какие-либо условия ошибки, которые могут привести к этому, я бы также попытался форсировать их, что произойдет, если вы передадите нулевое значение для одного или обоих объектов и т. д.Конечно, вы можете предположить во время теста FooFactory, что Foo делает то, что должен.

0 голосов
/ 03 февраля 2012

Фабрики обычно не проходят модульное тестирование, по крайней мере, по моему опыту - нет особой ценности в их модульном тестировании . Поскольку это реализация этого отображения реализации интерфейса, следовательно, оно относится к конкретной реализации. Таким образом, насмешка Foo не позволяет проверить, получает ли он те параметры, которые были переданы.

С другой стороны, обычно контейнеры DI работают как фабрики, поэтому, если вы используете их, вам не понадобится фабрика.

0 голосов
/ 03 февраля 2012

Вы можете сделать FooFactory универсальным классом, который ожидает Foo в качестве параметра, и указать, что он будет фиктивным. Вызов factory.Create (...) создает экземпляр макета, и вы можете подтвердить, что макет получил ожидаемые вами аргументы.

...