首页 > 解决方案 > 使用查询表达式模拟 IDocumentQuery

问题描述

我需要能够模拟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

标签: linqazure-cosmosdbnsubstitute

解决方案


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

查看以下内容

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; }
}

推荐阅读