1

I have coded a constructor for a class and I am testing for each parameter being null. See example below:

public MyClass(IObjectA objA, IObjectB objB) : IMyClass
{
    if (objA == null)
    {
        throw new ArgumentNullException("objA");
    }

    if (objB == null)
    {
        throw new ArgumentNullException("objB");
    }

    ...
}

Usually I unit test (using Moq) this by mocking out IObjectA and IObjectB and passing them in. The example above would create 2 unit tests to test each scenario.

The problem I have is when a 3rd parameter is passed into the constructor. It requires that I alter my previous tests, as I suddenly get a "No constructor for MyClass has 2 parameters" type exception.

I also use AutoMockContainer. Essentially I'd like to be able to test the constructor by registering a null object in the container. For example:

[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>();
}

Then it doesn't matter how many new parameters are added to the constructor. I will not have to update my unit tests.

Sadly, the above unit test passes. But for the wrong reason. The Register<T>() method throws the ArgumentNullException not the code executed in the 'Act' section.

Does anyone have a suggestion for being able to test constructor parameters and not have to revisit the unit test when new parameters are later added?

Adrian Thompson Phillips
  • 6,893
  • 6
  • 38
  • 69
  • 1
    If you're explicitly testing the constructor, then the test will have to change when you change the contract of the constructor. The same goes for any function under test. That's life, I'm afraid. – Benjamin Hodgson Jan 26 '15 at 14:57
  • Are you using a IoC container within your app to inject depdencies into this class? if so, question the value of writing (and therefore having to maintain) many tests (could be hundreds) that test your infrastructure. One solution I worked on had many thousands of tests all testing null ctor arguments and it became a nightmare to maintain. – Matt Jan 26 '15 at 15:23
  • @Matt sadly my client wants 100% test coverage. I'm not fussed about test coverage stats myself and wouldn't normally be testing these sorts of things or the wiring. I like to program defensively, but sometimes when I work at places trying to achieve 100% coverage, I feel like I'm just making things hard for myself. – Adrian Thompson Phillips Jan 26 '15 at 16:41

1 Answers1

3

You can help alleviate some of this burden by utilizing either the factory or builder patterns to create your objects.

A simplified example of the builder pattern would be:

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
        );
    }
}

This way if/when your Foo object changes, it's a bit easier to update your unit tests to take the changes into account.

Consider:

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
    }
}

With this implementation your unit tests will break, but updating your builder is much easier than updating each unit test relying on the proper construction of a 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
        );
    }
}

With this new implementation of Foo and FooBuilder, the only unit test that would break is the one that created the Foo manually, the FooBuilder utilizing unit tests will still work without error.

This is a simplified example, but imagine if you had 20-30 unit tests reliant on the construction of a Foo object. Rather than updating those 20-30 unit tests, you can simply just update your builder to properly construct the Foo object.

In your example to unit test a null in the constructor, you could write the unit test using the builder pattern as such:

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

    // in this case "f" will have Prop1 = null, prop2 = "another value"
}  
Kritner
  • 13,557
  • 10
  • 46
  • 72