[CSHARP-89] Adding virtual to Insert, Update, and Save Created: 29/Oct/10  Updated: 19/Oct/16  Resolved: 19/Jan/11

Status: Closed
Project: C# Driver
Component/s: Feature Request
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Minor - P4
Reporter: Robert Schooley Assignee: Robert Stam
Resolution: Done Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Attachments: Text File WithInterfaces.txt     Text File WithVirtual.txt    

 Description   

Adding virtual to the overloads of Insert, Update, and Save in MongoCollection will make unit testing much easier. I pulled the source and made the change for Insert and it does work as expected.

Example usage in unit test (using RhinoMocks):

public class DatabaseFactory
{
public static MongoDatabase Create()

{ var server = MockRepository.GenerateStub<MongoServer>(new MongoConnectionSettings()); var database = MockRepository.GenerateStub<MongoDatabase>(server, "doesntmatter"); var foos= MockRepository.GenerateStub<MongoCollection<BsonDocument>>(database, "foos"); database.Stub(x => x.GetCollection("foos")).Return(foos); questions.Stub(x => x.Insert<BsonDocument>(new BsonDocument())); // add other mocked document collections here return database; }

}

This allows defensively checking boundary conditions in repositories, and if they all pass the database isn't actually called. The server process can actually be turned off and all unit tests can be run, and then turned on for integration tests.

Maybe a mocking super genius has a better way, but this is the best I could come up with after a while of fiddling around.



 Comments   
Comment by Robert Stam [ 19/Jan/11 ]

Implemented. Made public properties and methods of MongoServer, MongoDatabase, MongoCollection and MongoCursor virtual to enable the use of mock object unit testing frameworks.

Comment by Robert Schooley [ 16/Nov/10 ]

I marked the public methods and properties and everything passed my tests.

Findings:

  • The internal constructor on MongoCursor wouldn't work so I had to mark it public.
    • This isn't the case when using the interface approach as the concrete type is never called.
  • The url passed into MongoUrl has to be valid as there is a concrete dependency on MongoUrlBuilder inside the constructor

Attached is an example of how the classes are mocked/stubbed. I have included the interface based approach as a reference point.

Comment by Robert Stam [ 15/Nov/10 ]

Thanks. Let me know what you find out. I wasn't planning to work on this right away.

Comment by Robert Schooley [ 15/Nov/10 ]

Those three classes sound good to me. Additionally, I did hit down to MongoCursor GetEnumerator when calling a foreach loop in a repository method, so that class probably needs it too:

cursor.Stub(x => x.GetEnumerator())
.Return(null)
.WhenCalled(x => x.ReturnValue = new List<BsonDocument>().GetEnumerator());

The only thing I can think of is possibly MapReduce at some point.

Before you go through this effort I'd like to pull out the interfaces and mark the methods as virtual ion my local copy to make sure it all works correctly. I had some issues around MongoCursor's internal constructor and I don't know if virtual will help there. I have unit tests with the interface approach so I can do a fresh pull of the driver and recompile it with virtual and know pretty quickly if there are any issues.

Comment by Robert Stam [ 15/Nov/10 ]

I don't want to create interfaces for every class (or top level class). But it does seem like a good compromise to make all the public properties and methods of MongoServer, MongoDatabase and MongoCollection virtual. That should be enough to allow mock object frameworks like RhinoMocks to mock these classes. By compromise I mean: making the properties and methods virtual even though they wouldn't otherwise need to be virtual doesn't really change or complicate the API but gives the mock object frameworks what they need.

So where do we stop? Is it enough to put virtual on the three classes just mentioned? How far down do you want to go when creating mock objects for the C# driver classes? One other class that might make sense is MongoCursor, but deeper than that and I think you are creating mock objects at way too low a level.

Comment by Robert Schooley [ 29/Oct/10 ]

I'd also like to add another way to do this may be to extract an interface from MongoDabase. I did that and all was fine and dandy until I had to update dependencies in the driver to use the interface and sparks started to fly. I promptly stopped.

Generated at Wed Feb 07 21:35:46 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.