Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq - Checking method call on concrete class

Here is a very simplistic example of what I'm trying to do:

public class Bar
{
    public void SomeMethod(string param)
    {
        //whatever
    }
}

public interface IBarRepository
{
    List<Bar> GetBarsFromStore();
}

public class FooService
{
    private readonly IBarRepository _barRepository;

    public FooService(IBarRepository barRepository)
    {
        _barRepository = barRepository;
    }

    public List<Bar> GetBars()
    {
        var bars = _barRepository.GetBarsFromStore();
        foreach (var bar in bars)
        {
            bar.SomeMethod("someValue");
        }
        return bars;
    }
}

In my test, I'm mocking IBarRepository to return a concrete List defined in the unit test and passing that mocked repository instance to the FooService constructor.

I want to verify in the FooService method GetBars that SomeMethod was called for each of the Bars returned from the repository. I'm using Moq. Is there any way to do this without mocking the list of Bars returned (if even possible) and not having to put some hacky flag in Bar (yuck) ?.

I'm following an example from a DDD book, but I'm starting to think it smells because I'm challenged in testing the implementation....

like image 310
drogon Avatar asked Feb 22 '23 15:02

drogon


2 Answers

Revised... this passes:

public class Bar
{
    public virtual void SomeMethod(string param)
    {
        //whatever
    }
}

public interface IBarRepository
{
    List<Bar> GetBarsFromStore();
}

public class FooService
{
    private readonly IBarRepository _barRepository;

    public FooService(IBarRepository barRepository)
    {
        _barRepository = barRepository;
    }

    public List<Bar> GetBars()
    {
        var bars = _barRepository.GetBarsFromStore();
        foreach (var bar in bars)
        {
            bar.SomeMethod("someValue");
        }
        return bars;
    }
}

[TestMethod]
public void Verify_All_Bars_Called()
{
    var myBarStub = new Mock<Bar>();
    var mySecondBarStub = new Mock<Bar>();

    var myBarList = new List<Bar>() { myBarStub.Object, mySecondBarStub.Object };
    var myStub = new Mock<IBarRepository>();
    myStub.Setup(repos => repos.GetBarsFromStore()).Returns(myBarList);
    var myService = new FooService(myStub.Object);
    myService.GetBars();

    myBarStub.Verify(bar => bar.SomeMethod(It.IsAny<string>()), Times.Once());
    mySecondBarStub.Verify(bar => bar.SomeMethod(It.IsAny<string>()), Times.Once());
}

Note the slight change to class Bar (SomeMethod() is virtual). A change, but not one involving a flag... :)

Now, in terms of broader design, there is some mutation going on with your bar (whatever "SomeMethod()" actually does). The best thing to do would probably be to verify that this mutation happened on each Bar returned from FooService.GetBars(). That is, setup your repository stub to return some bars, and then verify that whatever mutation is performed by SomeMethod() has taken place. After all, you control the Bars that will be returned, so you can setup their pre-SomeMethod() state, and then inspect their post-SomeMethod() state.

like image 195
Erik Dietrich Avatar answered Mar 03 '23 18:03

Erik Dietrich


If I was writing these classes with unit testing in mind, I would likely either have the class Bar implement an interface IBar and use that interface in my service, or make SomeMethod virtual in Bar.

Ideally like this:

public interface IBar
{
    void SomeMethod(string param);
}

public class Bar : IBar
{
    public void SomeMethod(string param) {}
}

public interface IBarRepository
{
    List<IBar> GetBarsFromStore();
}

public class FooService
{
    private readonly IBarRepository _barRepository;

    public FooService(IBarRepository barRepository)
    {
        _barRepository = barRepository;
    }

    public List<IBar> GetBars()
    {
        var bars = _barRepository.GetBarsFromStore();
        foreach (var bar in bars)
        {
            bar.SomeMethod("someValue");
        }
        return bars;
    }
}

Then my unit test would look as follows:

[Test]
public void TestSomeMethodCalledForEachBar()
{
    // Setup
    var barMocks = new Mock<IBar>[] { new Mock<IBar>(), new Mock<IBar>() };
    var barObjects = barMocks.Select(m => m.Object);
    var repoList = new List<IBar>(barsObjects);
    var repositoryMock = new Mock<IBarRepository>();
    repositoryMock.Setup(r => r.GetBarsFromStore()).Returns(repoList);

    // Execute
    var service = new FooService(repositoryMock.Object);
    service.GetBars();

    // Assert
    foreach(var barMock in barMocks)
        barMock.Verify(b => b.SomeMethod("someValue"));
}
like image 42
Lukazoid Avatar answered Mar 03 '23 18:03

Lukazoid