This post is a discussion of Part 12 of the online NerdDinner ASP.NET MVC tutorial.
Here we get an intro to unit testing and some related concepts like dependency injection. This is another long one–if you’re new to these ideas, expect to spend some time here.
Unit testing seems to be one of those things that everyone knows they should do, they make an occasional effort toward doing, but thorough testing often ends up postponed forever until later when they’re not so busy. That said, it’s a discipline I’d like to use more of, and it’s one you want to at least have passing familiarity with.
So let’s get started. Getting DinnerTest.cs set up should be easy–just be sure to put it under a new \Models folder. Go ahead and run those tests–you should get two green checkmarks. There’s something that appeals to my monkey brain about seeing those green checkmarks, it’s like playing a game.
(By the way, I think the Visual Studio configuration recommendations are good. I applied them myself.)
Now we start creating a new test. This one will be trickier. Be careful when adding the new unit test to \Controllers. You want to click on the Controllers folder and Add -> “New Test”. Don’t do what I did and choose Add -> “Unit Test”, that road will give you more than you want to take on right now.
To get the new unit test to work, you’ll have to add some namespace references. You’re used to this by now. Add using NerdDinner.Controllers; and using System.Web.Mvc; to your list at the top of the file. Run the test and see the red Xs pop up–we’re throwing exceptions. We don’t have a db object–and it would be kind of a mess to make a real one for testing.
Enter dependency injection. Instead of always creating our db inside the class, we’ll alter our constructors to allow us to pass a “db” object in from outside. In our tests here, this will be a fake db object instead of a real one.
There’s another step. We want our controller to work with the fake db object as well as the real one. But the fake db and the real db will have two different object types. So, we’ll add an interface layer that will be compatible with both at once. (You’ve probably worked with interfaces before, but if you’re not used to writing them, you may want to google (er, Bing!) something like “csharp learn interfaces” to get an overview.)
The next step in the tutorial is creating this interface layer. It starts off a little confusing: don’t add the interface manually, do use the refactoring tools. However, there’s one tweak you’ll want to make. The autogenerated code shows:
interface IDinnerRepository
but you’ll want to change it to:
public interface IDinnerRepository
Fix up the constructors for DinnersController.cs as shown. The new parameterized constructor will let us pass in our fake db for testing. (Trouble finding DinnerRepository.cs and DinnersController.cs? The changes we’re making are in the actual web project, not in the testing project. Use the real project from Parts 1 – 11.)
Add the FakeDinnerRepository class (we’re back to the testing project again). Note that you need to add using NerdDinner.Models; at the top of the class, and you have to start the implementation (i.e., type ” : IDinnerRepository ” yourself) before the right click “Implement Interface” becomes available. Generate the stubs, then copy the code from the next part of the tutorial and finish setting up the fake class with real methods. (Sure, you can skip generating the stubs and go straight to the real code if that is clear to you.)
Now we can close the circle and come back to DinnersControllerTest, and put in new tests (with the same names) that use FakeDinnerRepository to interact with the controller in our web project. We get real, passing test results. Hurray! (Note that I had to add using NerdDinner.Models; and using NerdDinner.Tests.Fakes; to the test class to get it to compile.) The new tests themselves are simple–most of the new code is about setting up the new FakeDinnerRepository.
We then set up another testing method (we’re not changing the actual controller, don’t be confused by the controller text) to test Edit-GET. It fails. We don’t have a User object. Our solution here is a little different than before. We won’t try to inject a fake User via the constructor this time. Instead, we’ll use a mocking framework to create a fake User, and after creating the controller in our test environment, we’ll overwrite the default (null) User with this fake User.
The tutorial uses Moq as a mocking framework. The link they give for downloading Moq is dead, but you can still get it here: http://code.google.com/p/moq/. (Moq is highly regarded as a mocking framework, FWIW. Search for discussions of mocking frameworks on, say, StackOverflow, and you’ll see a lot of people choose it as their top pick.) You’ll be downloading a newer version than the one in the tutorial, that’s OK, they’re compatible. Put the binaries somewhere you like and add a reference to Moq.dll to your NerdDinner.Tests project.
Now you can add the new CreateDinnersControllerAs() method. (You’ll need to add using Moq; to your using directives to get it to compile.) Isn’t it handy to have a ControllerContext that you can overwrite so easily? :)
Add the two new Edit testing methods from the tutorial, one expecting a typed view of type DinnerModel, the other expecting the InvalidOwner view. Notice our ValidOwner test uses “SomeUser” as the user name–matching the user name we set up when creating the FakeDinnerRepository. You’ll also want to delete the old EditAction_Should_Return_View_For_ValidDinner() method–don’t try to fix it up, just scrap it. Run the tests and both the new tests should both pass. (The method names are a little different in the tutorial’s screenshot of the test run–disregard that screenshot.)
For the last bit of Part 12 (and the end of the online tutorial), we add two more methods that simulate saving with and without errors. (Again, we aren’t changing the controller, only the testing code.) You’ll notice we don’t have to say anything special in our test to hit Edit-POST, we just pass the correct parameters to hit that overload.
Thinking about what’s happening here might get a little confusing. Remember that our test suite is always creating a controller using CreateDinnerController(). That creates a real controller using a FakeDinnerRepository, which has a fake Save() method. The controller doesn’t “know” it’s using a fake .Save() on a fake repository–but for these tests, it doesn’t need to. (We could set up more tests in the data layer if we wanted to check the internals of .Save() as well.)
. . . and that’s the end. It was fun–I hope you thought so too. Despite the many little glitches, I think NerdDinner is an excellent way to get started learning ASP.NET MVC. It’s much more comprehensive and “serious” than you normally get in an intro-to-X tutorial.
Feel free to drop me a note if you’ve got any questions (or notice any little mistakes!) about this blog.
So long!
You can download my source code for NerdDinner Part 12 here.