6

I'm just getting started with RavenDB and I like it so far. I am however stuck on how I should unit test controller actions that interact with it.

All the questions/articles I have found like this one: Unit testing RavenDb queries tell me I should use RavenDB in memory rather than mock it away but I cannot find a solid example of how this is done.

For example I have a controller action to add an employee to the database (yes, it's overly simplified but I don't want to complicate the issue)

public class EmployeesController : Controller { IDocumentStore _documentStore; private IDocumentSession _session; public EmployeesController(IDocumentStore documentStore) { this._documentStore = documentStore; } protected override void OnActionExecuting(ActionExecutingContext filterContext) { _session = _documentStore.OpenSession("StaffDirectory"); } protected override void OnActionExecuted(ActionExecutedContext filterContext) { if (_session != null && filterContext.Exception == null) { _session.SaveChanges(); _session.Dispose(); } } [HttpGet] public ViewResult Create() { return View(); } [HttpPost] public RedirectToRouteResult Create(Employee emp) { ValidateModel(emp); _session.Store(emp); return RedirectToAction("Index"); } 

How can I verify what was added to the database in a unit test? Does anyone have any examples of unit tests involving RavenDb in MVC applications?

I'm using MSTest if that matters but I'm happy to try and translate tests from other frameworks.

Thanks.

EDIT

Ok, my test initialise creates the document store that is injected into the controller constructor, but when I run my test the OnActionExecuting event doesn't run so there is no session to use and the test fails with a null reference exception.

[TestClass] public class EmployeesControllerTests { IDocumentStore _store; [TestInitialize] public void InitialiseTest() { _store = new EmbeddableDocumentStore { RunInMemory = true }; _store.Initialize(); } [TestMethod] public void CreateInsertsANewEmployeeIntoTheDocumentStore() { Employee newEmp = new Employee() { FirstName = "Test", Surname = "User" }; var target = new EmployeesController(_store); ControllerUtilities.SetUpControllerContext(target, "testUser", "Test User", null); RedirectToRouteResult actual = target.Create(newEmp); Assert.AreEqual("Index", actual.RouteName); // verify employee was successfully added to the database. } } 

What am I missing? How do I get the session created to use in the test?

1
  • I've updated my question, see below Commented May 9, 2012 at 10:05

3 Answers 3

7

After you've run your unit test, just assert that there is a new doc in the database and that it has the right fields set.

var newDoc = session.Load<T>(docId) 

or

var docs = session.Query<T>.Where(....).ToList(); 

RavenDB in-memory mode is there so that you don't have to mock it out, you just do the following:

  • Open a new in-memory embedded doc store (with no data)
  • If needed insert any data your unit test needs to run
  • RUN the unit test
  • Look at the data in the in-memory store and see if it has been updated correctly

Update If you want a full sample, take a look at how the RacoonBlog code does it, this is the code running Ayende's blog. See these 2 files:

Sign up to request clarification or add additional context in comments.

1 Comment

Yeah, I was looking for a more complete example as I've got that far :) see my question for further information.
3

How can I verify what was added to the database in a unit test?

You don't. We don't test such things in unit tests. This is a responsibility for integration tests, NOT unit testing.

If you want to unit test classes, which depend on some external source (like your db), mock the database access.

EDIT:

To correct some mentioned mistakes, I'll quote definition from MSDN (however all other resources agree with that):

The primary goal of unit testing is to take the smallest piece of testable software in the application, isolate it from the remainder of the code, and determine whether it behaves exactly as you expect.

Without mocking you are ignoring the basic principles of Unit testing - isolation and testing the smallest piece possible. Unit test need to be persistent-ignorant and shouldn't be relying on some external class. What if the db changes over time? Rewrite all tests, even though the functionality stays exactly the same?

COME ON. You can give me -1 how many times you want, but that won't make you right.

6 Comments

That's true, but the in-memory mode of RavenDB makes it so easy to do integration tests, that you often don't need to bother doing unit tests and mocking everything
@MattWarren, hmm, and I thought we were writing tests to be persistent-ignorant...This isn't a question about whether it is easy or not. Unit test should NEVER go outside class boundaries, otherwise it would be an integration test. It doesn't matter whether you have your db in memory, in file, or on a completely different server. The principle stays the same I'm afraid...
Usually with a RDBMS like SQL Server I'd have a repository and mock it for these tests. However, all the articles I've read on RavenDB, including Ayende's blog, say don't do that....so I thought I'd try it.
@Nick, the difference is, that with mocking, it doesn't matter what db you actually use. With using actual storage you're limiting yourself to just one specific db you've set up. If you change your db, you can pretty much rewrite all your tests, because they will 1000% fail. Unit testing shouldn't be dependent on usage of some specific external storage. Doing so violates basic principles. What you actually need are integration tests. Can it work anyway? Sure. Is it a recommended approach? I don't think so. Your call though. Just wanted to point out the main idea of unit testing...
@Walther, I appreciate the principles involved and impress them upon my team as often as I think they'll listen :) but this time I wanted to be a little more pragmatic and not worry about the relationship with RavenDB. To be honest, although I normally stick to the principles as far as is possible, in over 15 years of professional development I don't think I've ever swapped out the datastore on an application once I've started a project. :\ But yes, I understand your concerns.
|
1

As that thread you linked to mentioned use the EmbeddableDocumentStore by embedding RavenDB.

Here's how to set that up: http://msdn.microsoft.com/en-us/magazine/hh547101.aspx

Here's how to use the repository pattern with raven, so you can test easily: http://novuscraft.com/blog/ravendb-and-the-repository-pattern

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.