Moq - проверка вызова метода на конкретном классе - PullRequest
3 голосов
/ 03 февраля 2012

Вот очень упрощенный пример того, что я пытаюсь сделать:

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;
    }
}

В моем тесте я пытаюсь заставить IBarRepository вернуть конкретный список, определенный в модульном тесте, и передать имитированныйэкземпляр репозитория для конструктора FooService.

Я хочу проверить в методе FooService GetBars, что SomeMethod был вызван для каждого из столбцов, возвращаемых из хранилища.Я использую Moq.Есть ли способ сделать это, не посмеиваясь над списком возвращенных баров (если вообще возможно) и без необходимости ставить какой-нибудь хакерский флаг в баре (гадость)?

Я следую примеру из книги DDD, но начинаю думать, что он пахнет, потому что я испытал трудности при тестировании реализации ....

Ответы [ 2 ]

4 голосов
/ 03 февраля 2012

Пересмотрено ... это проходит:

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());
}

Обратите внимание на небольшое изменение в классе Bar (SomeMethod () является виртуальным). Изменение, но не с участием флага ...:)

Теперь, с точки зрения более широкого дизайна, в вашем баре происходит некоторая мутация (что бы ни делал "SomeMethod ()"). Лучше всего было бы убедиться, что эта мутация произошла на каждой панели, возвращаемой из FooService.GetBars (). То есть настройте свою заглушку репозитория так, чтобы она возвращала несколько баров, а затем убедитесь, что какая-либо мутация выполняется SomeMethod (). В конце концов, вы контролируете столбцы, которые будут возвращены, поэтому вы можете установить их состояние pre-SomeMethod (), а затем проверить их состояние post-SomeMethod ().

2 голосов
/ 03 февраля 2012

Если бы я писал эти классы с учетом модульного тестирования, я бы, скорее всего, либо Bar реализовал интерфейс IBar и использовал бы этот интерфейс в своем сервисе, либо сделал бы SomeMethod виртуальным в Bar.

В идеале так:

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;
    }
}

Тогда мой модульный тест будет выглядеть следующим образом:

[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"));
}
...