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

本文关键字:方法 调用 检查 Moq- | 更新日期: 2023-09-27 18:22:13

下面是一个非常简单的例子,说明我要做的事情:

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。有没有什么方法可以做到这一点,而不嘲笑返回的酒吧列表(如果可能的话),也不必在酒吧里放一些粗鲁的旗帜(恶心)?。

我正在学习DDD书中的一个例子,但我开始觉得它有味道,因为我在测试实现时遇到了挑战。。。。

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

修订。。。通过:

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

如果我在编写这些类时考虑到单元测试,我可能会让类Bar实现接口IBar并在我的服务中使用该接口,或者在Bar中使SomeMethod虚拟化。

理想情况下是这样的:

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