Sunday, November 27, 2005

Model View Presenter - is testing the presenter enough?

Source code: Download


Lately, I have noticed that the Humble Dialog Box or Model View Presenter are gaining more and more acceptance among software developers, especially in agile communities, because of its benefits regarding the very good separation between the view and the behavior and because it can be very easily unit tested, on a problematic field: user interface.

Let's meet Model View Presenter

In model view presenter just as Martin Fowler or Michael Feathers [2] say, the logic of the UI is separated into a class called presenter, that handles all the input from the user and that tells the "dumb" view what and when to display. The special testability of the pattern comes from the fact that the entire view can be replaced with a mock object and in this way the presenter, which is the most important part, can be easily unit tested in isolation. Let's take a small sample:

A client writes in a user story:

User management

The system will support managing users. Each user can have a username and a password. The system will present a list of user and when one is selected, it can be edited. Also there will be functionality to add new users and to be able to select a user from the list and delete it.


Ok, now let's get to work:

Our view should need the following data, which will be put into an interface:



The UsersList represents the list of users, and the User is the current user selected at a certain time.

When the UI is displayed, we want to be able to see the list of available users, and by default the first in the list to be selected. Let’s write a small test for that. Having no view, we will make a simple implementation of the IUsersView, called UsersMockView. Using it we will be able to see if the presenter when it is initialized sets the needed list and the first is selected in it.



where we have:



And for the data we use a separate model class, like:



If I want to make my test compile, I must write my presenter, see the test failing then keep working on it until the test is working:



and now it is time for a new test, and a small refactoring in the test code:




Now we can go further and add the missing code ... until it works. We then follow the same procedure until we have quite a suite that test selecting a user in the list, updating its details, deleting a user, deleting all the users, and others. Now we know we have a Presenter that works fine. At least where it is tested.



Could we have used a dynamic mock?



I like writing in some cases the mock objects by hand. However there are a few dynamic solutions that can prove quite helpful, like NMock [3] for instance. NMock can create mock objects, using Reflection.Emit and your interfaces “on the fly”. Then it is also very useful when it comes to seeing what methods were invoked in the mock object by your class under test. You can also set predefined responses, when the class under test invokes your mock object, but that is another matter. Let’s make a small sample, just as we did before manually. For the TestInit, using NMock it will be like:






It works. The presenter has invoked the properties of the mock object, once as expected and set the expected values. This way we can throw away our previous tests, and can build a new presenter based on testing with dynamic mock objects.

Implementing the real view

If you read Michael Feather’s “Humble Dialog Box” article, you’ll be able to see very well how the real view should be built. The presenter sets the values into the view which are the propagated to the properties of the controls, much like:



but once .NET was released, it comes with the concept of data binding, and there are some developers that do not want to ignore it and use the “old and safe” ways as above.

How does MVP cope with data-binding?

.NET 2.0 comes with a new and much better concept for data binding then 1.0 or 1.1 did, the BindingSource. This now has the possibility to be connected to different kinds of data sources, including business objects. For our interface we create two: bsUser for User and bsUserList for the list of Users. Then for binding we can use, the designer as:




Now, we go back to our properties that are used by the presenter to “push” data into the view to be displays, and modify the code to use the binding sources as follows:



As you can also see, user actions on the view are delegated to the presenter, which is now fully in control of what behavior of out user management interface.

If we run the tests again, they are all green, all succeed. This is great, but this green is false if you think that it works with data-binding because the tests do not touch in any way the actual view. So what do we do?

A first option would be to leave it like that. But since a view for a desktop application is not exactly as “dumb” as we would like, and can quickly become a source of error, we need to add some tests.

Using a functional testing framework like NUnitForms or SharpRobo could be a solution but this can result in tests that test the same thing, and that is not exactly maintenance friendly. But wait, what if our stub is not exactly a stub but an extension of the view itself, to which we can add testing code. For this we inherit, the real view and create ExtendedUsersView. Now we want to modify the testing code in such a way, that the existing code is still reused. For this we inherit the manual test we have written, and just override the SetUp method:



If we run it, it will run the initial tests but on the actual view. It needs to be shown as data-binding to the controls is done when the controls become visible, but that is not a problem. The problem can be speed, but if you run the code you’ll be able to see that the difference is not that significant. If speed is a problem, then maybe you can run the tests that use the actual view, more rarely, as you can get quite enough feedback from the mock tests.

The process

About the process of building destop application pieces, the way I have constructed the application above, might seem wrong, as using TDD, I built the presenter, but I did not have the view yet, and that was constructed later on the exact same set of tests. In practice I wouldn't recommend this, as the first step, building the presenter without the view, using TDD might lead to a code that will need big refactorings (both presenter and tests) when the view is built. A better approach would be to construct, a two test cases, one inheriting teh other, with two setup methods, one with the mock view, and one with the actual extended view, then build a test inside the first, and build some code on it in both the presenter and the view, then add a new test and so on, until we have tested both the presenter and the view, having both tests that are fast and isolate the presenter's bahavoius and tests that also cover the view.

Conclusion

I have tried to present a solution, that can cover more of the code with unit tests, then only the presenter, as for desktop applications this can be an important issue. I have also tried to find a solution that works with data-binding, and what I have found seems ok so far, but only time and more research and tests can really tell if this was a good choise….

1. Martin Fowler - Model View Presenter
2. Michel Feathers - The humble dialog box
3. Peter Provost - TDD in .NET
4. Jeremy Miller - A simple example of "humble dialog box"
5. Ben Reichelt - Learning teh model view presenter pattern

10 comments:

parag medsinge said...

u have not mentioned or shown in ur code contents of IView, i hope there will be declrations for events, delegates and properties declarations in that

Unknown said...

It's the code available?

Thanks

Ezequiel Jadib
ezequieljadib@hotmail.com
http://ejadib.wordpress.com

Darius Damalakas said...

Em.. Source code is unavailable since geocities is out of bandwidth.

That's atleast what i was told. :)

Dan Bunea said...

Hi,

Damn geocities. I will move the code somewhere else and modify the link.

Sorry for the inconvenience,
Dan

Dan Bunea said...

Solved, the download sources problem.

Eric De C# said...

Download is not available anymore!

Eric De C# said...

The download is not available anymore!

Eric De C# said...

The downloas is not available anymore!

Anonymous said...

Hi,
I want to use Nunit with WCSF [Web Client Software factory].

if you knew it please let me know..

if have any resources/links than also.

waiting for reply..

Kiran

RickardNilsson.Net said...

The Humble dialog v.2