Пустые параметры модульного теста в конструкторе
Я закодировал конструктор для класса, и я тестирую для каждого параметра, являющегося нулевым. Смотрите пример ниже:
public MyClass(IObjectA objA, IObjectB objB) : IMyClass
{
if (objA == null)
{
throw new ArgumentNullException("objA");
}
if (objB == null)
{
throw new ArgumentNullException("objB");
}
...
}
Обычно я тестирую модуль (используя Moq), отсекая IObjectA
а также IObjectB
и передать их. В приведенном выше примере будут созданы 2 модульных теста для тестирования каждого сценария.
У меня проблема, когда третий параметр передается в конструктор. Это требует, чтобы я изменил свои предыдущие тесты, поскольку я внезапно получаю исключение типа "Нет конструктора для MyClass имеет 2 параметра".
Я также использую AutoMockContainer. По сути, я хотел бы иметь возможность проверить конструктор, зарегистрировав нулевой объект в контейнере. Например:
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ConstructionThrowsExceptionForNullObjA()
{
// Arrange.
var container = new AutoMockContainer(new MockRepository(MockBehavior.Default));
container.Register<IObjectA>(null);
// Act.
var sut = container.Create<MyClass>();
}
Тогда не имеет значения, сколько новых параметров добавлено в конструктор. Мне не придется обновлять свои юнит-тесты.
К сожалению, вышеупомянутый модульный тест проходит. Но по неправильной причине. Register<T>()
метод бросает ArgumentNullException
не код, выполненный в разделе "Акт".
Есть ли у кого-нибудь предложение для возможности тестирования параметров конструктора и не нужно ли повторно посещать модульный тест, когда новые параметры добавляются позже?
1 ответ
Вы можете помочь облегчить часть этого бремени, используя шаблоны фабрики или производителя для создания ваших объектов.
Упрощенный пример шаблона построителя будет:
public class Foo
{
public string Prop1 { get; private set; }
public Foo(string prop1)
{
this.Prop1 = prop1;
}
}
[TestClass]
public class FooTests
{
[TestMethod]
public void SomeTestThatRequiresAFoo()
{
Foo f = new Foo("a");
// testy stuff
}
[TestMethod]
public void SomeTestThatRequiresAFooUtilizingBuilderPattern()
{
Foo f = new FooBuilder().Build();
}
[TestMethod]
public void SomeTestThatRequiresAFooUtilizingBuilderPatternOverrideDefaultValue()
{
Foo f = new FooBuilder()
.WithProp1("different than default")
.Build();
}
}
internal class FooBuilder
{
public string Prop1 { get; private set; }
// default constructor, provide default values to Foo object
public FooBuilder()
{
this.Prop1 = "test";
}
// Sets the "Prop1" value and returns this, done this way to create a "Fluent API"
public FooBuilder WithProp1(string prop1)
{
this.Prop1 = prop1;
return this;
}
// Builds the Foo object by utilizing the properties created as BuilderConstruction and/or the "With[PropName]" methods.
public Foo Build()
{
return new Foo(
this.Prop1
);
}
}
Таким образом, если / когда ваш объект Foo изменится, вам будет проще обновить свои модульные тесты, чтобы учесть эти изменения.
Рассматривать:
public class Foo
{
public string Prop1 { get; private set; }
public string Prop2 { get; private set; }
public Foo(string prop1, string prop2)
{
this.Prop1 = prop1;
this.Prop2 = prop2
}
}
С этой реализацией ваши модульные тесты будут ломаться, но обновление вашего компоновщика намного проще, чем обновление каждого модульного теста, полагаясь на правильную конструкцию Foo
internal class FooBuilder
{
public string Prop1 { get; private set; }
public string Prop2 { get; private set; }
// default constructor, provide default values to Foo object
public FooBuilder()
{
this.Prop1 = "test";
this.Prop2 = "another value";
}
// Sets the "Prop1" value and returns this, done this way to create a "Fluent API"
public FooBuilder WithProp1(string prop1)
{
this.Prop1 = prop1;
return this;
}
// Similar to the "WithProp1"
public FooBuilder WithProp2(string prop2)
{
this.Prop2 = prop2;
return this;
}
// Builds the Foo object by utilizing the properties created as BuilderConstruction and/or the "With[PropName]" methods.
public Foo Build()
{
return new Foo(
this.Prop1,
this.Prop2
);
}
}
С этой новой реализацией Foo и FooBuilder, единственный модульный тест, который сломает это тот, который создал Foo вручную, FooBuilder, использующий модульные тесты, все еще будет работать без ошибок.
Это упрощенный пример, но представьте, если у вас было 20-30 модульных тестов, зависящих от конструкции объекта Foo. Вместо того, чтобы обновлять эти 20-30 модульных тестов, вы можете просто обновить ваш компоновщик, чтобы правильно построить объект Foo.
В вашем примере для модульного тестирования null в конструкторе вы можете написать модульный тест, используя шаблон построителя как таковой:
[TestMethod]
public void TestWithNullInFirstParam()
{
Foo f = new FooBuilder()
.WithProp1(null)
.Build()
// in this case "f" will have Prop1 = null, prop2 = "another value"
}