11

多くのテストケースで繰り返しフィクスチャ/モックセットアップをコーディングしていることがわかります-この場合のように:

var fixture = new Fixture().Customize(new AutoMoqCustomization());
var encodingMock = fixture.Freeze<Mock<IEncodingWrapper>>();
var httpClientMock = fixture.Freeze<Mock<IHttpWebClientWrapper>>();
var httpResponseMock = fixture.Freeze<Mock<IHttpWebResponseWrapper>>();
var httpHeaderMock = fixture.Freeze<Mock<IHttpHeaderCollectionWrapper>>();
var etag = fixture.CreateAnonymous<string>();
byte[] data = fixture.CreateAnonymous<byte[]>();
Stream stream =  new MemoryStream(data);

encodingMock.Setup(m => m.GetBytes(It.IsAny<string>())).Returns(data);
httpHeaderMock.SetupGet(m => m[It.IsAny<string>()]).Returns(etag).Verifiable();
httpClientMock.Setup(m => m.GetResponse()).Returns(httpResponseMock.Object);
httpResponseMock.Setup(m => m.StatusCode).Returns(HttpStatusCode.OK);
httpResponseMock.SetupGet(m => m.Headers).Returns(httpHeaderMock.Object);
httpResponseMock.Setup(m => m.GetResponseStream()).Returns(stream);

テストは自己完結型であり、最初から最後まで読み取り可能である必要があるという考えに従って、魔法のセットアップ/分解メソッドを使用しません。

何らかの方法(AutoFixtureのカスタマイズ、ヘルパーメソッド)で、これらのテストの「うんざりする作業」を減らすことができますか?

4

3 に答える 3

15

Growing Object-Oriented Software(GOOS)から、良いアドバイスが得られます。テストを作成するのが難しい場合は、テスト対象システム(SUT)のAPIに関するフィードバックです。SUTの再設計を検討してください。この特定の例では、SUTに少なくとも4つの依存関係があるように見えます。これは、単一責任の原則に違反していることを示している可能性があります。ファサードサービスにリファクタリングすることは可能でしょうか?

GOOSからのもう1つのすばらしいアドバイスは、

上記の例では、実際にはクエリであるメソッドに対して多くのMoqセットアップを実行する必要があるように見えます。これは、テスト臭も示しています。どこかにデメテルの法則違反はありますか?メソッドチェーンを切断することは可能でしょうか?

于 2012-05-08T08:10:36.263 に答える
13

含まれているすべてのカスタマイズを使用して、フィクスチャをカスタマイズする複合カスタマイズを作成できます。

public class HttpMocksCustomization : CompositeCustomization
{
    public HttpMocksCustomization()
        : base(
            new AutoMoqCustomization(),
            new HttpWebClientWrapperMockCustomization(),
            new HttpWebResponseWrapperMockCustomization()
            // ...
            )
    {
    }
}

各カスタマイズは、次のように定義できます。

public class HttpWebClientWrapperMockCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        var mock = new Mock<IHttpWebClientWrapper>();
        mock.Setup(m => m.GetResponse()).Returns(httpResponseMock.Object);

        fixture.Inject(mock);
    }
}

public class HttpWebResponseWrapperMockCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        var mock = new Mock<IHttpWebResponseWrapper>();
        mock.Setup(m => m.StatusCode).Returns(HttpStatusCode.OK);

        fixture.Inject(mock);
    }
}

// The rest of the Customizations.

次に、テストメソッド内でこれを行うことができます。

var fixture = new Fixture().Customize(new HttpMocksCustomization());

そうすれば、モックインスタンスをリクエストするときに、セットアップ手順を繰り返す必要がありません。以前にカスタマイズしたものが返されます:

var httpClientMock = fixture.Freeze<Mock<IHttpWebClientWrapper>>();

ただし、xUnit.netを使用すると、さらに簡略化できます。

AutoDataAttributeから派生した型を作成して、AutoFixtureによって生成された自動生成されたデータ標本をxUnit.netのTheory属性の拡張として提供できます。

public class AutoHttpMocksDataAttribute : AutoDataAttribute
{
    public AutoHttpMocksDataAttribute()
        : base(new Fixture().Customize(new HttpMocksCustomization()))
    {
    }
}

次に、テストメソッドでモックを引数として渡すことができます。

[Theory, AutoHttpMocksData]
public void MyTestMethod([Freeze]Mock<IHttpWebClientWrapper> httpClientMock, [Freeze]Mock<IHttpWebResponseWrapper> httpResponseMock)
{
    // ...
} 
于 2012-05-08T07:13:42.883 に答える
4

すべてのテストでこのコードを使用する場合は、セットアップ/ティアダウンメソッドに配置する必要があります。すべての単体テストがそれに依存している限り、セットアップ/ティアダウンの方法が多少複雑であっても問題ありません。これは、すべてのテストで複雑なものをすべて複製するよりも確かに優れています。テストを読んだとき、セットアップとティアダウンは暗黙的に各テストの一部であることがわかっているので、読みやすさも失われることはないと思います。避けるべきことは、すべてのテストが必要とするわけではないものをセットアップに含めることです。これにより、セットアップ方法がすべてのテストとうまく一致しないという混乱した状況が発生します。理想的には、セットアップ方法はすべてのテストに100%適用する必要があります。

共有コードがすべてのテストで使用されていない場合は、共有コードをヘルパー関数に抽出します。適切なテストコードを作成することは、他の適切なコードを作成することと同じであり、同じ原則が適用されます。

于 2012-05-08T06:22:03.727 に答える