如何(应该)模拟DocumentClient进行DocumentDb单元测试? [英] How to (should I) mock DocumentClient for DocumentDb unit testing?

查看:67
本文介绍了如何(应该)模拟DocumentClient进行DocumentDb单元测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

从新的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()以返回我的对象​​,我也不知道如何为HasMoreResultsExecuteNextAsync提供结果,以使其对我的单元测试有意义.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆