3

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)' に対して '続行' を試みました。ターゲットなしでメソッドを呼び出す場合、「続行」する実装はなく、実装を模倣するのはインターセプターの責任です (戻り値の設定、引数の出力など)。

今、私は少し混乱していて、次に何をすべきかわかりません。

4

1 に答える 1

6

問題は、偽物を作成する方法と、後でそれが行うことを期待することから生じます。

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

どのベースメソッド?FakeItEasy は、基本クラスが何であるかを認識していないため、DynamicProxy22 番目のケースの例外です。この方法で部分モックを作成できます。

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これにより、実際の実装にリダイレクトされる呼び出しを除いて、偽のオブジェクトで作業することができます。

于 2012-01-29T15:23:24.230 に答える