The solemn temples, the great globe itself,
Yea, all which it inherit, shall dissolve,
And, like this insubstantial pageant faded,
Leave not a rack behind. We are such stuff"
-- Shakespeare, The Tempest
Act IV, Scene I, Lines 152 -156
In my day-to-day work I do a lot of .Net programming. It seem at some point in each of the applications I am either enhancing or creating I ended including Mark Seemann's AutoFixture (if it is not already in use). AutoFixture is an easy way to create a fixture object. A fixture object is an object which centralizes your helper methods in your test code, like methods which create your system under test and help generate test data.
Fixture objects are great and I often find myself wanting one in my day-to-day work, but I am lazy. Since I am lazy I do not want to go to all the trouble of creating my own fixture object, to quote Homer Simpson, "Can't someone else do it". Luckily in the .Net realm someone already has, Mark Seemann. AutoFixture lets you get the best of all worlds, you get a fixture object and you do not have to write the framework around it! (working with it for a few years now, I can say it is well thought out and not a big hair ball, see also Simple Made Easy for the full reference)
How about some examples? (taken from the AutoFixture cheat sheet and rewritten using xUnit)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using Ploeh.AutoFixture; | |
using Ploeh.AutoFixture.Xunit2; | |
using Xunit; | |
namespace AutoFixtureDemo | |
{ | |
public class Demo | |
{ | |
public class MyClass | |
{ | |
public int Echo(int expected) | |
{ | |
return expected; | |
} | |
} | |
[Fact] | |
public void IntroTetFromGitHub() | |
{ | |
var fixture = new Fixture(); | |
var expected = fixture.Create<int>(); | |
var sut = fixture.Create<MyClass>(); | |
var actual = sut.Echo(expected); | |
Assert.Equal(expected, actual); | |
} | |
[Theory, AutoData] | |
public void IntroXunitTetFromGitHub(int expected, MyClass sut) | |
{ | |
var actual = sut.Echo(expected); | |
Assert.Equal(expected, actual); | |
} | |
[Theory, AutoData] | |
public void CheatSheetTestsForStrings(Fixture fixture) | |
{ | |
Assert.IsType<string>(fixture.Create<string>()); | |
var seed = "seed"; | |
Assert.Contains(seed, fixture.Create(seed)); | |
} | |
[Theory, AutoData] | |
public void CheatSheetTestsForNumbers(Fixture fixture) | |
{ | |
Assert.IsType<int>(fixture.Create<int>()); | |
} | |
public class ComplexType | |
{ | |
public string Name; | |
public int Number; | |
} | |
[Theory, AutoData] | |
public void CheatSheetTestsForComplexTypes(ComplexType complexType) | |
{ | |
Assert.NotNull(complexType.Name); | |
Assert.NotNull(complexType.Number); | |
Assert.Contains("Name", complexType.Name); | |
} | |
public abstract class AbstractType {} | |
public class MyFakeAbstract : AbstractType { } | |
public interface IInterface { } | |
public class MyFakeInterface : IInterface { } | |
[Theory, AutoData] | |
public void CheatSheetTestsForAbstractTypes(Fixture fixture) | |
{ | |
fixture.Register<AbstractType>(() => new MyFakeAbstract()); | |
fixture.Register<IInterface>(() => new MyFakeInterface()); | |
Assert.IsType<MyFakeAbstract>(fixture.Create<AbstractType>()); | |
Assert.IsType<MyFakeInterface>(fixture.Create<IInterface>()); | |
} | |
[Theory, AutoData] | |
public void CheatSheetTestsForReplacingDefaultAlgorithms(string replacement, Fixture fixture) | |
{ | |
Assert.NotEqual(replacement, fixture.Create<string>()); | |
fixture.Register<string>(() => replacement); | |
var actual = fixture.Create<string>(); | |
Assert.Equal(replacement, actual); | |
} | |
[Theory, AutoData] | |
public void CheatSheetTestsSequences(Fixture fixture) | |
{ | |
Assert.Equal(3, fixture.CreateMany<string>().Count()); | |
Assert.Equal(3, fixture.CreateMany<int>().Count()); | |
Assert.Equal(3, fixture.CreateMany<int>().Distinct().Count()); | |
const int expected = 100; | |
Assert.Equal(expected, fixture.CreateMany<int>(expected).Count()); | |
Assert.Equal(3, fixture.CreateMany<MyClass>().Count()); | |
} | |
[Theory, AutoData] | |
public void CheatSheetTestsAddManyTo(Fixture fixture) | |
{ | |
var list = new List<int>(); | |
Assert.Equal(0, list.Count); | |
fixture.AddManyTo(list); | |
Assert.Equal(3, list.Count); | |
} | |
public class MyClassWithProperties | |
{ | |
public int Number; | |
public string SomeString { get; set; } | |
} | |
[Theory, AutoData] | |
public void CheatSheetTestsSetProperty(Fixture fixture) | |
{ | |
var expected = "my string"; | |
var actual = fixture | |
.Build<MyClassWithProperties>() | |
.With(w => w.SomeString, expected) | |
.Create(); | |
Assert.Equal(expected, actual.SomeString); | |
actual = fixture | |
.Build<MyClassWithProperties>() | |
.Without(w => w.SomeString) | |
.Create(); | |
Assert.Null(actual.SomeString); | |
var counter = 0; | |
fixture | |
.Build<MyClass>() | |
.Do(_ => ++counter) | |
.Create(); | |
Assert.Equal(1, counter); | |
} | |
[Theory, AutoData] | |
public void CheatSheetTestsCustomizeTypes(Fixture fixture) | |
{ | |
var expected = fixture.Create<int>(); | |
var counter = 0; | |
fixture | |
.Customize<MyClassWithProperties>(c => c | |
.Do(_ => ++counter) | |
.WithAutoProperties() | |
.With(w => w.Number, expected)); | |
var myClass = fixture.Create<MyClassWithProperties>(); | |
Assert.Equal(1, counter); | |
Assert.Equal(expected, myClass.Number); | |
} | |
} | |
} |
We see in the above lots of wonderful things.
- We can walk up to the fixture object and ask it for some test data.
- We can use the AutoData attribute and ask for test data.
- We can register implementation for abstract types.
- We can create collections of test data.
- We can build specific test data saying what attributes we care about and letting the fixture object set up the rest.
- We can even have a do method to allow for modification outside of the object we are having the fixture object create (this is not good design but sometimes it is needed).
Another framework I use a lot in my day-to-day .Net programming is Moq. Guess what, AutoFixture can be uses as an auto-mocking container with Moq (and it has plugins for other mocking frameworks too).
Yet another example. (using MS Test taken from an overview of AutoFixture I did at work recently)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Microsoft.VisualStudio.TestTools.UnitTesting; | |
using Moq; | |
using Ploeh.AutoFixture; | |
using Ploeh.AutoFixture.AutoMoq; | |
namespace AutoFixtureDemo.MSTest | |
{ | |
public class Echoer | |
{ | |
private ILogger _logger; | |
private readonly ISaver _saver; | |
public Echoer(ILogger logger, ISaver saver) | |
{ | |
_logger = logger; | |
_saver = saver; | |
} | |
public string Echo(string value) | |
{ | |
_logger.Log(value); | |
var result = _saver.Save(value); | |
if (result.Result) _logger.Log("Successfully saved!"); | |
return value; | |
} | |
} | |
[TestClass] | |
public class Demo | |
{ | |
private IFixture _fixture; | |
private Echoer _sut; | |
private Mock<ILogger> _spyLogger; | |
private Mock<ISaver> _spySaver; | |
[TestInitialize] | |
public void BeforeEach() | |
{ | |
_fixture = new Fixture().Customize(new AutoMoqCustomization()); | |
_spyLogger = _fixture.Freeze<Mock<ILogger>>(); | |
_spySaver = _fixture.Freeze<Mock<ISaver>>(); | |
_sut = _fixture.Create<Echoer>(); | |
} | |
[TestMethod] | |
public void GivenStringItMustEchoIt() | |
{ | |
var expected = _fixture.Create<string>(); | |
var actual = _sut.Echo(expected); | |
Assert.AreEqual(expected, actual); | |
} | |
[TestMethod] | |
public void GivenNullItMustReturnNull() | |
{ | |
var actual = _sut.Echo(null); | |
Assert.IsNull(actual); | |
} | |
[TestMethod] | |
public void GivenStringItMustLogIt() | |
{ | |
var expected = _fixture.Create<string>(); | |
_sut.Echo(expected); | |
_spyLogger.Verify(v => v.Log(expected)); | |
} | |
[TestMethod] | |
public void GivenStringItMustSaveIt() | |
{ | |
var expected = _fixture.Create<string>(); | |
_sut.Echo(expected); | |
_spySaver.Verify(v => v.Save(expected)); | |
} | |
[TestMethod] | |
public void GivenStringAndSuccessfulSaveItMustLogSuccess() | |
{ | |
_spySaver | |
.Setup(s => s.Save(It.IsAny<string>())) | |
.Returns(_fixture.Build<SaverResult>().With(w => w.Result, true).Create()); | |
_sut.Echo(_fixture.Create<string>()); | |
_spyLogger.Verify(v => v.Log("Successfully saved!")); | |
} | |
} | |
public interface ILogger | |
{ | |
void Log(string message); | |
} | |
public interface ISaver | |
{ | |
SaverResult Save(string value); | |
} | |
public class SaverResult | |
{ | |
public bool Result { get; set; } | |
} | |
} |
We see in this example that we had a simple class called Echo which got top hatted into having logging and a backup added to it. The interactions with the logger and back-upper need to be tested, luckily we can tell the fixture object that we would like to get spy objects for the logger and back-upper. These spies from AutoFixture are Moq mocks which allows us to verify that the behaviors we want.
By using AutoFixture and Moq we can meet all the "needs" of Top Hats everywhere.
(The term Top Hats comes from Uncle Bob's Clean Coder series episode 7, in which there is a scene with an Architect talking about choosing an IDE and Database for a project hence the term Top Hat and top hatting to describe this type of "architecture".)
I find that AutoFixture allows me to simplify my test code (simple as discussed in Simple Made Easy) and allows me to stay focus on what I am actually trying to test.