Mock IDocumentQuery with ability to use query expressions

。_饼干妹妹 提交于 2019-11-28 05:25:40

问题


I need to be able to mock IDocumentQuery, to be able to test piece of code, that queries document collection and might use predicate to filter them:

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

I've used answer from https://stackoverflow.com/a/49911733/212121 to write following method:

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

Which works fine as long as there's no filtering using IQueryable.Where.

My question:

Is there any way to capture predicate, that was used to create documentQuery and apply that predicate on collectionDocuments parameter?


回答1:


Access the expression from the query provider so that it will be passed on to the backing collection to apply the desired filter.

Review the following

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

The important part is where the expression passed to the query is used to create another query on the backing data source (the array).

Using the following example subject under test for demonstration purposes.

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

The following sample tests when an expression is passed to the query

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


来源:https://stackoverflow.com/questions/54929728/mock-idocumentquery-with-ability-to-use-query-expressions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!