单元测试:如何验证返回Mock的工厂函数的实例化与方法调用
依赖
Func<IFoo>工厂的SUT单元测试验证指南 问题背景
被测系统(SUT)依赖两个注入参数:Func<IFoo>委托工厂与IBar接口。SUT内部会通过该工厂实例化IFoo对象,随后分别调用该IFoo对象的方法与持有的_bar对象的方法。
SUT实现代码如下:
public class Sut : ISut { readonly Func<IFoo> _fooFactory; IBar _bar; public Sut(Func<IFoo> fooFactory, IBar bar) { _fooFactory = fooFactory; _bar = bar; } public SutMethod() { foo = _fooFactory(); foo.fooMethod(); _bar.barMethod(); } }
现有测试的缺陷
目前测试中仅能通过IBar的Mock对象barMock验证_bar的方法调用逻辑,无法直接验证工厂生成的foo对象的状态,需要覆盖两个核心验证点:
- 工厂被正确调用生成IFoo实例
- 该IFoo对象的
fooMethod方法被正确调用
除了原测试代码中存在的语法笔误外,最核心的逻辑问题是将IFoo的Mock实例完全封闭在工厂委托的闭包内部,外部测试上下文无法获取该Mock实例的引用,自然没有办法在SUT执行完成后,通过Mock对象的Verify方法断言fooMethod的调用行为。
原有测试代码片段:
public class Test { Func<Func<IFoo>, IBar, ISut> _sutFactory; public Test(Func<Func<IFoo>, IBar, ISut> sutFactory) { _sutFactory = sutFactory; } [Fact] public void TestCase() { Func<IFoo> fooFactory = () => { var mock => new Mock<IFoo>(); mock.Setup(x => x.fooMethod(); return mock.Object; } Mock<IBar> barMock = new Mock<IBar>(); sut = _sutFactory(fooFactory, barMock); sut.SutMethod(); barMock.Verify(mock => mock.barMethod(), Times.Once()); } }
修正后的测试实现
将IFoo的Mock实例提前提到工厂定义的外部,让测试上下文持有该Mock的引用,工厂委托直接返回这个Mock实例即可完成验证。
修正后的测试代码:
public class SutTests { private readonly Func<Func<IFoo>, IBar, ISut> _sutFactory; public SutTests(Func<Func<IFoo>, IBar, ISut> sutFactory) { _sutFactory = sutFactory; } [Fact] public void SutMethod_WhenExecuted_ShouldTriggerExpectedMethodCalls() { // 测试上下文持有IFoo Mock的引用 var fooMock = new Mock<IFoo>(); // 工厂委托直接返回可被外部引用的Mock实例 Func<IFoo> fooFactory = () => fooMock.Object; var barMock = new Mock<IBar>(); var sut = _sutFactory(fooFactory, barMock); sut.SutMethod(); // 验证IBar方法调用 barMock.Verify(mock => mock.barMethod(), Times.Once()); // 验证IFoo方法调用 fooMock.Verify(mock => mock.fooMethod(), Times.Once()); } }
可选补充验证
如果需要严格确认工厂的调用次数,可以在工厂委托内部增加计数逻辑:
int factoryInvocationCount = 0; Func<IFoo> fooFactory = () => { factoryInvocationCount++; return fooMock.Object; }; // SUT方法执行完成后做断言 Assert.Equal(1, factoryInvocationCount);
内容的提问来源于stack exchange,提问作者RadioMan85
相关产品推荐
相关产品推荐

