The Selkie.AutoMocking project is an extension for MSTest2 which provides a SUT Factory. It tries to replicate the functionality of the package AutoFixture.AutoNSubstitute and make it available for MSTest2.
Goal: The goal is to make writing unit tests easier and more refactoring-safe.
How is it done? The SUT and all the required dependencies are automatically provided to the test method.
How do I use it? Like a normal MSTest2 Data Driven Test Method.
[AutoDataTestMethod]
public void Add_ForNumbers_Adds(Calculator sut,
[Freeze] IAdd add)
{
add.Execute(1, 2)
.Returns(3);
sut.Add(1, 2)
.Should()
.Be(3);
}
AutoDataTestMethod? The attribute tells MSTest to use the Selkie.AutoMocking extension of the DataTestMethod.
Freeze? Freezing a parameter makes sure that the SUT uses the same instance ass the frozen parameter (see AutoFixture Freeze).
Let do a very simple Calculator which will show us how to use the Selkie.AutoMocking package.
The Calculator will only...
- work for integers and
- adds two integers together.
The complete source code can be found here: Example
The Calculator class depends on the IAdd and ISubtract interface which is injected in the constructor.
public class Calculator : ICalculator
{
private readonly IAdd _add;
private readonly ISubtract _subtract;
public Calculator([NotNull] IAdd add,
[NotNull] ISubtract subtract)
{
Guard.ArgumentNotNull(add,
nameof(add));
Guard.ArgumentNotNull(subtract,
nameof(subtract));
_add = add;
_subtract = subtract;
}
...
}
The job of the Add class is just to add 2 integers together.
public class Add : IAdd
{
public int Execute(int a, int b)
{
return a + b;
}
}
All my constructors in my projects make sure that the injected arguments are not null. This mean that I have to delay the creation of the SUT in my tests using Lazy and being able to set arguments to null. This can be done by using the [BeNull] attribute.
[AutoDataTestMethod]
public void Create_ForAddIsNull_Throws(Lazy<Calculator> sut,
[BeNull] IAdd add)
{
Action action = () =>
{
// ReSharper disable once UnusedVariable
var actual = sut.Value;
};
action.Should()
.Throw<ArgumentNullException>()
.WithParameter("add");
}
The example below shows how to test the Add method using the AutoDataTestMethod of the Selkie.AutoMocking package.
using Calculator.Interfaces;
using FluentAssertions;
using NSubstitute;
using Selkie.AutoMocking;
namespace Calculator.Tests
{
[AutoDataTestClass]
public class CalculatorTests
{
[AutoDataTestMethod]
public void Add_ForNumbers_Adds(Calculator sut,
[Freeze] IAdd add)
{
add.Execute(1, 2)
.Returns(3);
sut.Add(1, 2)
.Should()
.Be(3);
}
}
}
Note: There isn't a method to initialize the test nor a CreateSut() method!
Now we add the subtraction functionality to the Calculator class by adding a dependency in the constructor.
public class Calculator : ICalculator
{
private readonly IAdd _add;
private readonly ISubtract _subtract;
public Calculator([NotNull] IAdd add,
[NotNull] ISubtract subtract)
{
_add = add;
_subtract = subtract;
}
public int Add(int a, int b)
{
return _add.Execute(a, b);
}
public int Subtract(int a, int b)
{
return _subtract.Execute(a, b);
}
}
No existing code needs to be modified, only the new test for the subtraction needs to be added!
[AutoDataTestClass]
public class CalculatorTests
{
[AutoDataTestMethod]
public void Add_ForNumbers_Adds(Calculator sut,
[Freeze] IAdd add)
{
add.Execute(1, 2)
.Returns(3);
sut.Add(1, 2)
.Should()
.Be(3);
}
[AutoDataTestMethod]
public void Subtract_ForNumbers_Subtracts(Calculator sut,
[Freeze] ISubtract subtract)
{
subtract?.Execute(1, 2)
.Returns(-1);
sut.Subtract(1, 2)
.Should()
.Be(-1);
}
}