Asp.Net MVC without 'The framework' Part 2: a good example of Unit Testing
This article is probably a good example of unit testing a web app. Not some example about testing if switches statement, but real testing scenarios that one would come across when testing a web application
Part 1 we ave covered how to implement ASP.Net MVC on the webform. We're making use of the pattern Model View Presenter/Supervising Controller pattern.
In this part 2 we're going to cover how to test a Controller(which from this point on will be referrred to as the Presenter).
To test the Presenter class, we're going to rely heavily on Inversion of Control technique. In general, we want to inject all the dependencies that a Presenter should have. We
replace this dependencies with a bunch of special objects. We then verify whether the Presenter is behaving as expected, by asking it to these special objects.
Let's go into an example to make sense of it.
public class Presenter { private IView view; public Presenter(IView view) {this.view = view} public ListAll() { var repository = new CarRepository(); view.Cars = repository.FindAll(); } }
So above, we have a presenter class with ListAll() method that's responsible of retreiving all the available cars. Afterwich the presenter passes it on to the View.
Let's have alook at the View.
public interface IView { IList Cars { set;} } public class ListingPage : System.Web.UI.Page, IView { private Presenter presenter; public ListingPage() { presenter = new Presenter(this); } public void Page_Load() { presenter.ListAll(); } public IList Cars { set { someRepeater.DataSource = value; someRepeater.DataBind(); } } }
So the view instantiate the Presenter, hook up the page_load event to the presenter.ListAll() call, which in turn going to populate someRepeater with a list of cars. Simple enough right? (if you're not sure why this is, revise Part 1). The problem of testing the Presenter class here is the fact that is so tiedly bound with the Repository class. The repository class probably calls the database and creates a collection of cars. It doesn't seem possibly to test the Presenter without accessing the database. This is where the Inversion of Control technique comes in to save the day.
Let's overload the Presenter constructor so that it's also accepts any types that implements the same FindAll method call.
public class Presenter { private IView view; private ICarRepository repository; public Presenter (IView view) : this(view, new CarRepository) public Presenter (IView view, ICarRepository repository) { this.view = view; this.repository = repository; } }
expose the dependency so that we can inject mocks later on
With the change that we made above, we can start doing this:
[Test] public class PresenterTest { public void Presenter_FetchDataFromRepository_BindItToView { // set up all the dependencies so that we can assert whether // certain method and properties get called properly by // the presenter IView view = generateSpecialView(); ICarRepository carRepository = generateSpecialkCarRepository(); // set up the system under test (SUT) var Presenter = new Presenter(view, carRepository); // run the method presenter.ListAll() // assert that the presenter is doing stuff to these // special objects that we created, as expected // so 2 questions that we want to ask is; // Assert.IsTrue ( is the CarRepository.FindAll method // Called by the presenter ) // Assert.IsTrue ( does the view gets binded by // the return value of the car repository ) } }
The aim here is to test if the Presenter doing the right interactivity with its dependencies. In our case here, the carRepository.FindAll() is called, and the result is being passed to the view. If the 2 asserts on the dependencies passed, then we have successfully test the Presenter.
To put it simply:
It's like bringing in a sparrinng partner to know whether a boxer have trained enough ;)
Easier said than done. How do you do this type of assertion? So here I'm relying on a mocking framework. One of the most popular mocking framework is RhinoMocks written by a guy whose work I admire: Oren Eini (Ayende Rahien). This is the syntax that RhinoMocks uses, which obviously differ depending on the mocking framework you use.
[Test] public class PresenterTest { public void Presenter_FetchDataFromRepository_BindItToView { // this is something that Rhino Mocks use to create mocks. // potentially can be put in the test's contructor if it's // going to be reused // between test methods Mocker mockRepository = new Mocker(); IView mockView = mockRepository.Create(); ICarRepository mockCarRepository = DynamicMock.Create(); using (mockRepository.Record(0)) { // here we setup all the expectations on the dependencies // for e.g we expect the carRepository. // Find all to get called and return something IList something = new List() { new Car(1), new Car(2) }; Expect.Call(() => carRepository.FindAll()) .Return(something) .Message("CarRepository.FindAll() isn't called"); // we also expect the view to be binded with the // same 'something' Expect.Call(() => view.Cars) .SetPropertyWithArguments(something) .Message("View isn't binded with the result set"); } //now set the system under test (SUT) using(mockRepository.Playback()) { // set up the system under test (SUT) var Presenter = new Presenter(view, carRepository); // run the method presenter.ListAll() } } }
So above, I've created a couple of RhinoMock objects and set up the expectation. Afterwhich, I set it to playback mode, where at the end, it'll run through all the expectations and check whether they have been satisfied. Assertion will fail if they're not met. Pretty cool huh? You can also notice that I've used different methods to assert method and property calls.
Obviously I have throwned alot here. Let's recap:
- To make it testable: All Presenter's dependencies needs be injectable throught the constructor.
- To test it: Inject the presenter with Mock dependencies, and ask the expected behavior to these mock objects.
Yet another good example with compact explanation, indeed : )
I'm going to write about mocking also, in my Unit Testing series. Unfortunately, to me... to find a perfect time to start writing is rather harder than to sleep : ( at least, for recently.
There are two articles in CodeProject I like the most, talking about Unit Testing. The first one is Advanced Unit Testing series by Marc Clifton: https://www.codeproject.com/KB/cs/autp1.aspx, and the second one is Test-Driven Development in .NET by Peter Provost: https://www.codeproject.com/KB/dotnet/tdd_in_dotnet.aspx.
We can find Peter shows another example how to test a controller in the second link I just gave.
However, unit testing is intended to be well isolated from any external parts of the target object. That's why mock object come up with such a cool job.
@Fajar Endra Nusa
That's why it's called 'unit' testing right? (-:
Thanks for the links.
I specifically enjoy the 2nd link by Peter Provost. He uses the same technique to implement Model View Presenter / Supervising Controller in the article.
Looking forward to read ur posts on it.
Ronald,
Thansk for comment. Are you from Dubai?. I am also from Dubai, Working in Emirates Airline IT Dept.
Sanjeev
@SanjeevAgarwal hey man. yes I am. My email is ronaldwidha (at) gmail dot com. let's hook up
[...] Notes example to unit test berbagi dengan yang [...]
[...] on opting for MVC pattern on Asp.net Webform (Asp.Net MVC without using ’˜the framework part1 and part 2: a good example of unit testing). A few months ago, I heard about Tatham Oddie's project WebForms MVP, which until yesterday I [...]
I have looked at numerous repository representations but still failed to see how one would mock a repository method that takes a parameter e.g. interface IProduct{ IProduct GetProductId(int id); } Every where people use tests on GetAllProducts(). How can one test using Moq or generate implementations to test a method such as in the interface above. There is something wrong with Moq testing according to my understanding. Why is every body using GetAll() as a representation of the Moq tests in Repository methods, yet methods that take paramters should be tested. Does any one understand MVC out there??? I truly don't see why nobody gives an example of parametized Repository method tests.....
Martin, parameterized repository method should be treated the same way. To mock a parameterized method you can do the following with RhinoMock: Expect.Call(() => carRepository.FindById(expectedId)) .Return(something) .Message("CarRepository.FindById isn't called"); or with the AAA syntax CarRepository.AssertWasCalled( x => x.FindById(expectedId)); or with Moq CarRepositoryMock.Setup(x => x.FindById(expectedId)) .Returns(something); The reason ppl don't give examples on parameterized repository method test is because it is NOT a part of MVC. You should google for "examples unit testing". That is just the basics of unit testing.