RhinoMocks を使用して 3 つの異なる型を持つジェネリック メソッドを AssertWasCalled するにはどうすればよいですか?
-
26-09-2019 - |
質問
Rhino Mocks AAA 構文を学ぼうとしていますが、特定のメソッド (任意の引数の値) が呼び出されたことをアサートするのに苦労しています。私はテスト フレームワークとして Machine.payments を使用しています。
この特定のメソッドは汎用的なもので、3 つの異なるタイプで 3 回呼び出されたことを確認したいと考えています。
repo.Save<T1>(anything), repo.Save<T2>(anything), and repo.Save<T3>(anything)
タイプごとに関数をスタブ化しました。しかし、興味深い結果が得られました。(下に)
[Subject("Test")]
public class When_something_happens_with_constraint
{
static IRepository repo;
static TestController controller;
static ActionResult result;
Establish context = () =>
{
repo = MockRepository.GenerateMock<IRepository>();
controller = new TestController(repo);
repo.Stub(o => o.Save<Something>(Arg<Something>.Is.Anything));
repo.Stub(o => o.Save<SomethingElse>(Arg<SomethingElse>.Is.Anything));
repo.Stub(o => o.Save<AnotherOne>(Arg<AnotherOne>.Is.Anything));
};
//post data to a controller
Because of = () => { result = controller.SaveAction(new SomethingModel() { Name = "test", Description = "test" }); };
//controller constructs its own something using the data posted, then saves it. I want to make sure three calls were made.
It Should_save_something = () => repo.AssertWasCalled(o => o.Save<Somethign>(Arg<Something>.Is.Anything));
It Should_save_something_else = () => repo.AssertWasCalled(o => o.Save<SomethingElse>(Arg<SomethingElse>.Is.Anything));
It Should_save_another_one = () => repo.AssertWasCalled(o => o.Save<AnotherOne>(Arg<AnotherOne>.Is.Anything));
}
結果は 2 つの例外と 1 つのパスです。
最初の呼び出しでは以下がスローされます。
System.InvalidOperationException:検証対象のセットアップはありません。アクション内のメソッド呼び出しが仮想 (C#) / オーバーライド可能な (VB.Net) メソッド呼び出しであることを確認してください。
2 番目のものは次のようにスローします。
System.InvalidOperationException:記録中のモック メソッド呼び出し内でのみ Arg を使用してください。1 つの引数が予期されていますが、2 つは定義されています。
3 つ目はパスします...何らかの奇妙な理由で。
また、セットアップで Expect で GenerateMock() を使用したり、スタブで GenerateStub() を使用したりしてみました。どちらもまったく同じ結果になりました。何か間違ったことをしているに違いない。
私が使用しているもの:MachineSpec 0.3.0.0 および RhinoMocks 3.6.0.0
何か案は?
- - -修理済み - - - - -
Lee の協力による完全版 (実用版) は次のとおりです。追加の (非 linq) レイヤーを使用しています。私の実際の問題は、テストの 1 つがオフラインの実際のコードで間違ったラムダ変数を再利用していたことでした。それは Should_do_something = () => repo.AssertWasCalled(ああ=>リポジトリ.Save(データ));//不正なラムダ
そこで、参考のために正しいテストのサンプルを次に示します。
using System;
using System.Linq;
using System.Collections.Generic;
using Machine.Specifications;
using Rhino.Mocks;
namespace OnlineTesting.Specifications
{
public interface Repository
{
void Save<T>(T data);
IQueryable<T> All<T>();
}
public interface Service
{
void SaveItem(Item data);
void SaveAnotherItem(AnotherItem data);
void SaveOtherItem(OtherItem data);
List<Item> GetItems();
List<AnotherItem> GetAnotherItems();
List<OtherItem> GetOtherItems();
}
public class ConcreteService : Service
{
Repository repo;
public ConcreteService(Repository repo)
{
this.repo = repo;
}
public void SaveItem(Item data)
{
repo.Save(data);
}
public void SaveAnotherItem(AnotherItem data)
{
repo.Save(data);
}
public void SaveOtherItem(OtherItem data)
{
repo.Save(data);
}
public List<Item> GetItems()
{
return repo.All<Item>().ToList();
}
public List<AnotherItem> GetAnotherItems()
{
return repo.All<AnotherItem>().ToList();
}
public List<OtherItem> GetOtherItems()
{
return repo.All<OtherItem>().ToList();
}
}
public class Item
{
public int Id { get; set; }
}
public class OtherItem
{
}
public class AnotherItem
{
}
public class When_something_else_happens
{
Establish context = () =>
{
_repository = MockRepository.GenerateMock<Repository>();
_service = new ConcreteService(_repository);
_controller = new TestController(_service);
_repository.Stub(o => o.Save<Item>(Arg<Item>.Is.Anything)).WhenCalled(
new Action<MethodInvocation>((o) =>
{
var data = o.Arguments.FirstOrDefault() as Item;
if (data != null && data.Id == 0)
data.Id++;
}));
};
Because of = () => _controller.DoSomethingElse();
It should_save_the_first_thing = () =>
_repository.AssertWasCalled(repo => repo.Save(Arg<Item>.Is.Anything));
It should_save_the_other_thing = () =>
_repository.AssertWasCalled(repo => repo.Save(Arg<OtherItem>.Is.Anything));
It should_save_the_last_thing = () =>
_repository.AssertWasCalled(repo => repo.Save(Arg<AnotherItem>.Is.Anything));
static Repository _repository;
static TestController _controller;
static Service _service;
}
public class TestController
{
readonly Service _service;
public TestController(Service service)
{
_service = service;
}
public void DoSomethingElse()
{
_service.SaveItem(new Item());
_service.SaveOtherItem(new OtherItem());
_service.SaveAnotherItem(new AnotherItem());
}
}
}
解決
これを試してみてください。
[Subject("Test")]
public class When_something_happens_with_constraint
{
static IRepository repo;
static TestController controller;
static ActionResult result;
Establish context = () =>
{
repo = MockRepository.GenerateMock<IRepository>();
controller = new TestController(repo);
};
//post data to a controller
Because of = () => result = controller.SaveAction(new SomethingModel() { Name = "test", Description = "test" });
//controller constructs its own something using the data posted, then saves it. I want to make sure three calls were made.
It Should_save_something = () => repo.AssertWasCalled(o => o.Save(Arg<Something>.Is.Anything));
It Should_save_something_else = () => repo.AssertWasCalled(o => o.Save(Arg<SomethingElse>.Is.Anything));
It Should_save_another_one = () => repo.AssertWasCalled(o => o.Save(Arg<AnotherOne>.Is.Anything));
}
他のヒント
誰もがごまかしているように見える点は、あなたが スタブする必要はありません 「アサートが呼び出された」を実行するため。そして、あなたが書いた方法では、 Arg<T>.Is.Anything
, の場合、型は無視されます。一般的な制約は検証していません。使いたい Arg<T>.Is.TypeOf<T>
. 。チェックしてください ドキュメンテーション 詳細については。
-----------------------------------------------
| Arg<T>.Is | |
===============================================
| Anything() | No constraints |
-----------------------------------------------
| TypeOf<T>() | Argument is of a certain type |
-----------------------------------------------
あなたの 修理済み コードスニペットはまだ複雑すぎます。あなたは使用していません data
「呼び出されたとき」に保存しているオブジェクト。また、単純な「アサートが呼び出された」場合にはこれは必要ありません。
@leebrandt のコードは、自動モッキング コンテナーを導入していなくても、可能な限り正確かつシンプルに見えます。