C# 4.0 假装的第一步和动作类型的问题

C# 4.0 假装的第一步和动作类型的问题,c#-4.0,action,fakeiteasy,C# 4.0,Action,Fakeiteasy,我有以下(这里是简化的)代码,我想用它们进行测试 我想测试是否抛出了ServiceException,所以我就这样设置了测试 var repo = A.Fake<IRepository>(); A.CallTo(() => repo.FindByName(A<ISpec<Item>>.Ignored)) .Returns(null); var executor = A.Fake<IActionExecutor>(); executor.C

我有以下(这里是简化的)代码,我想用它们进行测试

我想测试是否抛出了
ServiceException
,所以我就这样设置了测试

var repo = A.Fake<IRepository>();
A.CallTo(() => repo.FindByName(A<ISpec<Item>>.Ignored))
 .Returns(null);

var executor = A.Fake<IActionExecutor>();
executor.Configure()
        .CallsTo(x => x.Rollback()).DoesNothing();
executor.Configure()
        .CallsTo(x => x.Commit()).DoesNothing();
executor.Configure()
        .CallsTo(x => x.TransactionalExecutionOf(A<Action>.Ignored))
        .CallsBaseMethod();
我得到以下信息

当前代理生成器无法拦截指定的方法 原因如下: -无法拦截密封的方法

如果我直接在服务上调用该方法,如

var service = new Service(executor, repo);
service.ServiceMethod("NotExists");
我收到这个消息

这是DynamicProxy 2错误:拦截器试图“继续” 对于方法“Void TransactionalExecutionOf(System.Action)”,该方法没有 目标。当调用没有目标的方法时,没有实现 “前进”到,拦截者有责任 模拟实现(设置返回值、输出参数等)


现在我有点困惑,不知道下一步该怎么办。

问题来自于您创建假的方式以及您以后期望它做什么:

var executor = A.Fake<IActionExecutor>();
// ...
executor.Configure()
    .CallsTo(x => x.TransactionalExecutionOf(A<Action>.Ignored))
    .CallsBaseMethod();
请注意,我们是基于实际的实现,而不是接口

然而,这引入了一组新的问题,因为
ActionExecutor
上的方法不是虚拟的,因此拦截器无法很好地连接到拦截器上。要使当前设置正常工作,您必须更改
ActionExecutor
,并使(所有)方法虚拟化

但是,您可能(甚至应该)希望避免修改现有代码(有时甚至可能不是一个选项)。然后,您可以设置
IActionExecutor
false,如下所示:

var executor = A.Fake<IActionExecutor>();
A.CallTo(() => executor.TransactionalExecutionOf(A<Action>.Ignored))
    .Invokes(f => new ActionExecutor()
        .TransactionalExecutionOf((Action)f.Arguments.First())
    );
var executor=A.Fake();
A.CallTo(()=>executor.TransactionalExecutionOf(A.Ignored))
.Invokes(f=>newActionExecutor()
.TransactionalExecutionOf((操作)f.Arguments.First())
);

这将允许您处理伪造的对象,但对
TransactionalExecutionOf
的调用除外,该调用将重定向到实际实现。

如果我使用实现和
virtual
它可以工作,但我不想将实现更改为virtual。我的方式/目标(太)错误了吗?还有其他的框架/库可以做这样的事情吗?@K.Hoffmann:没有,这是完全合理的,可以避免修改现有代码。我已经添加了一些变通方法,让您能够以不同的方式进行测试。据我所知,大多数免费模拟库不允许这样的事情(拦截非虚拟方法),因为它们都依赖于DynamicProxy和类似的机制。
var executor = A.Fake<IActionExecutor>();
// ...
executor.Configure()
    .CallsTo(x => x.TransactionalExecutionOf(A<Action>.Ignored))
    .CallsBaseMethod();
var executor = A.Fake<ActionExecutor>();
var executor = A.Fake<IActionExecutor>();
A.CallTo(() => executor.TransactionalExecutionOf(A<Action>.Ignored))
    .Invokes(f => new ActionExecutor()
        .TransactionalExecutionOf((Action)f.Arguments.First())
    );