本当にテストしたいことが正確に何であるかに応じて、実行できることはたくさんあります。
まず、この特定の質問の問題の多くは、IInvocationの非常に弱い型のAPIに起因していることと、Moqが通常プロパティを実装しているようにプロパティを実装していないことを指摘しておきます。
スタブが必要ない場合はセットアップしないでください
まず、必要がない場合は、ProxyプロパティとReturnValueプロパティの戻り値を設定する必要はありません。
AutoFixture.AutoMoqがMock<T>
インスタンスを設定する方法は、常にを設定することDefaultValue = DefaultValue.Mock
です。両方のプロパティの戻り型はデフォルトのコンストラクターobject
でobject
あり、デフォルトのコンストラクターがあるため、オブジェクト(実際には)が自動的にObjectProxy
返されます。
つまり、これらのテストも合格します。
[Theory, CustomAutoData]
public void TestA2(InterceptorA sut, IInvocation context)
{
sut.Intercept(context);
// assert
}
[Theory, CustomAutoData]
public void TestB2(InterceptorB sut, IInvocation context)
{
sut.Intercept(context);
// assert
}
ReturnValueを直接割り当てます
私の答えの残りの部分では、テストでプロパティ値を実際に割り当てたり、読み取ったりする必要があると想定します。
まず、ReturnValueを直接割り当てることで、重いMoq構文を減らすことができます。
[Theory, Custom3AutoData]
public void TestA3(InterceptorA sut, IInvocation context)
{
context.ReturnValue = "b";
sut.Intercept(context);
// assert
Assert.Equal("b", context.ReturnValue);
}
[Theory, Custom3AutoData]
public void TestB3(InterceptorB sut, IInvocation context)
{
context.ReturnValue = "z";
sut.Intercept(context);
// assert
Assert.Equal("z", context.ReturnValue);
}
ただし、ReturnValue
書き込み可能なプロパティであるため、この場合にのみ機能します。Proxy
読み取り専用であるため(コンパイルされないため)、プロパティでは機能しません。
これを機能させるには、IInvocation
プロパティを「実際の」プロパティとして扱うようにMoqに指示する必要があります。
public class Customization3 : CompositeCustomization
{
public Customization3()
: base(
new RealPropertiesOnInvocation(),
new AutoMoqCustomization())
{
}
private class RealPropertiesOnInvocation : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Register<Mock<IInvocation>>(() =>
{
var td = new Mock<IInvocation>();
td.DefaultValue = DefaultValue.Mock;
td.SetupAllProperties();
return td;
});
}
}
}
への呼び出しに注意してくださいSetupAllProperties
。
これが機能するのは、AutoFixture.AutoMoqが、インターフェイスのすべての要求をそのインターフェイスのモックの要求に中継することによって機能するためです。つまり、の要求IInvocation
はの要求に変換されMock<IInvocation>
ます。
テスト値を設定しないでください。それらを読み返します
最後に、自分自身に問いかける必要があります。これらのプロパティに特定の値( "a"、 "b"、 "z"など)を割り当てる必要が本当にありますか。AutoFixtureに必要な値を作成させることはできませんか?その場合、明示的に割り当てる必要がありますか?代わりに、割り当てられた値を読み戻すことはできませんか?
これはおそらく、私がシグナルタイプと呼ぶちょっとしたトリックです。シグナルタイプは、値の特定の役割を通知するクラスです。
各プロパティの信号タイプを導入します。
public class InvocationReturnValue
{
private readonly object value;
public InvocationReturnValue(object value)
{
this.value = value;
}
public object Value
{
get { return this.value; }
}
}
public class InvocationProxy
{
private readonly object value;
public InvocationProxy(object value)
{
this.value = value;
}
public object Value
{
get { return this.value; }
}
}
(値を常に文字列にする必要がある場合は、コンストラクターの署名を変更して、のstring
代わりにを要求することができますobject
。)
IInvocationインスタンスが構成されているときに同じインスタンスが再利用されることがわかるように、気になるシグナルタイプをフリーズします。
[Theory, Custom4AutoData]
public void TestA4(
InterceptorA sut,
[Frozen]InvocationProxy proxy,
[Frozen]InvocationReturnValue returnValue,
IInvocation context)
{
sut.Intercept(context);
// assert
Assert.Equal(proxy.Value, context.Proxy);
Assert.Equal(returnValue.Value, context.ReturnValue);
}
[Theory, Custom4AutoData]
public void TestB4(
InterceptorB sut,
[Frozen]InvocationReturnValue returnValue,
IInvocation context)
{
sut.Intercept(context);
// assert
Assert.Equal(returnValue.Value, context.ReturnValue);
}
ReturnValue
このアプローチの利点は、またはを気にしないテストケースでは、Proxy
これらのメソッド引数を省略できることです。
対応するカスタマイズは、前のカスタマイズの拡張です。
public class Customization4 : CompositeCustomization
{
public Customization4()
: base(
new RelayedPropertiesOnInvocation(),
new AutoMoqCustomization())
{
}
private class RelayedPropertiesOnInvocation : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Register<Mock<IInvocation>>(() =>
{
var td = new Mock<IInvocation>();
td.DefaultValue = DefaultValue.Mock;
td.SetupAllProperties();
td.Object.ReturnValue =
fixture.CreateAnonymous<InvocationReturnValue>().Value;
td.Setup(i => i.Proxy).Returns(
fixture.CreateAnonymous<InvocationProxy>().Value);
return td;
});
}
}
}
各プロパティの値は、IFixtureインスタンスに対応するシグナルタイプの新しいインスタンスを作成するように要求し、その値をアンラップすることによって割り当てられることに注意してください。
このアプローチは一般化することができますが、それがその要点です。