6

仕事中に次のようなクラスに遭遇しました。

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()を使用してすべてを設定するのは非常に面倒です)簡略化された例)。

一方、次のようにAutoFixtureを使用します。

var fixture = new Fixture();
var anyMyObject = fixture.CreateAnonymous<MyObject>();

GetSomeValue()メソッドの呼び出しを失敗させることができなくなります。

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;
}
4

3 に答える 3

5

これ実際にはAutoFixtureで行うことができますが、少し調整する必要があります。拡張性のポイントはすべてそこにありますが、この場合、解決策は特に発見可能ではないことを認めます。

ネストされた/複雑なタイプで動作させたい場合は、さらに難しくなります。

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));
    }
}

MockHybridCustomizationには何が含まれていますか?これ:

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()));
    }
}

、およびクラスはAutoFixtureのAutoMoq拡張機能MockPostprocessorで定義されているため、このライブラリへの参照を追加する必要があります。ただし、を追加する必要はないことに注意してください。MockConstructorQueryMockRelayAutoMoqCustomization

この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;
        }
    }
}

このソリューションは、質問に対する一般的なソリューションを提供します。ただし、十分にテストされていないため、フィードバックをお寄せください。

于 2012-02-19T04:29:53.127 に答える
4

おそらくもっと良い理由がありますが、これはうまくいきます:

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()ため、機能しません。

于 2012-02-18T14:10:37.293 に答える
2

3.20.0以降、を使用できますAutoConfiguredMoqCustomization。これにより、メンバーの戻り値がAutoFixtureによって生成されるように、すべてのモックが自動的に構成されます。

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);
于 2014-08-21T17:49:25.737 に答える