新しい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);
_
リポジトリがIQueryable
をIDocumentQuery
オブジェクトに変換しようとするため失敗します。これはDocumentDb APIに非常に固有です(上記のExecuteQueryAsync
メソッドを参照)。後で、HasMoreResults
メソッドを実行します。したがって、問題は、.asDocumentQuery()
をモックしてオブジェクトを返す場合でも、HasMoreResults
およびExecuteNextAsync
の結果を提供する方法がわからないので、理にかなっています私の単体テストのために。
3番目のオプションは、DocumentClientオブジェクトの代わりにリポジトリをまっすぐに模擬することです。私は思うに、もっとシンプルになりますが、DocumentDb APIの多くはテストしません。
これの鍵は、呼び出しているCreateDocumentQuery
がIOrderedQueryable
を返すように示されていますが、カプセル化された結果は.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);
}
}