我有以下(这里是简化的)代码,我想用FakeItEasy测试这些代码。
public class ActionExecutor : IActionExecutor
{
public void TransactionalExecutionOf(Action action)
{
try
{
// ...
action();
// ...
}
catch
{
// ...
Rollback();
}
}
public void Commit()
{ }
public void Rollback()
{ }
}
public class Service : IService
{
private readonly IRepository _repository;
private readonly IActionExecutor _actionExecutor;
// ctor for CI
public void ServiceMethod(string name)
{
_actionExecutor.TransactionalExecutionOf(() =>
{
var item = _repository.FindByName(ItemSpecs.FindByNameSpec(name));
if (item == null) throw new ServiceException("Item not found");
item.DoSomething();
_actionExecutor.Commit();
}
}
}
我想测试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")
.Throws(new ServiceException());
我收到以下消息
当前代理生成器无法截获指定的方法原因如下:-密封的方法不能被拦截。
如果我直接在这样的服务上调用该方法
var service = new Service(executor, repo);
service.ServiceMethod("NotExists");
我收到这个消息
这是DynamicProxy2错误:拦截器尝试"继续"方法"Void TransactionalExecutionOf(System.Action)"没有目标当调用没有目标的方法时,没有实现"继续",拦截者有责任模拟实现(设置返回值、输出参数等)
现在我有点困惑,不知道下一步该怎么办。
问题来自于你创建假的方式以及你以后期望它做什么:
var executor = A.Fake<IActionExecutor>();
// ...
executor.Configure()
.CallsTo(x => x.TransactionalExecutionOf(A<Action>.Ignored))
.CallsBaseMethod();
什么基本方法?FakeIseasy不知道基类是什么,因此在第二种情况下会出现DynamicProxy2
异常。您可以通过以下方式创建部分模拟:
var executor = A.Fake<ActionExecutor>();
请注意,我们基于实际实现,而不是接口
然而,这引入了一组新的问题,因为ActionExecutor
上的方法不是虚拟的,因此拦截器不能很好地拦截它们。要使当前的设置正常工作,您必须更改ActionExecutor
并使(所有)方法成为虚拟方法。
然而,您可能(甚至应该)希望避免修改现有代码(有时甚至可能不是一种选择)。然后你可以设置你的IActionExecutor
假像这样:
var executor = A.Fake<IActionExecutor>();
A.CallTo(() => executor.TransactionalExecutionOf(A<Action>.Ignored))
.Invokes(f => new ActionExecutor()
.TransactionalExecutionOf((Action)f.Arguments.First())
);
这将允许您处理伪造的对象,但对TransactionalExecutionOf
的调用除外,该调用将重定向到实际实现。