Moq - 检查具体类的方法调用

时间:2012-02-02 23:33:20

标签: c# unit-testing nunit moq

以下是我正在尝试做的一个非常简单的例子:

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以返回在单元测试中定义的具体List,并将该模拟的存储库实例传递给FooService构造函数。

我想在FooService方法GetBars中验证是否为从存储库返回的每个Bars调用了SomeMethod。我正在使用Moq。有没有办法做到这一点,没有嘲笑返回的酒吧列表(如果可能的话),而不必在Bar(yuck)中放一些hacky旗帜?

我正在关注DDD书籍中的一个例子,但我开始认为它有气味,因为我在测试实现方面遇到了挑战....

2 个答案:

答案 0 :(得分:3)

修订......通过:

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()返回的每个Bar上发生了这种突变。也就是说,设置您的存储库存根以返回一些条形,然后验证SomeMethod()是否已执行任何突变。毕竟,你控制将返回的Bars,这样你就可以设置它们的SomeMethod()状态,然后检查它们的后SomeMethod()状态。

答案 1 :(得分:2)

如果我在编写这些带有单元测试的类时,我可能会让类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"));
}