使用FakeItEasy的第一步和Action类型的问题

Khh*_*Khh 3 action fakeiteasy c#-4.0

我有以下(这里简化的)代码,我想用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(); 
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我想测试它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();
Run Code Online (Sandbox Code Playgroud)

使用以下代码

var service = new Service(executor, repo);
service.ServiceMethod("notExists")
       .Throws(new ServiceException());
Run Code Online (Sandbox Code Playgroud)

我收到以下消息

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

如果我直接在服务上调用方法就好了

var service = new Service(executor, repo);
service.ServiceMethod("NotExists");
Run Code Online (Sandbox Code Playgroud)

我收到这条消息

这是一个DynamicProxy2错误:拦截器试图为没有目标的方法'Void TransactionalExecutionOf(System.Action)''继续'.当调用没有目标的方法时,没有实现"继续",拦截器有责任模仿实现(设置返回值,输出参数等)

现在我有点困惑,不知道接下来该做什么.

k.m*_*k.m 6

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

var executor = A.Fake<IActionExecutor>();
// ...
executor.Configure()
    .CallsTo(x => x.TransactionalExecutionOf(A<Action>.Ignored))
    .CallsBaseMethod();
Run Code Online (Sandbox Code Playgroud)

什么基础方法?FakeItEasy不知道基类是什么,因此DynamicProxy2在第二种情况下是例外.您可以通过以下方式创建部分模拟:

var executor = A.Fake<ActionExecutor>();
Run Code Online (Sandbox Code Playgroud)

请注意,我们基于实际实现,而不是接口

然而,这引入了一组新的问题,因为方法ActionExecutor不是虚拟的,因此拦截器不能很好地连接它们 - 拦截它们.要使当前设置正常工作,您必须更改ActionExecutor并使(全部)方法成为虚拟方法.

但是,您可能(或者甚至应该)希望避免修改现有代码(有时甚至可能不是一个选项).然后你可以这样设置你的IActionExecutor假货:

var executor = A.Fake<IActionExecutor>();
A.CallTo(() => executor.TransactionalExecutionOf(A<Action>.Ignored))
    .Invokes(f => new ActionExecutor()
        .TransactionalExecutionOf((Action)f.Arguments.First())
    );
Run Code Online (Sandbox Code Playgroud)

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