HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Unit tests using C#, Moq, AutoFixture, FluentAssertions

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
autofixturefluentassertionstestsmoqusingunit

Problem

Could you give any suggestion on the following unit tests? How to make it more readable, maintainable and trustworthy. If it is not a problem - post some code with the suggestions.

```
[TestClass]
public class PropertyBagCompositeTests
{
private readonly IFixture fixture;
private readonly Mock secondPropertyBagMock;
private readonly Mock firstPropertyBagMock;
private readonly string propertyName;
private readonly int propertyValue;
private readonly PropertyBagComposite cut;

public PropertyBagCompositeTests()
{
fixture = new Fixture();
firstPropertyBagMock = new Mock();
secondPropertyBagMock = new Mock();
propertyName = Any();
propertyValue = Any();
cut = new PropertyBagComposite(new [] { firstPropertyBagMock.Object, secondPropertyBagMock.Object});
}

[TestMethod]
public void GetProperty_DelegatedToBags()
{
cut.PropertyGet(propertyName);

firstPropertyBagMock.Verify(x => x.PropertyGet(propertyName), Times.Once());
secondPropertyBagMock.Verify(x => x.PropertyGet(propertyName), Times.Once());
}

[TestMethod]
public void GetProperty_NoBagHasProperty_ReturnsNull()
{
object result = cut.PropertyGet(propertyName);

result.Should().BeNull();
}

[TestMethod]
public void GetProperty_OneBagHasPropety_ResturnsItsResult()
{
firstPropertyBagMock.Setup(x => x.PropertyGet(propertyName)).Returns(propertyValue);

object result = cut.PropertyGet(propertyName);

result.Should().Be(propertyValue);
}

[TestMethod]
public void GetProperty_BothBagHasPropetyWithSameValue_ResturnsItsResult()
{
firstPropertyBagMock.Setup(x => x.PropertyGet(propertyName)).Returns(propertyValue);
secondPropertyBagMock.Setup(x => x.PropertyGet(propertyName)).Returns(propertyValue);

object result = cut.PropertyGet(propertyName);

result.Should().Be(propertyValue);
}

Solution

It's pretty clean. In my oppinion, test parameters should be setup in each test, and not during setup. Setup is for setting up infrastructure bits. You should be able to take advantage of AutoFixture AutoMocking customization to make your tests more resiliant to change (let autofixture create your sut, so adding future dependencies won't break your valid tests).

firstPropertyBagMock.Verify(x => x.PropertySet(propertyName, propertyValue), Times.Once());


The times.once() is kind of redundant, as this is the default.

Assertion like this:

result.Count(x => x == duplicated).Should().Be(1);


Can be rewritten a bit cleaner (Will have a better message on fail:

result.Where(x => x == duplicated).Should().HaveCount(c => c == 1)


Otherwise, you can achieve a bit more readability by creating a common base class to help with your tests, one I use (Uses rhino mocks, but gives you the idea):

public abstract class AutoFixtureBaseTest : AutoFixtureBaseTest where TSut : class {}

[TestFixture]
public abstract class AutoFixtureBaseTest
    where TSut : class, TContract
    where TContract : class
{
    protected IFixture Fixture { get; private set; }

    [SetUp]
    protected virtual void Initialize()
    {
        Fixture = new Fixture().Customize(new AutoRhinoMockCustomization());
        Fixture.Behaviors.Add(new OmitOnRecursionBehavior());
        Fixture.Customize(x => x.FromFactory(new MethodInvoker(new GreedyConstructorQuery())));
    } 

    public T Dep()
    {
        return Fixture.Freeze();
    }

    public T Fake(Func, IPostprocessComposer> builder)
    {
        return builder(Fixture.Build()).Create();
    }

    public T Fake()
    {
        return Fixture.Create();
    }

    public void Chain(Function expression) where TParent : class where TChild : class
    {
        Dep().Stub(expression).Return(Dep());
    }

    public IEnumerable FakeMany(Func, IPostprocessComposer> builder)
    {
        return builder(Fixture.Build()).CreateMany();
    }

    public IEnumerable FakeMany()
    {
        return Fixture.CreateMany();
    }

    public TContract Sut
    {
        get
        {
            return Fixture.Freeze();
        }
    }
}


With this setup and configuration, most times you only need to setup the specific cases for each test.

EDIT: Quick example of how to convert your code to use automocking:

Take this section of code from your example:

public PropertyBagCompositeTests()
    {
        fixture = new Fixture();
        firstPropertyBagMock = new Mock();
        secondPropertyBagMock = new Mock();
        propertyName = Any();
        propertyValue = Any();
        cut = new PropertyBagComposite(new [] { firstPropertyBagMock.Object, secondPropertyBagMock.Object});
    }


You're particular example, is somewhat complicated by the fact you are injecting an IEnumerable, for that you have to explicitly register:

public PropertyBagCompositeTests()
    {
        fixture = new Fixture().Customize(new AutoMoqCustomization());
        propertyBags = fixture.CreateMany>();
        fixture.Register>(() => propertyBags.Select(x => x.Object));

        propertyName = Any();
        propertyValue = Any();
        cut = fixture.Freeze();
    }


A more common example, and a good reference point:
http://blog.ploeh.dk/2010/08/19/AutoFixtureasanauto-mockingcontainer/

Using the base class similar to what I mentioned, you can do things like:

public class MyObjectSpecification : AutoFixtureBaseTest {
     [TestMethod]
     public void Should_do_something_with_dependency(){
         var expected = Fake();
         Dep(x => x.Setup(dep => dep.DoSomething()).Returns(expected).Verifiable());
         var result = Sut.DoSomethingWithDependency();
         result.Should().Be(expected);
         Verify();
     }
}

Code Snippets

firstPropertyBagMock.Verify(x => x.PropertySet(propertyName, propertyValue), Times.Once());
result.Count(x => x == duplicated).Should().Be(1);
result.Where(x => x == duplicated).Should().HaveCount(c => c == 1)
[TestFixture]
public abstract class AutoFixtureBaseTest<TSut, TContract>
    where TSut : class, TContract
    where TContract : class
{
    protected IFixture Fixture { get; private set; }

    [SetUp]
    protected virtual void Initialize()
    {
        Fixture = new Fixture().Customize(new AutoRhinoMockCustomization());
        Fixture.Behaviors.Add(new OmitOnRecursionBehavior());
        Fixture.Customize<TSut>(x => x.FromFactory(new MethodInvoker(new GreedyConstructorQuery())));
    } 

    public T Dep<T>()
    {
        return Fixture.Freeze<T>();
    }

    public T Fake<T>(Func<ICustomizationComposer<T>, IPostprocessComposer<T>> builder)
    {
        return builder(Fixture.Build<T>()).Create();
    }

    public T Fake<T>()
    {
        return Fixture.Create<T>();
    }

    public void Chain<TParent, TChild>(Function<TParent, TChild> expression) where TParent : class where TChild : class
    {
        Dep<TParent>().Stub(expression).Return(Dep<TChild>());
    }

    public IEnumerable<T> FakeMany<T>(Func<ICustomizationComposer<T>, IPostprocessComposer<T>> builder)
    {
        return builder(Fixture.Build<T>()).CreateMany();
    }

    public IEnumerable<T> FakeMany<T>()
    {
        return Fixture.CreateMany<T>();
    }

    public TContract Sut
    {
        get
        {
            return Fixture.Freeze<TSut>();
        }
    }
}
public PropertyBagCompositeTests()
    {
        fixture = new Fixture();
        firstPropertyBagMock = new Mock<IPropertyBag>();
        secondPropertyBagMock = new Mock<IPropertyBag>();
        propertyName = Any<string>();
        propertyValue = Any<int>();
        cut = new PropertyBagComposite(new [] { firstPropertyBagMock.Object, secondPropertyBagMock.Object});
    }

Context

StackExchange Code Review Q#45392, answer score: 9

Revisions (0)

No revisions yet.