如何通过反射处理装饰器以验证内部mock是否被正确调用?
实现思路
核心逻辑
你可以结合反射+FakeItEasy的动态调用匹配能力实现批量验证,不需要为每个方法单独编写断言,步骤如下:
- 第一步:初始化测试依赖,创建
IFoo的Fake实例,再用该Fake实例构造FooDecorator对象。 - 第二步:获取
IFoo定义的所有公共实例方法,提前维护需要跳过验证的方法集合(比如存在特殊逻辑不需要委托的方法,可以直接存方法名,也可以通过自定义Attribute标记后反射筛选)。 - 第三步:对每个需要验证的方法,通过反射调用FakeItEasy的
A.Dummy()方法,生成对应参数类型的测试参数值。 - 第四步:用反射调用装饰器实例的当前方法,传入上一步生成的参数集合。
- 第五步:用FakeItEasy的动态调用匹配API,验证Fake实例的对应方法被调用了一次,且入参与传入装饰器的参数完全一致。
示例实现代码
using FakeItEasy; using System.Reflection; public void TestDecoratorDelegation() { // 1. 初始化测试对象 var fakeFoo = A.Fake<IFoo>(); var decorator = new FooDecorator(fakeFoo); // 2. 配置跳过验证的方法列表,存方法名即可 var skipMethods = new HashSet<string> { nameof(IFoo.需要跳过验证的方法名) }; // 3. 遍历IFoo的所有公共实例方法 var fooMethods = typeof(IFoo).GetMethods(BindingFlags.Public | BindingFlags.Instance); foreach (var method in fooMethods) { if (skipMethods.Contains(method.Name)) { continue; } // 4. 生成对应参数的Dummy值 var parameters = method.GetParameters(); var args = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { // 反射调用A.Dummy<T>生成对应类型的测试参数 var dummyMethod = typeof(A).GetMethod(nameof(A.Dummy), BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(parameters[i].ParameterType); args[i] = dummyMethod.Invoke(null, null); } // 5. 调用装饰器的对应方法 method.Invoke(decorator, args); // 6. 验证Fake的对应方法被正确调用,参数完全匹配 A.CallTo(fakeFoo) .Where(call => call.Method == method) .WithArguments(args) .MustHaveHappenedOnceExactly(); // 每次验证完重置Fake的调用记录,避免不同方法之间互相干扰 Fake.ClearRecordedCalls(fakeFoo); } }
扩展优化
如果需要更灵活的跳过规则,可以自定义一个[SkipDelegationVerification]特性,打在装饰器有特殊逻辑的方法上,反射遍历的时候判断方法是否带有该特性即可自动跳过,不需要手动维护方法名列表。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

