web-dev-qa-db-ja.com

DocumentDb単体テストのためにDocumentClientをモックする方法(私)はどうすればよいですか?

新しいCosmosDbエミュレータから、基本的なdocumentdb操作を実行するための一種のリポジトリを取得しました。このリポジトリは他のクラスに注入されます。基本的なクエリを単体テストしたかった。

_public class DocumentDBRepository<T> where T : class
{
 //Details ommited...

    public IQueryable<T> GetQueryable()
    {
        return _client.CreateDocumentQuery<T>(
            UriFactory.CreateDocumentCollectionUri(_databaseId, _collectionId),
            new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true });
    }

    public async Task<IEnumerable<T>> ExecuteQueryAsync(IQueryable<T> query)
    {
        IDocumentQuery<T> documentQuery = query.AsDocumentQuery();
        List<T> results = new List<T>();
        while (documentQuery.HasMoreResults)
        {
            results.AddRange(await documentQuery.ExecuteNextAsync<T>());
        }

        return results;
    }


}
_

このリポジトリには、ドキュメントクライアントが機能する必要があり、ドキュメントクライアントもコンストラクタに挿入されます。

_public DocumentDBRepository(string databaseId, IDocumentClient client)
{
    _client = client;
    _databaseId = databaseId;
    _collectionId = GetCollectionName();
}
_

私の最初のアプローチはCosmosDbエミュレーターを使用することでしたが、そのためにはエミュレーターを実行する必要があり、これは気に入らず、テストが遅くなります。

2番目のアプローチは、ドキュメントクライアントのモックを試して使用することでした。

_var data = new List<MyDocumentClass>
{
    new MyDocumentClass{ Description= "BBB" },
    new MyDocumentClass{ Description= "ZZZ" },

}
.AsQueryable()
.OrderBy(q => q.Description);
var client = new Mock<IDocumentClient>();
client.As<IDocumentClient>()
    .Setup(foo => foo.CreateDocumentQuery<MyDocumentClass>(It.IsAny<Uri>(), It.IsAny<FeedOptions>()))
    .Returns(data);

DocumentDBRepository<MyDocumentClass> repo= new DocumentDBRepository<MyDocumentClass>(cosmosDatabase, client.Object);
_

このリポジトリを使用するコードは次のように機能します。

_var query = _documentsRepository.GetQueryable()
                .Where(d => d.Description = description)
                .OrderByDescending(d => d.description)
                .Take(100);
//Execute query async fails. 
var result = await _documentsRepository.ExecuteQueryAsync(query);
_

リポジトリがIQueryableIDocumentQueryオブジェクトに変換しようとするため失敗します。これはDocumentDb APIに非常に固有です(上記のExecuteQueryAsyncメソッドを参照)。後で、HasMoreResultsメソッドを実行します。したがって、問題は、.asDocumentQuery()をモックしてオブジェクトを返す場合でも、HasMoreResultsおよびExecuteNextAsyncの結果を提供する方法がわからないので、理にかなっています私の単体テストのために。

3番目のオプションは、DocumentClientオブジェクトの代わりにリポジトリをまっすぐに模擬することです。私は思うに、もっとシンプルになりますが、DocumentDb APIの多くはテストしません。

17
Ernesto

これの鍵は、呼び出しているCreateDocumentQueryIOrderedQueryableを返すように示されていますが、カプセル化された結果は.AsDocumentQuery()を許可するIDocumentQueryから派生することです。働く。

今では通常、自分が所有していないものをm笑してはいけません。ただし、この場合、ExecuteQueryAsyncを完了まで実行する場合は、テストを完了まで実行できるようにする偽の抽象化を作成できます。

次の例は、その方法を示しています。

[TestClass]
public class DocumentDBRepositoryShould {
    /// <summary>
    /// Fake IOrderedQueryable IDocumentQuery for mocking purposes
    /// </summary>        
    public interface IFakeDocumentQuery<T> : IDocumentQuery<T>, IOrderedQueryable<T> {

    }

    [TestMethod]
    public async Task ExecuteQueryAsync() {
        //Arrange
        var description = "BBB";
        var expected = new List<MyDocumentClass> {
            new MyDocumentClass{ Description = description },
            new MyDocumentClass{ Description = "ZZZ" },
            new MyDocumentClass{ Description = "AAA" },
            new MyDocumentClass{ Description = "CCC" },

        };
        var response = new FeedResponse<MyDocumentClass>(expected);

        var mockDocumentQuery = new Mock<IFakeDocumentQuery<MyDocumentClass>>();
        mockDocumentQuery
            .SetupSequence(_ => _.HasMoreResults)
            .Returns(true)
            .Returns(false);

        mockDocumentQuery
            .Setup(_ => _.ExecuteNextAsync<MyDocumentClass>(It.IsAny<CancellationToken>()))
            .ReturnsAsync(response);

        var client = new Mock<IDocumentClient>();

        client
            .Setup(_ => _.CreateDocumentQuery<MyDocumentClass>(It.IsAny<Uri>(), It.IsAny<FeedOptions>()))
            .Returns(mockDocumentQuery.Object);

        var cosmosDatabase = string.Empty;

        var documentsRepository = new DocumentDBRepository<MyDocumentClass>(cosmosDatabase, client.Object);

        //Act
        var query = documentsRepository.GetQueryable(); //Simple query.

        var actual = await documentsRepository.ExecuteQueryAsync(query);

        //Assert
        actual.Should().BeEquivalentTo(expected);
    }
}
16
Nkosi