Модульное тестирование абстрактной фабрики, принимающей параметры
учитывая абстрактную реализацию фабрики:
public class FooFactory : IFooFactory {
public IFoo Create(object param1, object param2) {
return new Foo(param1, param2);
}
}
что модульные тесты будут написаны для этого класса? Как я могу проверить, что param1 и param2 были перенаправлены на создание Foo? Должен ли я сделать эти публичные свойства Foo? Разве это не было бы нарушением инкапсуляции? Или я должен оставить это интеграционному тестированию?
5 ответов
вот как я хотел написать пару тестов для такой фабрики (с 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
свойство слегка нарушать закон Деметра!--9-->, он не связывается с инвариантами класса, потому что он доступен только для чтения.
кроме того,Object1
свойство является частью конкретного класса Foo-не интерфейс IFoo:
public interface IFoo
{
void MethodDefinedByInterface();
}
как только вы поймете, что в слабо связанном API, конкретные члены детали реализации, например, бетон-только для чтения свойства имеют очень низкое влияние на инкапсуляции. Подумайте об этом так:
публичный конструктор Foo и часть API конкретного класса Foo, поэтому, просто проверяя публичный API, мы узнаем, что param1
и param2
являются частью класса. В некотором смысле это уже "разрывает инкапсуляцию", поэтому предоставление каждого параметра в качестве свойств только для чтения в конкретном классе не сильно меняется.
такие свойства обеспечивают преимущество что мы можем теперь модульный тест структурная форма класса Foo возвращенного фабрикой.
это намного проще чем повторять набор поведенческих модульных тестов, которые, мы должны предположить, уже охватывают конкретный класс Foo. Это почти как логическое доказательство:
- из тестов конкретного класса Foo мы знаем, что он правильно использует/взаимодействует со своими параметрами конструктора.
- из этих тестов мы также знаем, что параметр конструктора представляется как свойство только для чтения.
- из теста FooFactory мы знаем, что он возвращает экземпляр бетон класса Foo.
- кроме того, из тестов метода Create мы знаем, что его параметры правильно передаются конструктору Foo.
- В. Е. Д.
Ну, предположительно эти параметры делают возвращены IFoo
есть что-то истинное об этом. Проверьте, верно ли это для возвращаемого экземпляра.
вы можете сделать foofactory универсальным классом, который ожидает Foo в качестве параметра, и указать, что это макет. Вызываю фабрику.Создавать.(..) затем создает экземпляр макета, и вы можете подтвердить, что макет получил аргументы, которые вы ожидали.
фабрики обычно не проходят модульное тестирование, по крайней мере, согласно моему опыту - нет большой ценности в модульном тестировании их. Поскольку это реализация этого отображения интерфейса-реализации, следовательно, это относится к конкретной реализации. Как таковой, издеваясь Foo
невозможно проверить, получает ли он переданные параметры.
С другой стороны, обычно контейнеры DI работают как фабрики, поэтому, если вы используете один, вам не понадобится фабрика.
Это зависит от того, что Foo делает с 2 входными объектами. Проверьте что-то, что Фу делает с ними, что можно легко запросить. Например, если методы (включая геттеры или сеттеры) вызываются для объектов, укажите насмешки или заглушки, которые можно проверить, были ли вызваны ожидаемые вещи. Если в IFoo есть что-то, с чем можно протестировать, вы можете передать известные значения через ваши макетные объекты для тестирования после создания. Сами объекты не должны быть в открытом доступе, чтобы убедиться, что вы их пропустили.
Я также могу проверить, что возвращается ожидаемый тип (Foo), в зависимости от того, зависят ли другие тестируемые поведения от разницы между реализациями IFoo.
Если есть какие-либо условия ошибки, которые могут привести, я бы попытался заставить их также, что произойдет, если вы передадите null для любого или обоих объектов и т. д. Конечно, Foo будет тестироваться отдельно, поэтому вы можете предположить во время теста FooFactory, что Фу что надо.