以下是我正在尝试做的一个非常简单的例子:
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书籍中的一个例子,但我开始认为它有气味,因为我在测试实现方面遇到了挑战....
答案 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"));
}