InlineAutoData not compatible with Moq?

Apr 16, 2012 at 3:43 PM
Edited Apr 16, 2012 at 3:44 PM

This test works:

 

        [Theory]
        [AutoMoqData]
        public void CanAdd(int x, int y, [Frozen] Mock<ILogger> logger, Calculator calculator)
        {
            logger.Setup(l => l.Log(It.IsAny<string>()))
                .Callback<string>(message => Trace.WriteLine(message, "Mocked Logger"));

            calculator.Add(x, y).Should().Be(x + y);
        }

 

Then I wanted to use InlineAutoData, to test a few data sets and modified the test:

 

        [Theory]
        [InlineAutoData(5, 6, 11)]
        [InlineAutoData(-1, 3, 2)]
        public void CanAdd(int x, int y, int result, [Frozen] Mock<ILogger> logger, Calculator calculator)
        {
            logger.Setup(l => l.Log(It.IsAny<string>()))
                .Callback<string>(message => Trace.WriteLine(message, "Mocked Logger"));

            calculator.Add(x, y).Should().Be(result);
        }

 

Now, the test will no longer run because AutoFixture does not know how the generate the mocked logger.

AutoMoqData uses the AutoMoqCustomization and apparently, that one is not present in InlineAutoData. Is there an easy way to make both work together? Am I missing something obvious here?

For completeness: the Calculator class and ILogger interface:

    public class Calculator
    {
        private readonly ILogger logger;

        public Calculator(ILogger logger)
        {
            this.logger = logger;
        }

        internal int Add(int x, int y)
        {
            logger.Log(string.Format("Adding {0} and {1}...", x, y));
            return x + y;
        }
    }

    public interface ILogger
    {
        void Log(string message);
    }

Coordinator
Apr 16, 2012 at 7:47 PM

This is, indeed, possible. Here a cut-and-paste job from some code I recently wrote:

public class InlineAutoRestDataAttribute : InlineAutoDataAttribute
{
    public InlineAutoRestDataAttribute(params object[] values)
        : base(new AutoRestDataAttribute(), values)
    {
    }
}

where AutoRestDataAttribute is defined like this:

public class AutoRestDataAttribute : AutoDataAttribute
{
    public AutoRestDataAttribute()
        : base(new Fixture().Customize(new RestCustomization()))
    {
    }
}

and again RestCustomization derives from CompositeCustomization and contains a series of Customizations, including AutoMoqCustomization.

Here's a real test I wrote using the InlineAutoRestDataAttribute:

[Theory]
[InlineAutoRestData(0)]
[InlineAutoRestData(1)]
[InlineAutoRestData(2)]
public void GetPagedSearchResultsReturnsCorrectMatches(int page,
    [Frozen]Mock<ISearchQuery> queryStub,
    [Frozen]Mock<IResourceLinker> linkerStub,
    SearchController sut,
    string term,
    Generator<SearchMatch> infiniteMatches,
    Uri selfUri)
{
    linkerStub
        .Setup(l => l.GetUri<SearchController>(
            It.IsAny<Expression<Action<SearchController>>>()))
        .Returns(selfUri);

    var allMatches = infiniteMatches.Take(100).ToArray();
    queryStub.Setup(q => q.GetMatches(term)).Returns(allMatches);

    var actual = sut.GetPagedSearchResults(term, page);

    var expected = (from m in allMatches
                    select m.AsSource().OfLikeness<SearchMatch>())
                    .Skip(page * SearchController.PageSize)
                    .Take(SearchController.PageSize);
    Assert.True(expected.Cast<object>().SequenceEqual(
        actual.Matches.Cast<object>()));
}
HTH

Developer
Apr 16, 2012 at 8:10 PM

This behavior is by design because the InlineAutoDataAttribute class does not know anything about Moq (or any other mocking framework).

However, it is possible to combine data coming from inline values with auto-mocked generated data specimens.

Define a custom CompositeDataAttribute derived type, for ex. InlineAutoMoqDataAttribute and pass the AutoMoqDataAttribute class in the .ctor arguments.

public class InlineAutoMoqDataAttribute : CompositeDataAttribute
{
    public InlineAutoMoqDataAttribute(params object[] values)
        : base(new DataAttribute[] { new InlineDataAttribute(values), new AutoMoqDataAttribute() })
    {
    }
}

The AutoMoqDataAttribute should be similar to the one below:

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute()
        : base(new Fixture().Customize(new AutoMoqCustomization()))
    {
    }
}