1

I have been trying to mock/fake the static method FindAsync() in my Unit test cases using Wrappers, some concepts of Pose. As the static methods cannot be mocked or faked normally. It is not successful.

The code in the repository layer which I want to unit test points to the IMongoCollectionExtension.FindAsync() method.

This is the method I am trying to mock

 public async Task<MyClass> GetItem(Guid id)
    {
        var filter = Builders<MyClass>.Filter.Eq(m => m.Id, id);
        var result = await _context.MyCollection.FindAsync(filter);
        return result.FirstOrDefault();
    }

This FindAsync() is pointing to IMongoCollectionExtensions STATIC class

   public static Task<IAsyncCursor<TDocument>> FindAsync<TDocument>(this IMongoCollection<TDocument> collection, FilterDefinition<TDocument> filter, FindOptions<TDocument, TDocument> options = null, CancellationToken cancellationToken = default(CancellationToken));

So as it is pointing to Static class and a static method I started writing wrapper to mock,

First Method tried using Wrapper:

This is wrapper I have created.

  public interface IMongoCollectionExtensionsWrapper
{
    Task<IAsyncCursor<MyClass>> FindAsync<MyClass>(IMongoCollection<MyClass> collection, FilterDefinition<MyClass> filter, FindOptions<MyClass, MyClass> options = null, CancellationToken cancellationToken = default(CancellationToken));
}

public class MongoCollectionExtensionsWrapper : IMongoCollectionExtensionsWrapper
{
    public Task<IAsyncCursor<MyClass>> FindAsync<MyClass>(IMongoCollection<MyClass> collection, FilterDefinition<MyClass> filter, FindOptions<MyClass, MyClass> options = null, CancellationToken cancellationToken = default(CancellationToken))
    {
        return collection.FindAsync(filter, options, cancellationToken);
    }
}

public static class FakeExtensions
{
    public static IMongoCollectionExtensionsWrapper defaultmcExtWrapper = new MongoCollectionExtensionsWrapper();

    public static Task<IAsyncCursor<MyClass>> FindAsync(this IMongoCollection<MyClass> collection, FilterDefinition<MyClass> filter, FindOptions<MyClass, MyClass> options = null, CancellationToken cancellationToken = default(CancellationToken))
    {
        return defaultmcExtWrapper.FindAsync(collection, filter, options, cancellationToken);
    }


}

As the wrapper was not working properly i checked out free framework Pose to mock static methods. That was not successful too.

Second trial using Pose

 Shim findshim = Shim.Replace(() => IMongoCollectionExtensions.FindAsync(Is.A<IMongoCollection<MyClass>>(), filter, null, CancellationToken.None)).With(delegate (IMongoCollection<MyClass> mc, FilterDefinition<MyClass> f, FindOptions<MyClass, MyClass> o, CancellationToken ca) { return Task.FromResult(_fakeOutput.FakedObject); });

NOTE: _fakeOutput is a faked Cursor holding an IEnumerable. It works fine.

PoseContext.Isolate(() =>
        {
            Task.Run(() =>
            {
                var exp = Task.FromResult(item1);

                var myres = _Repo.GetItem(Id);

                Assert.Equal(exp, myres);
            });

        }, findshim);
        var myres = _Repo.GetItem(Id);

In both the trials, I have tried mocking IMongoCollectionExtensions.FindAsync() but result (output of the method i want to unit test after setting up mock/fake) in both cases are null and when I tried below Assertion if the FindAsync() method of IMongoCollectionExtension has Happened or not, but it didn't hit. I dont understand when the method i want to unit test is pointing to IMongoCollectionExtension.FindAsync() only but it is not hitting.

fakeIMongoCollExt.CallsTo(x => x.FindAsync(A<IMongoCollection<MyClass>>.Ignored, A<FilterDefinition<MyClass>>.Ignored, null, CancellationToken.None)).MustHaveHappened();

(Method signature has MongoCollections as first parameter - Extension Method) is showing that it didn't hit that method.

So I tried checking MustHaveHappened() for IMongoCollection.FindAsync() (It is interface method not the static class method which we are discussing above) which also tells that "The target of this call is not the fake object being configured."

I am not sure how FindAsync() is pointing. How to proceed with unit test cases. Please let me know if you have any idea.. Thanks in Advance..

IMongoCollections.FindAsync() Mocking

Te Jas
  • 45
  • 9
  • It's a bit unclear from your question what you're doing exactly. What happens without the wrapper or the shim? In any case, it looks like the error message you get is incorrect. It should say that the method is static and cannot be intercepted, not that the target isn't the fake object. – Thomas Levesque May 15 '19 at 14:45
  • when i try to mock IMongoCollections.FindAsync() it says - "...target isn't the fake object", when I try to mock IMongoCollectionExtensions.FindAsync() it says - "..method cannot be intercepted as it is static.." I have to apply wrappers or Pose as the method in Repo layer is pointing to Static method in Static class. IMongoCollectionExtension is a static class with the below method.... "public static Task> FindAsync... " – Te Jas May 15 '19 at 16:56
  • I have edited the query to explain it bit clear. Please check @ThomasLevesque – Te Jas May 15 '19 at 17:11
  • 2
    FakeItEasy can only fake methods that you could have overriden yourself; static methods can't be overriden, so FakeItEasy can't fake them. However, the `FindAsync` extension method actually just calls `IMongoCollection.FindAsync`, so you could fake that instead. – Thomas Levesque May 16 '19 at 07:42
  • 1
    @ThomasLevesque I agree but I tried mocking IMongoCollection.FindAsync() as well, It throws the exception - "The target of this call is not the fake object being configured. " – Te Jas May 16 '19 at 09:47
  • @ThomasLevesque I have added a link in the ending of query which redirects to the code i used to mock IMongoCollection. Please check – Te Jas May 16 '19 at 10:02
  • I think it might be because overload resolutions resolves to the[extension method](https://github.com/mongodb/mongo-csharp-driver/blob/23cc4ae7a04278e6bdbe0ca2c8237299ad48810e/src/MongoDB.Driver/IMongoCollectionExtensions.cs#L976) instead of the [interface method](https://github.com/mongodb/mongo-csharp-driver/blob/23cc4ae7a04278e6bdbe0ca2c8237299ad48810e/src/MongoDB.Driver/IMongoCollection.cs#L563). They're very similar (in fact, so similar that it's confusing), so it's easy to call one instead of the other. Try specifying all generic types explicitly. Also, how are you creating the fake? – Thomas Levesque May 16 '19 at 10:53
  • @ThomasLevesque Can you be please more clear regarding "Try specifying all generic types explicitly." I am Faking using FakeItEasy – Te Jas May 28 '19 at 07:07

0 Answers0