我有以下(这里简化的)代码,我想用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)'没有 目标。在没有目标的情况下调用方法时,没有实现 “继续”,它是拦截器的责任 模仿实现(设置返回值,输出参数等)
现在我有点困惑,不知道下一步该做什么。
答案 0 :(得分:6)
问题来自于您创建假冒的方式以及您以后期望它做的事情:
var executor = A.Fake<IActionExecutor>();
// ...
executor.Configure()
.CallsTo(x => x.TransactionalExecutionOf(A<Action>.Ignored))
.CallsBaseMethod();
基本方法是什么? FakeItEasy不知道基类是什么,因此在你的第二种情况下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
除外,它将被重定向到实际实现。