2015-01-26 3 views
1

Я закодировал конструктор для класса, и я тестирую для каждого параметра, равным нулю. Смотрите пример ниже:.Единичный тест Null Parameters in Constructor

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

Если вы явно тестируете конструктор, то тест должен будет изменение при изменении контракта конструктора. То же самое относится к любой проверенной функции. Боюсь, это жизнь. –

+0

Вы используете контейнер IoC в своем приложении, чтобы вводить depdencies в этот класс?если это так, задайте вопрос о значении письма (и, следовательно, необходимо его поддерживать), во многих тестах (может быть сотни), которые проверяют вашу инфраструктуру. В одном из решений, над которым я работал, было много тысяч тестов, проверяющих нулевые аргументы ctor, и это стало кошмаром для поддержания. – Matt

+0

@Matt, к сожалению, мой клиент хочет 100% -ный охват тестированием. Я не суетился о тестах и ​​не тестировал подобные вещи или проводку. Мне нравится программировать оборонительно, но иногда, когда я работаю в местах, пытающихся достичь 100% -ного охвата, я чувствую, что я просто делаю все для себя. –

ответ

2

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

Упрощенный пример шаблона строитель будет:

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.

В вашем примере модульного тестирования нуля в конструкторе, можно написать модульный тест, используя шаблон строителя, как например:

[TestMethod] 
public void TestWithNullInFirstParam() 
{ 
    Foo f = new FooBuilder() 
     .WithProp1(null) 
     .Build() 

    // in this case "f" will have Prop1 = null, prop2 = "another value" 
} 
+0

Очень интересно, определенно что-то переварить и рассмотреть. –