Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoFixture+Moq - Freezing a mocked class prevent from setup

I'd like to understand why my test fails when I'm freezing a mocked class instead of an mocked interface

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

    var webRequestMock = fixture.Freeze<Mock<MyWebRequest>>(); // freezing a class + setuping a return value
    webRequestMock.Setup(a => a.GetData())
    .Returns("Foo");

    var myService = fixture.Create<MyService>();

    var actual = myService.GetData();

    webRequestMock.Verify(a => a.GetData(), Times.Once()); // Failed it's never called once
    Assert.Equal("Foo", actual); // Failed, if we comment previous line, 'actual' value is always empty
}

public class MyService
{
    private readonly MyWebRequest _request;

    public MyService(MyWebRequest request)
    {
        _request = request;
    }

    public string GetData()
    {
        var data = _request.GetData();
        return data;
    }
}

public class MyWebRequest 
{
    public virtual string GetData() // you can see here, the method is well virtual, and should be overridable by moq.
    {
        return string.Empty;
    }
}

1/ If I try with a mocked interface, it works.

[Fact]
public void MethodeName()
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    const string expected = "Foo";

    var webRequestMock = fixture.Freeze<Mock<IMyWebRequest>>();
    webRequestMock.Setup(a => a.GetData())
    .Returns("Foo");

    var myService = fixture.Create<MyService>();

    var actual = myService.GetData();

    webRequestMock.Verify(a => a.GetData(), Times.Once()); // Success
    Assert.Equal(expected, actual); // Success
}

public class MyService
{
    private readonly IMyWebRequest _request;

    public MyService(IMyWebRequest request)
    {
        _request = request;
    }

    public string GetData()
    {
        var data = _request.GetData();
        return data;
    }
}

public interface IMyWebRequest
{
    string GetData();
}

public class MyWebRequest : IMyWebRequest
{
    public virtual string GetData()
    {
        return string.Empty;
    }
}

2/ If I use 'inject' instead of freeze, it works:

[Fact]
public void MethodeName()
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    const string expected = "Foo";

    var webRequestMock = new Mock<MyWebRequest>(); 
    webRequestMock.Setup(a => a.GetData()).Returns("Foo");
    fixture.Inject(webRequestMock.Object);

    var myService = fixture.Create<MyService>();

    var actual = myService.GetData();

    webRequestMock.Verify(a => a.GetData(), Times.Once()); // Success
    Assert.Equal(expected, actual); // Success
}

public class MyService
{
    private readonly MyWebRequest _request;

    public MyService(MyWebRequest request)
    {
        _request = request;
    }

    public string GetData()
    {
        var data = _request.GetData();
        return data;
    }
}

public class MyWebRequest 
{
    public virtual string GetData()
    {
        // make WebRequest
        return string.Empty;
    }
}

My autofixture version: 3.6.5.0

like image 307
John Avatar asked Mar 05 '17 09:03

John


1 Answers

MyWebRequest is a concrete (non-abstract) class, and AutoFixture.AutoMoq doesn't create those using Moq. This is by design, since the AutoFixture kernel already deals with creation of 'normal' classes.

You can change this behaviour if you'd like. Often, though, I'd consider having to do this a design smell. It's difficult to tell in this case, though, because you've done a good job of reducing the problem to a minimal repro. The problem with this, though, is that as presented here, MyService doesn't add any value. I'm sure that's not what your real code looks like, but since I can't guess what your real code looks like, I can't tell if a better design would be possible.

like image 147
Mark Seemann Avatar answered Oct 15 '22 02:10

Mark Seemann