【问题标题】:first steps with FakeItEasy and problems with Action typeFakeItEasy 的第一步和 Action 类型的问题
【发布时间】:2012-01-29 14:17:52
【问题描述】:

我想用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)' 没有 目标。调用没有目标的方法时没有实现 “继续”,拦截器有责任 模仿实现(设置返回值,输出参数等)

现在有点迷茫,不知道接下来该怎么办。

【问题讨论】:

    标签: c#-4.0 action fakeiteasy


    【解决方案1】:

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

    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 fake:

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

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

    【讨论】:

    • 如果我采用实现方式并且virtual 它可以工作,但我不想将实现更改为虚拟。我的方式/我的目标(也)错了吗?是否有其他框架/库可以做这样的事情?
    • @K.Hoffmann:不,避免修改现有代码是完全合理的。我添加了有关如何以不同方式进行测试的解决方法。据我所知,大多数免费的模拟库都不允许这样的事情(拦截非虚拟方法),因为它们都依赖于 DynamicProxy 和类似的机制。
    猜你喜欢
    • 2021-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-14
    相关资源
    最近更新 更多