如何(应该)模拟DocumentClient进行DocumentDb单元测试? [英] How to (should I) mock DocumentClient for DocumentDb unit testing?
问题描述
从新的CosmosDb模拟器中,我得到了一个执行基本documentdb操作的存储库,该存储库被注入到其他类中.我想对基本查询进行单元测试.
From the new CosmosDb emulator I got sort of a repository to perform basic documentdb operations, this repository gets injected to other classes. I wanted to unit test a basic query.
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;
}
}
此存储库需要一个文档客户端才能工作,该文档客户端也会注入到构造函数中.
This repository needs a document client to work, which also gets injected on the constructor.
public DocumentDBRepository(string databaseId, IDocumentClient client)
{
_client = client;
_databaseId = databaseId;
_collectionId = GetCollectionName();
}
我最初的方法是使用CosmosDb仿真器,但这要求仿真器运行我不喜欢的仿真器,并且会使测试变慢.
My initial approach was to use the CosmosDb emulator, but that required the emulator to run which I don't like and makes the tests slower.
我的第二种方法是尝试使用文档客户端的模拟.
My second approach was to try and use a mock of the document client.
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);
使用该存储库的代码如下:
The code that uses this repository works like this:
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
提供结果,以使其对我的单元测试有意义.
It fails because the repository tries to convert the IQueryable
to a IDocumentQuery
object, which is very specific to DocumentDb api (See method ExecuteQueryAsync
above). Later on, it executes HasMoreResults
method on it. So the problem is, even if I mock .asDocumentQuery()
to return my object, I don't know how to provide a result for HasMoreResults
and ExecuteNextAsync
so that it makes sense for my unit tests.
我的第三个选择是直接模拟我的存储库,而不是DocumentClient对象.我认为这会更简单,但是我不会测试很多DocumentDb api.
My third option would be to straight mock my repository instead of the DocumentClient object. Would be, I think, simpler, but I wouldn't be testing much of the DocumentDb api.
推荐答案
此操作的关键是您正在调用的CreateDocumentQuery
,尽管显示为返回IOrderedQueryable
,但封装的结果也将从IDocumentQuery
派生.这将使.AsDocumentQuery()
正常工作.
The key to this is that the CreateDocumentQuery
you are calling, though shown as returning IOrderedQueryable
, the encapsulated result will also be derived from IDocumentQuery
which is what would allow .AsDocumentQuery()
to work.
现在,通常您不应该嘲笑自己不拥有的东西.但是,在这种情况下,如果您想行使ExecuteQueryAsync
来完成测试,则可以创建一个伪造的抽象,以使测试可以完成.
Now normally you should not be mocking what you do not own. However in this case if you want to exercise ExecuteQueryAsync
to completion you can create a fake abstraction that will allow the test to be exercised to completion.
以下示例显示了如何完成此操作.
The following Example shows how it can be done.
[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);
}
}
这篇关于如何(应该)模拟DocumentClient进行DocumentDb单元测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!