【问题标题】:Mock IDocumentQuery with ability to use query expressions使用查询表达式模拟 IDocumentQuery
【发布时间】:2019-02-28 16:02:13
【问题描述】:

我需要能够模拟IDocumentQuery,以便能够测试查询文档集合并可能使用谓词过滤它们的代码:

IQueryable<T> documentQuery = client
                .CreateDocumentQuery<T>(collectionUri, options);

if (predicate != null)
{
   documentQuery = documentQuery.Where(predicate);
}

var list = documentQuery.AsDocumentQuery();
var documents = new List<T>();           

while (list.HasMoreResults)
{
   documents.AddRange(await list.ExecuteNextAsync<T>());
}

我使用https://stackoverflow.com/a/49911733/212121 的答案编写了以下方法:

public static IDocumentClient Create<T>(params T[] collectionDocuments)
{
    var query = Substitute.For<IFakeDocumentQuery<T>>();

    var provider = Substitute.For<IQueryProvider>();

    provider
        .CreateQuery<T>(Arg.Any<Expression>())                                                
        .Returns(x => query);

    query.Provider.Returns(provider);
    query.ElementType.Returns(collectionDocuments.AsQueryable().ElementType);
    query.Expression.Returns(collectionDocuments.AsQueryable().Expression);
    query.GetEnumerator().Returns(collectionDocuments.AsQueryable().GetEnumerator());

    query.ExecuteNextAsync<T>().Returns(x => new FeedResponse<T>(collectionDocuments));
    query.HasMoreResults.Returns(true, false);

    var client = Substitute.For<IDocumentClient>();

    client
        .CreateDocumentQuery<T>(Arg.Any<Uri>(), Arg.Any<FeedOptions>())
        .Returns(query);

    return client;
}

只要不使用IQueryable.Where 进行过滤,就可以正常工作。

我的问题:

有什么方法可以捕获用于创建documentQuery 并将该谓词应用于collectionDocuments 参数的谓词?

【问题讨论】:

    标签: linq azure-cosmosdb nsubstitute


    【解决方案1】:

    从查询提供程序访问表达式,以便将其传递到支持集合以应用所需的过滤器。

    查看以下内容

    public static IDocumentClient Create<T>(params T[] collectionDocuments) {
        var query = Substitute.For<IFakeDocumentQuery<T>>();
    
        var queryable = collectionDocuments.AsQueryable();
    
        var provider = Substitute.For<IQueryProvider>();
        provider.CreateQuery<T>(Arg.Any<Expression>())
            .Returns(x => {
                var expression = x.Arg<Expression>();
                if (expression != null) {
                    queryable = queryable.Provider.CreateQuery<T>(expression);
                }
                return query;
            });
    
        query.Provider.Returns(_ => provider);
        query.ElementType.Returns(_ => queryable.ElementType);
        query.Expression.Returns(_ => queryable.Expression);
        query.GetEnumerator().Returns(_ => queryable.GetEnumerator());
    
        query.ExecuteNextAsync<T>().Returns(x => new FeedResponse<T>(query));
        query.HasMoreResults.Returns(true, true, false);
    
        var client = Substitute.For<IDocumentClient>();
    
        client
            .CreateDocumentQuery<T>(Arg.Any<Uri>(), Arg.Any<FeedOptions>())
            .Returns(query);
    
        return client;
    }
    

    重要的部分是传递给查询的表达式用于在支持数据源(数组)上创建另一个查询。

    使用以下示例主题进行演示。

    public class SubjectUnderTest {
        private readonly IDocumentClient client;
    
        public SubjectUnderTest(IDocumentClient client) {
            this.client = client;
        }
    
        public async Task<List<T>> Query<T>(Expression<Func<T, bool>> predicate = null) {
            FeedOptions options = null; //for dummy purposes only
            Uri collectionUri = null;  //for dummy purposes only
            IQueryable<T> documentQuery = client.CreateDocumentQuery<T>(collectionUri, options);
    
            if (predicate != null) {
                documentQuery = documentQuery.Where(predicate);
            }
    
            var list = documentQuery.AsDocumentQuery();
            var documents = new List<T>();
    
            while (list.HasMoreResults) {
                documents.AddRange(await list.ExecuteNextAsync<T>());
            }
    
            return documents;
        }
    }
    

    以下示例测试将表达式传递给查询时

    [TestMethod]
    public async Task Should_Filter_DocumentQuery() {
        //Arrange
        var dataSource = Enumerable.Range(0, 3)
            .Select(_ => new Document() { Key = _ }).ToArray();
        var client = Create(dataSource);
        var subject = new SubjectUnderTest(client);
    
        Expression<Func<Document, bool>> predicate = _ => _.Key == 1;
        var expected = dataSource.Where(predicate.Compile());
    
        //Act
        var actual = await subject.Query<Document>(predicate);
    
        //Assert
        actual.Should().BeEquivalentTo(expected);
    }
    
    public class Document {
        public int Key { get; set; }
    }
    

    【讨论】:

    • 出色的答案,我认为这是我第一次看到解决方案对我的问题的回答,其中还包括单元测试。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    • 1970-01-01
    相关资源
    最近更新 更多