20

I am trying to do tdd and use mongodb as database. But i cant resolve problem of mocking mongodb. Is there any ability to mock mongodb for unit testing in .NET?


Update

I found very good soltion reading blog. You can find it here:

F0rc0sigan
  • 676
  • 2
  • 11
  • 22

4 Answers4

17

Instead of mocking MongoDB, you should be mocking a layer on top of MongoDB.

You might want to consider an interface that exposes the operations on your repository which are agnostic to the underlying data store. For example, you might want an interface that abstracts out operations on Student types, like so:

public interface IStudentOperations
{
    void Add(Student student);
}

When you create your other dependencies, you inject instances of the above interface, or whichever higher-level abstractions you choose.

The point is, don't expose MongoDB directly.

Once you do that, you can mock the interfaces you create all you want, having one implementation for testing against the mock implementation, and then an actual implementation with it's own tests to validate that operations on the implementation are correct when the underlying implementation is with MongoDB.

While it's definitely possible to mock most of MongoDB's classes (as the methods are virtual), you gain the benefit of being persistence agnostic; if you want to switch to say, CouchDB or elasticsearch, you don't have to change the calls to these interfaces, you simply create a new implementation.


Because you are trying to test the implementation of the repository, then you are generally fine, as has been stated before, most of MongoDB's functions are virtual, which is friendly to most mocking libraries.

That said, you'll have to make sure that you pass the MongoDatabase into your repository (not create it in the repository) so that in your unit tests, you can create the appropriate mock and then pass it into your repository implementation for testing.

Community
  • 1
  • 1
casperOne
  • 73,706
  • 19
  • 184
  • 253
  • While I agree with casperOne, it would be possible to mock the MongoDB driver's classes with most mocking frameworks since the public methods are virtual. – kfuglsang Oct 09 '12 at 16:50
  • I will use repository pattern. SO i need to mock All, Save, and others. That's why i need mock mongo – F0rc0sigan Oct 09 '12 at 16:53
  • @kfuglsang Updated answer with benefits of not mocking MongoDB directly. – casperOne Oct 09 '12 at 16:54
  • 1
    @F0rc0sigan Are you testing the implementation of the repository, or calls *to the repository*? – casperOne Oct 09 '12 at 16:55
  • @F0rc0sigan Updated, note it's just about making sure you can pass the `MongoDatabase` instance into the repository not creating it in your repository. – casperOne Oct 09 '12 at 17:02
  • Can you please make a little example of mocking with mongodb. Very Thanks, – F0rc0sigan Oct 09 '12 at 17:03
  • 4
    @F0rc0sigan Sorry, Stack Overflow is not a code-writing service. You've also not indicated what methods you'd like to mock, what you've tried, or what mocking library you're using. There's not enough information to even begin to try and give you a sample. – casperOne Oct 09 '12 at 17:07
  • What if you want to unit test the DAL itself? Not sure if it's right to say that it's the wrong approach to mock Mongo. It's more like, it's too hard to accomplish so just use integration tests... – kovac May 16 '20 at 04:37
4

See this similar question: Mocking database in node.js?

In short, mocking MongoDB isn't the right approach. Mocking your repository is adequate for testing your own units, but you'll still need to test against MongoDB if you want to make sure that you're using it correctly, or if you are relying on uniqueness constraints etc.

Community
  • 1
  • 1
cirrus
  • 5,624
  • 8
  • 44
  • 62
3

You need a repository/DAL layer to hide implementation details. Something like this:

public interface IDataContext<T>
{
   // Query
   IList<T> GetAll<T>();
   IList<T> GetByCriteria<T>(Query query);
   T GetByID<T>(string id);

   // CUD
   void Add(T item);
   void Delete(T item);
   void Save(T item);
}
  • For production code, implement the interface with mongo db operations with the assistance of C# driver
  • For unit test codes, mock the interface with an in-memory collection

It looks straightforward to mock Add/Delete/Save, and the intersting part is query function. Fortunately, since mongo C# driver supports LINQ expressions, we can use LINQ as the query language rather than reinventing the wheels.

For example, the production code may look like:

// collection is a MongoCollection<T> object
var items = collection.AsQueryable().Where(...linq expression...);

And unit test code (if you go with Shim, MS test):

using (ShimsContext.Create())
{                  
    ShimLinqExtensionMethods.AsQueryableOf1MongoCollectionOfM0(
        (MongoCollection<T> x) => Fake_DB_Collection.AsQueryable());
    // After this, the above "collection.AsQueryable()" will be mocked as 
    // an in-memory collection, which can be any subclass of IEnumerable<T>             
} 
David Hu
  • 49
  • 1
1

If you're focused on Unit Tests then follow the @casperOne's advice to mock a layer on top of DB (could be easy with Repository Pattern). However, you might be in need of Integration Tests to assert on a certain behaviour, then you need a real database and you've got 2 options:

Option 1: Run MongoDB in Docker

The official packages like the C# Driver run tests against a life instance (e.g. running in Docker) and passing the connection string via environment variables.

You would need:

  1. Launch MongoDB engine in Docker
  2. Create & populate a test database
  3. Connect xUnit/NUnit tests to the database and for each test (rinse and repeat):
    1. Seed test data
    2. Perform the testable activity and checks
    3. Revert the database to the pristine state
  4. Tear down the Docker container.

For writing actual tests the official examples is a good starting point. Just don't forget to turn off parallel test execution (you've got a single DB instance).

Option 2: Mongo2Go NuGet – launch Mongo on tests kick-off

The community tried to ease the suffering of managing Docker instances and came up with Mongo2Go NuGet package that instantiates Mongo Community Server from included binaries (only v4.4.4 for Windows, Linux and MacOs) that blows out the package size and can download the latest version from the Internet when testing get kicked off.

Alex Klaus
  • 8,168
  • 8
  • 71
  • 87