EG MOQとAutofixtureを使用して、模擬および匿名オブジェクトのハイブリッドを作成しますか?
-
27-10-2019 - |
質問
仕事中にこのように見えるクラスに遭遇しました:
public class MyObject
{
public int? A {get; set;}
public int? B {get; set;}
public int? C {get; set;}
public virtual int? GetSomeValue()
{
//simplified behavior:
return A ?? B ?? C;
}
}
問題は、a、b、cにアクセスしてgetSomevalue()メソッドを呼び出すコードを持っていることです(これは良いデザインではないと言われていますが、時には私の手が縛られています;-))。このオブジェクトのモックを作成したいと思います。同時に、A、B、Cがいくつかの値に設定されています。だから、私がMOQをそのように使用するとき:
var m = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock };
getSomeValue()メソッドで結果をセットアップしますが、すべてのプロパティはnullに設定されます(およびsetup()を使用してすべてのセットアップは非常に面倒です。実際のオブジェクトは厄介なデータオブジェクトであり、上記よりも多くのプロパティを持っているため簡略化された例)。
したがって、このような自動フィクストールを使用してください。
var fixture = new Fixture();
var anyMyObject = fixture.CreateAnonymous<MyObject>();
getSomevalue()メソッドへの呼び出しをstりませる能力がなくなります。
2つを組み合わせて、匿名の値とコール結果をセットアップする機能を持つ方法はありますか?
編集
Nemesvの答えに基づいて、私は次のユーティリティ方法を導き出しました(私がそれを正しくしたことを願っています):
public static Mock<T> AnonymousMock<T>() where T : class
{
var mock = new Mock<T>();
fixture.Customize<T>(c => c.FromFactory(() => mock.Object));
fixture.CreateAnonymous<T>();
fixture.Customizations.RemoveAt(0);
return mock;
}
解決
これ は 実際には自動フィクストールを使用することができますが、少し調整が必要です。拡張性ポイントはすべてそこにありますが、この場合、ソリューションは特に発見できないことを認めます。
ネストされた/複雑なタイプで動作したい場合、さらに難しくなります。
与えられた MyObject
上記のクラスとこれと同様に MyParent
クラス:
public class MyParent
{
public MyObject Object { get; set; }
public string Text { get; set; }
}
これらのユニットテストはすべて合格します。
public class Scenario
{
[Fact]
public void CreateMyObject()
{
var fixture = new Fixture().Customize(new MockHybridCustomization());
var actual = fixture.CreateAnonymous<MyObject>();
Assert.NotNull(actual.A);
Assert.NotNull(actual.B);
Assert.NotNull(actual.C);
}
[Fact]
public void MyObjectIsMock()
{
var fixture = new Fixture().Customize(new MockHybridCustomization());
var actual = fixture.CreateAnonymous<MyObject>();
Assert.NotNull(Mock.Get(actual));
}
[Fact]
public void CreateMyParent()
{
var fixture = new Fixture().Customize(new MockHybridCustomization());
var actual = fixture.CreateAnonymous<MyParent>();
Assert.NotNull(actual.Object);
Assert.NotNull(actual.Text);
Assert.NotNull(Mock.Get(actual.Object));
}
[Fact]
public void MyParentIsMock()
{
var fixture = new Fixture().Customize(new MockHybridCustomization());
var actual = fixture.CreateAnonymous<MyParent>();
Assert.NotNull(Mock.Get(actual));
}
}
モックハイブリッド顧問には何がありますか?これ:
public class MockHybridCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(
new MockPostprocessor(
new MethodInvoker(
new MockConstructorQuery())));
fixture.Customizations.Add(
new Postprocessor(
new MockRelay(t =>
t == typeof(MyObject) || t == typeof(MyParent)),
new AutoExceptMoqPropertiesCommand().Execute,
new AnyTypeSpecification()));
}
}
MockPostprocessor
, MockConstructorQuery
と MockRelay
クラスはで定義されています AutomoQ拡張機能 Autofixtureには、このライブラリへの参照を追加する必要があります。ただし、追加する必要はないことに注意してください AutoMoqCustomization
.
AutoExceptMoqPropertiesCommand
クラスは、この機会にもカスタム構築されています。
public class AutoExceptMoqPropertiesCommand : AutoPropertiesCommand<object>
{
public AutoExceptMoqPropertiesCommand()
: base(new NoInterceptorsSpecification())
{
}
protected override Type GetSpecimenType(object specimen)
{
return specimen.GetType();
}
private class NoInterceptorsSpecification : IRequestSpecification
{
public bool IsSatisfiedBy(object request)
{
var fi = request as FieldInfo;
if (fi != null)
{
if (fi.Name == "__interceptors")
return false;
}
return true;
}
}
}
このソリューションは、質問に対する一般的なソリューションを提供します。しかし、それは広範囲にテストされていないので、私はそれについてフィードバックを得たいと思っています。
他のヒント
おそらくより良い理由がありますが、これはうまくいきます:
var fixture = new Fixture();
var moq = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock };
moq.Setup(m => m.GetSomeValue()).Returns(3);
fixture.Customize<MyObject>(c => c.FromFactory(() => moq.Object));
var anyMyObject = fixture.CreateAnonymous<MyObject>();
Assert.AreEqual(3, anyMyObject.GetSomeValue());
Assert.IsNotNull(anyMyObject.A);
//...
最初は使用しようとしました fixture.Register(() => moq.Object);
それ以外の fixture.Customize
しかし、それは作成者関数を登録します OmitAutoProperties()
だから、それはあなたの場合にはうまくいきません。
3.20.0の時点で、使用できます AutoConfiguredMoqCustomization
. 。これにより、すべてのモックが自動的に構成され、メンバーの返品値が自動フィックス材によって生成されます。
var fixture = new Fixture().Customize(new AutoConfiguredMoqCustomization());
var mock = fixture.Create<Mock<MyObject>>();
Assert.NotNull(mock.Object.A);
Assert.NotNull(mock.Object.B);
Assert.NotNull(mock.Object.C);