Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB C# driver Mock method that returns IAsyncCursor

I'm creating some unit tests for my DAL that uses mongoDB c# driver. The thing is that I have this method that I want to test:

    public async virtual Task<IEnumerable<T>> GetAsync(Expression<Func<T, bool>> predicate)
    {
        return (await Collection.FindAsync(predicate)).ToList();
    }

and using Moq I have mocked the collection like this:

var mockMongoCollectionAdapter = new Mock<IMongoCollectionAdapter<Entity>>();

var expectedEntities = new List<Entity>
{
    mockEntity1.Object,
    mockEntity2.Object
};

mockMongoCollectionAdapter.Setup(x => x.FindAsync(It.IsAny<Expression<Func<Entity,bool>>>(), null, default(CancellationToken))).ReturnsAsync(expectedEntities as IAsyncCursor<Entity>);

but as expectedEntities as IAsyncCursor<Entity> is null the test is not working.

What is the best way to mock this method and handle the IAsyncCursor?

like image 945
joacoleza Avatar asked Jan 09 '18 20:01

joacoleza


People also ask

What is a MongoDB driver?

The official MongoDB Node. js driver allows Node. js applications to connect to MongoDB and work with data. The driver features an asynchronous API which allows you to interact with MongoDB using Promises or via traditional callbacks.

Is MongoDB paid?

MongoDB is a NoSQL database that is open source. MongoDB is available in two editions. One is MongoDB Open Source, which is free as part of the Open-Source Community, but for the other editions, you must pay a License fee.


2 Answers

Mock the IAsyncCursor<TDocument> interface so that it can be enumerated. There are not many methods on the interface any way

var mockCursor = new Mock<IAsyncCursor<Entity>>();
mockCursor.Setup(_ => _.Current).Returns(expectedEntities); //<-- Note the entities here
mockCursor
    .SetupSequence(_ => _.MoveNext(It.IsAny<CancellationToken>()))
    .Returns(true)
    .Returns(false);
mockCursor
    .SetupSequence(_ => _.MoveNextAsync(It.IsAny<CancellationToken>()))
    .Returns(Task.FromResult(true))
    .Returns(Task.FromResult(false));

mockMongoCollectionAdapter
    .Setup(x => x.FindAsync(
            It.IsAny<Expression<Func<Entity, bool>>>(),
            null,
            It.IsAny<CancellationToken>()
        ))
    .ReturnsAsync(mockCursor.Object); //<-- return the cursor here.

For reference on how the cursor is enumerated take a look at this answer.

How is an IAsyncCursor used for iteration with the mongodb c# driver?

After this you will be able to understand why the mock was setup with sequences for the move next methods.

like image 51
Nkosi Avatar answered Nov 15 '22 06:11

Nkosi


If it helps anyone else.... off of @Nkosi's mocking answer I implemented the following class

public class MockAsyncCursor<T> : IAsyncCursor<T>
{
    private readonly IEnumerable<T> _items;
    private bool called = false;

    public MockAsyncCursor(IEnumerable<T> items)
    {
        _items = items ?? Enumerable.Empty<T>();
    }

    public IEnumerable<T> Current => _items;

    public bool MoveNext(CancellationToken cancellationToken = new CancellationToken())
    {
        return !called && (called = true);
    }

    public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(MoveNext(cancellationToken));
    }

    public void Dispose()
    {
    }
}


like image 33
Steve Avatar answered Nov 15 '22 05:11

Steve