7

このシステムをテストする場合:

public class MySut
{
    private readonly IHardToMockDependency _hardToMockDependency;

    public MySut(IHardToMockDependency hardToMockDependency,
                 IOtherDependency otherDependency)
    {
        _hardToMockDependency = hardToMockDependency;
    }

    public string GetResult()
    {
        return _hardToMockDependency.GetResult();
    }
}

public interface IOtherDependency { }

public interface IHardToMockDependency
{
    string GetResult();
}

そして、この単体テスト:

internal class FakeHardToMockDependency : IHardToMockDependency
{
    private readonly string _result;

    public FakeHardToMockDependency(string result)
    {
        _result = result;
    }

    public string GetResult()
    {
        return _result;
    }
}

public class MyTests
{
    [Fact]
    public void GetResultReturnsExpected()
    {
        string expectedResult = "what I want";
        var otherDependencyDummy = new Mock<IOtherDependency>();
        var sut = new MySut(new FakeHardToMockDependency(expectedResult),
                            otherDependencyDummy.Object);

        var actualResult = sut.GetResult();

        Assert.Equal(expectedResult, actualResult);
    }
}

AutoFixture.XunitとAutoFixture.AutoMoqを使用するように変換するにはどうすればよいですか(手動の偽物を使用している間)?

実際のテストでは、手動の偽物はより複雑なインターフェースと動作を持ちます。匿名変数(expectedResult文字列)を手動の偽物のコンストラクターに渡したいことに注意してください。

4

3 に答える 3

10

ここにはすでにいくつかの良い答えがありますが、FakeHardToMockDependency クラスの不変条件を少し緩めることを含む、より単純な代替案を提案したいと思います。それを公開し、コンストラクターから切り離された結果を割り当てる方法を提供します。

public class FakeHardToMockDependency : IHardToMockDependency
{
    private string _result;

    public FakeHardToMockDependency(string result)
    {
        _result = result;
    }

    internal string Result
    {
        get { return _result; }
        set { _result = value; }
    }

    public string GetResult()
    {
        return _result;
    }
}

内部プロパティを追加readonlyし、フィールドからキーワードを削除したことに注意してください。

これにより、元のテストを次のようにリファクタリングできます。

[Theory, AutoMoqData]
public void GetResultReturnsExpected_AutoDataVersion(
    [Frozen(As = typeof(IHardToMockDependency))]FakeHardToMockDependency fake,
    MySut sut)
{
    var expected = "what I want";
    fake.Result = expected;

    var actual = sut.GetResult();

    Assert.Equal(expected, actual);
}

完全を期すために、AutoMoqDataAttribute コードを次に示します。

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute()
        : base(new Fixture().Customize(new AutoMoqCustomization()))
    {
    }
}
于 2013-03-16T08:18:40.290 に答える
4

マニュアル フェイクに渡す必要のあるパラメーターの種類によっては、AutoFixture の組み込みのInlineAutoDataAttribute.

これらを考えると

public interface IHardToMockDependency
{
    string Value { get; }
}

public class FakeHardToMockDependency : IHardToMockDependency
{
    private readonly string _value;

    public FakeHardToMockDependency(string value)
    {
        _value = value;
    }

    #region IHardToMockDependency Members

    public string Value
    {
        get { return this._value; }
    }

    #endregion IHardToMockDependency Members
}

インターフェイスICustomizationの実装を作成する方法をフィクスチャ オブジェクトに伝える実装を作成します。IHardToFakeDependency

public class FakeHardToMockDependencyCustomization : ICustomization
{
    private readonly string _value;

    public FakeHardToMockDependencyCustomization(string value)
    {
        _value = value;
    }

    #region ICustomization Members

    public void Customize(IFixture fixture)
    {
        fixture.Register<IHardToMockDependency>(() => new FakeHardToMockDependency(this._value));
    }

    #endregion ICustomization Members
}

もちろん、これは渡したい文字列を知る必要があることに注意してください。

次に、これを で使用する他のカスタマイズと合わせてロールアップしますCompositeCustomization

public class ManualFakeTestConventions : CompositeCustomization
{
    public ManualFakeTestConventions(string value)
        : base(new FakeHardToMockDependencyCustomization(value), new AutoMoqCustomization())
    {
    }
}

ここで Mark Seemannが説明しているように、カスタマイズは常に最も具体的なものから最も一般的なものの順に配置してください。

AutoDataAttribute次に、このカスタマイズを使用する実装を作成します。

public class ManualFakeAutoDataAttribute : AutoDataAttribute
{
    public ManualFakeAutoDataAttribute(string value)
        : base(new Fixture().Customize(new ManualFakeTestConventions(value)))
    {
    }
}

これは、 と同じように使用できるようになりましたInlineAutoDataAttribute:

public class ManualFakeTests
{
    [Theory, ManualFakeAutoData("iksdee")]
    public void ManualFake(IHardToMockDependency fake)
    {
        Assert.IsType<FakeHardToMockDependency>(fake);
        Assert.Equal("iksdee", fake.Value);
    }
}

[Frozen]Theory パラメータに属性を適用することで、自動作成された SUT インスタンスにすぐに挿入することもできます。

    [Theory, ManualFakeAutoData("iksdee")]
    public void SutWithManualFake([Frozen] IHardToMockDependency fake, MySut sut)
    {

    }

これにより、MySutインスタンスとIHardToMockDependencyコンストラクターに必要なインスタンスが作成されます。これには、 AutoFixture で規則を指定し、そのインスタンスそのものを変数FakeHardToMockDependencyCustomizationとして提供しfakeます。

偽物を凍結しなくても正しいFakeHardToMockDependencyインスタンスが得られ、sut に挿入されますが、カスタマイズでファクトリ デリゲートを登録しているため、それらは異なることに注意してください。インスタンスをフリーズすると、フィクスチャはインターフェイスに対する後続のリクエストに対して常に同じインスタンスを返すようになります。

ただし、これにはいくつかの注意事項があります。

  • パラメータとして渡す文字列への参照がないため、文字列リテラルとして 2 回指定する必要があります。たとえば、テストクラス内の文字列定数を使用してこれを回避できます。
  • .NET で属性パラメーターとして使用できる型の数は限られています。基本的な型さえあればいいのですが、パラメータリストでコンストラクタなどを呼び出すことはできません。
  • この属性は、インスタンスが必要な場合にのみ使用してくださいIHardToFakeDependency。それ以外の場合は、常に文字列パラメーターを渡す必要があります。使用する必要がある一連の標準カスタマイズがある場合は、それらのみを含む別の属性を作成します。
  • の機能がInlineAutoDataAttribute同時に必要な場合は、両方の機能を組み合わせた別の属性も作成する必要があります。

具体的な状況によっては、xUnit.net の も見たいと思うかもしれませんがPropertyDataAttribute、それを使用していることはほとんどありません。

一般に、私の意見では、カスタマイズと autodata 属性を操作する方法と、独自の属性をいつどのように作成するかを理解することが、AutoFixture を効果的に使用し、実際に作業を節約するための鍵です。

テストが必要な特定のドメインでコードを頻繁に作成する場合は、xUnit.net、AutoFixture の隣にドロップすればいつでもすぐに使用できる、カスタマイズ、属性、およびスタブ オブジェクトを含むライブラリを作成するのが理にかなっています。とモク。私は私が私のものを作ってとてもうれしいことを知っています。

ああ、また: モックするのが難しい依存関係を持つことは、設計上の問題を示している可能性があります。嘲笑するのがなぜそんなに難しいのですか?

于 2013-03-16T01:43:05.823 に答える
3

おそらく、これは最も慣用的な Autofixture セットアップではありませんが、確実に機能します。

[Fact]
public void GetResultReturnsExpected()
{
    var fixture = new Fixture()
        .Customize(new AutoMoqCustomization());

    var expectedResult = fixture.Create<string>();

    fixture.Register<IHardToMockDependency>(
        () => new FakeHardToMockDependency(expectedResult));

    var sut = fixture.Create<MySut>();

    var actualResult = sut.GetResult();

    Assert.Equal(expectedResult, actualResult);
}

使用したい場合は、この素晴らしい記事に基づいAutoDataて独自のものを作成し、フィクスチャのカスタマイズの一部またはすべてを非表示にすることができます。AutoMoqData

何かのようなもの:

public class MySutAutoDataAttribute : AutoDataAttribute
{
    public MySutAutoData()
        : base(new Fixture()
            .Customize(new AutoMoqCustomization()))
    {
        Fixture.Freeze<string>();

        Fixture.Register<IHardToMockDependency>(
            () => new FakeHardToMockDependency(Fixture.Create<string>()));
    }
}

そして、次のように使用できます。

[Theory, MySutAutoData]
public void GetResultReturnsExpected(MySut sut, string expectedResult)
{
    var actualResult = sut.GetResult();

    Assert.Equal(expectedResult, actualResult);
}

ただし、たとえば、多くの改善の余地があることに注意してくださいMySutAutoDataAttribute。これはあまり一般的ではなく Fixture.Freeze<string>();、テストで複数の文字列を使用している場合に問題を引き起こす可能性があります。

于 2013-03-15T22:31:19.130 に答える