Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
674 views
in Technique[技术] by (71.8m points)

linq - Mock IDocumentQuery with ability to use query expressions

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...