Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoData Theories with AutoFixture using manual fakes

Given this system to test:

public class MySut
{
    private readonly IHardToMockDependency _hardToMockDependency;

    public MySut(IHardToMockDependency hardToMockDependency,
                 IOtherDependency otherDependency)
    {
        _hardToMockDependency = hardToMockDependency;
    }

    public string GetResult()
    {
        return _hardToMockDependency.GetResult();
    }
}

public interface IOtherDependency { }

public interface IHardToMockDependency
{
    string GetResult();
}

And this unit test:

internal class FakeHardToMockDependency : IHardToMockDependency
{
    private readonly string _result;

    public FakeHardToMockDependency(string result)
    {
        _result = result;
    }

    public string GetResult()
    {
        return _result;
    }
}

public class MyTests
{
    [Fact]
    public void GetResultReturnsExpected()
    {
        string expectedResult = "what I want";
        var otherDependencyDummy = new Mock<IOtherDependency>();
        var sut = new MySut(new FakeHardToMockDependency(expectedResult),
                            otherDependencyDummy.Object);

        var actualResult = sut.GetResult();

        Assert.Equal(expectedResult, actualResult);
    }
}

How should I convert it to use AutoFixture.Xunit and AutoFixture.AutoMoq (while still using the manual fake)?

In real-world tests, the manual fake would have a more complex interface and behavior. Note that I want to pass the anonymous variable (the expectedResult string) to the constructor of the manual fake.

like image 758
TrueWill Avatar asked Mar 15 '13 21:03

TrueWill


3 Answers

There are already some good answers here, but I'd like to suggest a simpler alternative that involves slightly loosening the invariants of the FakeHardToMockDependency class. Make it public, and provide a way to assign the result that is decoupled from the constructor:

public class FakeHardToMockDependency : IHardToMockDependency
{
    private string _result;

    public FakeHardToMockDependency(string result)
    {
        _result = result;
    }

    internal string Result
    {
        get { return _result; }
        set { _result = value; }
    }

    public string GetResult()
    {
        return _result;
    }
}

Notice that I've added an internal property and removed the readonly keyword from the field.

This enables you to refactor the original test to this:

[Theory, AutoMoqData]
public void GetResultReturnsExpected_AutoDataVersion(
    [Frozen(As = typeof(IHardToMockDependency))]FakeHardToMockDependency fake,
    MySut sut)
{
    var expected = "what I want";
    fake.Result = expected;

    var actual = sut.GetResult();

    Assert.Equal(expected, actual);
}

For completeness, here's the AutoMoqDataAttribute code:

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute()
        : base(new Fixture().Customize(new AutoMoqCustomization()))
    {
    }
}
like image 142
Mark Seemann Avatar answered Nov 16 '22 14:11

Mark Seemann


Depending on what kind of parameters you need to pass to your manual fake, you might be able to use a parameterized attribute, similar to the AutoFixture's built-in InlineAutoDataAttribute.

Given these

public interface IHardToMockDependency
{
    string Value { get; }
}

public class FakeHardToMockDependency : IHardToMockDependency
{
    private readonly string _value;

    public FakeHardToMockDependency(string value)
    {
        _value = value;
    }

    #region IHardToMockDependency Members

    public string Value
    {
        get { return this._value; }
    }

    #endregion IHardToMockDependency Members
}

you create an ICustomization implementation that tells a fixture object how to create an implemention of the IHardToFakeDependency interface:

public class FakeHardToMockDependencyCustomization : ICustomization
{
    private readonly string _value;

    public FakeHardToMockDependencyCustomization(string value)
    {
        _value = value;
    }

    #region ICustomization Members

    public void Customize(IFixture fixture)
    {
        fixture.Register<IHardToMockDependency>(() => new FakeHardToMockDependency(this._value));
    }

    #endregion ICustomization Members
}

Note that this needs to know the string you want to pass in, of course.

Next, you roll up this with the other customizations you want to use in a CompositeCustomization:

public class ManualFakeTestConventions : CompositeCustomization
{
    public ManualFakeTestConventions(string value)
        : base(new FakeHardToMockDependencyCustomization(value), new AutoMoqCustomization())
    {
    }
}

Make sure you always put the customizations in order from most specific to most general, as explained here by Mark Seemann.

Now you create an AutoDataAttribute implementation that uses this customization:

public class ManualFakeAutoDataAttribute : AutoDataAttribute
{
    public ManualFakeAutoDataAttribute(string value)
        : base(new Fixture().Customize(new ManualFakeTestConventions(value)))
    {
    }
}

This can now be used in the same way as an InlineAutoDataAttribute:

public class ManualFakeTests
{
    [Theory, ManualFakeAutoData("iksdee")]
    public void ManualFake(IHardToMockDependency fake)
    {
        Assert.IsType<FakeHardToMockDependency>(fake);
        Assert.Equal("iksdee", fake.Value);
    }
}

You can also have it injected into an auto-created SUT instance right away by applying the [Frozen] attribute to the Theory parameter:

    [Theory, ManualFakeAutoData("iksdee")]
    public void SutWithManualFake([Frozen] IHardToMockDependency fake, MySut sut)
    {

    }

This will create a MySut instance and the IHardToMockDependency instance needed for the constructor, for which you have given AutoFixture a rule in the FakeHardToMockDependencyCustomization, and also give you that very instance as the fake variable.

Note that not freezing the fake would still give you a correct FakeHardToMockDependency instance as well as inject one into the sut, but those would be distinct, as we have registered a factory delegate in the customization. Freezing the instance will cause the fixture to always return the same instance for subsequent requests for the interface.

This has a few caveats, however:

  • You have no reference to the string you pass in as a parameter, so you have to have it in there as a string literal twice. You can work around this with string constants within the test class, for example.
  • The number of types that can be used as attribute parameters in .NET is limited. As long as you only need basic types, you should be fine, but calling constructors or the like in the parameter list is not possible.
  • You should only use this attribute when you need an instance if IHardToFakeDependency; otherwise you'll always have to pass in a string parameter anyway. If you have a set of standard customizations you need to use, create another attribute that contains only those.
  • If you need the capabilities of the InlineAutoDataAttribute at the same time, you also need to create yet another attribute that combines the features of both.

Depending on the concrete circumstances, you might also want to look at xUnit.net's PropertyDataAttribute, but I hardly ever find myself using that.

In general, in my opinion, understanding how to work with customizations and autodata attributes and when and how to create your own are the key to effectively using AutoFixture and really make it save you work.

If you often write code in a specific domain that you need to test, it might well make sense to create a library containing customizations, attribute and stub objects that will always be ready to use once you drop it in next to xUnit.net, AutoFixture and Moq. I know I'm damn glad I built mine.

Oh, and also: Having a dependency that is hard to mock might point at a design issue. Why is it that hard to mock?

like image 41
TeaDrivenDev Avatar answered Nov 16 '22 14:11

TeaDrivenDev


Maybe this is not the most idiomatic Autofixture setup but definitely works:

[Fact]
public void GetResultReturnsExpected()
{
    var fixture = new Fixture()
        .Customize(new AutoMoqCustomization());

    var expectedResult = fixture.Create<string>();

    fixture.Register<IHardToMockDependency>(
        () => new FakeHardToMockDependency(expectedResult));

    var sut = fixture.Create<MySut>();

    var actualResult = sut.GetResult();

    Assert.Equal(expectedResult, actualResult);
}

If you want to use also AutoData you can create your own AutoMoqData based on this great article where you can hide some or all of the fixture custimizations.

Something like:

public class MySutAutoDataAttribute : AutoDataAttribute
{
    public MySutAutoData()
        : base(new Fixture()
            .Customize(new AutoMoqCustomization()))
    {
        Fixture.Freeze<string>();

        Fixture.Register<IHardToMockDependency>(
            () => new FakeHardToMockDependency(Fixture.Create<string>()));
    }
}

And you can use it like:

[Theory, MySutAutoData]
public void GetResultReturnsExpected(MySut sut, string expectedResult)
{
    var actualResult = sut.GetResult();

    Assert.Equal(expectedResult, actualResult);
}

But you should note that there is room for a lots of improvement in the MySutAutoDataAttribute for example: it is not very generic and Fixture.Freeze<string>(); can cause problems if you are using multiple strings in your tests.

like image 3
nemesv Avatar answered Nov 16 '22 16:11

nemesv