Behavior Driven Development using MVVM pattern and GreenPepper tests

MVVM is a quite mature design pattern one can use with the microsoft WPF framework, or with Silverlight. You’ll find a lot of literature and tools on the web to start a new development with this pattern.

In this article, we show how MVVM can be implemented for a quite large application, and the value it brings in a Behavior Driven Development approach.

Context

The project consists in the development of a point-of-sale application and management solution dedicated to corner shops.

The sale application is a .NET thick-client with a WPF GUI.

95% of the user interactions with this application are variants of a « sale a product » scenario, with various degrees of induced complexity : ranging from a simple change of sold quantity, to a real-time interaction with a third-party system for product booking and e-voucher printing.

As a consequence, most business scenarios are entangled in code, leading to a very high risk of regression during development. The company was already using an automated test tool, simulating clicks on the GUI, but this was a separated development stream, in a dedicated technology, and thus painful to develop and maintain.

To mitigate these risks, we decided to combine the following 2 approaches :

  • From a methodology point of view, we decided to use a Behavior Driven Development approach using the GreenPepper test framework. All the specifications are written in GreenPepper wiki pages.
  • From a software architecture point of view, we decided to implement a MVVM design pattern, so that the automated tests interact with the software at the highest possible level (very close to the view, above the business layer), without manipulating directly the graphical components.

Behavior Driven Development ?

I will not describe here the BDD approach in details. The main idea is to provide to non-technical people a way to write an acceptance test harness by describing the software behavior through scenarios. In BDD, the requirements are usually described using the given/when/then formalism :

  • Given some initial context
  • When some event occurs
  • Then ensure some outcomes

With GreenPepper, we can get very close to this formalism (setup/accept/check) and even more since the version 2.6 with special annotations.

Let’s see how a design pattern can help integrating with GreenPepper.

MVVM

MVVM is an avatar of the traditional MVC pattern, leveraging on the capabilities of WPF to have a more dedicated and efficient pattern for this technology.

M-V-VM stands for Model – View – ViewModel, where ViewModel is a « Model of the View », an abstraction of the GUI, exposing the content to be displayed and implementing all the behavior of the GUI, but deferring all the graphic stuff to the View layer.

In concrete terms, a ViewModel is a simple POCO class exposing Properties coming from the Model, and exposing « Command » methods that will be bound to events occurring on the View. It has (almost) no dependency to graphical libraries, nor to the View, thus achieving a very loose coupling.
The interaction with the View relies on the very powerful (yet complex) mechanism of WPF bindings. Bindings allow to map a Property from a ViewModel in the View in both directions (an update of the property is automatically reflected on the GUI, with no code, and an input from the user is forwarded to the underlying property). They also allow to bind the Command methods to events on the GUI (mouse clicks and so on, are propagated to the ViewModel).

[Note : There are similarities between the usual responsibility of a Controller in MVC and the binding mechanisms of WPF.]

One can also see the VM as a proxy on top of the Model : it manipulates the same data, but with concepts closer to the View.

What remains in the View ? Only pure graphical stuff : windows, controls, styles. Almost no code, except for some very local and very graphical specific logics.

Ok, MVVM looks nice, so what ?

So  … with this pattern, should I decide to remove the View layer, the software is … fully functional. It compiles, and can be run if I provide the glue-code to trigger the ViewModel layer.

The code doing the bridge between a BDD test and the ViewModel is as simple as this :

// Open a form to reference a new product ?
NewProductVM  formVM = new NewProductVM();

// Emulate a user entry in the label and price fields ?
// (params will come from values set in GreenPepper page)
formVM.Label = param1;
formVM.Price = param2;

// Validate the entered value ?
if( formVM.CanSave() ) ...

// Emulate a click on the save button ?
formVM.SaveCommand.Execute()

Usually, such a SaveCommand implementation in the VM calls some service in the business layer, in turn deciding what to do : trigger another service, do some modification in Model objects and save them, or even check some condition and throw a business exception, etc…

The global architecture :

Let’s have a look at the architecture in place to support this :

  • The application is classically divided in layers : Data access, Service, ViewModel, View (not used when running GreenPepper pages).
  • Dependencies are injected at run-time using an IoC container (Spring.NET)
  • Model entities are manipulated by all layers, but not directly by the View. The ViewModel acts as a proxy instead.
  • The GreenPepper block brings the Spring context, some mocks to replace peripherals, third-party components, etc.
  • GreenPepper usually triggers the business scenarios at the ViewModel level, but can also manipulate the underlying layers when necessary, for instance to check that the correct data has been pushed to the DB.

Conclusion :

Today, our BDD test harness represent 170 GreenPepper pages, for a total of around 7’800 assertions.

The software has 20 KLOC (not counting XAML code), the GreenPepper fixtures represent 5 KLOC. This is undeniably an important part of the overall development effort.

BUT … the value is huge !

98% of the total written pages of specification is executable. It is executed every day. Any regression is immediately detected. A deprecated specification or documentation is eliminated quickly. There are no misunderstanding on the specification : a test is either green or red.

This BDD test harness comes in addition to the usual unit tests, and it completes it well. Actually, we notice that writing unit tests on the ViewModel layer is often painful, needs a lot of mocks (doing the same job than underlying services) and boilerplate code. Instead, GreenPepper tests plugged at the ViewModel layer allows a very good test coverage. And it’s very reassuring when time for large refactorings comes.

As said beforehand, the business context also induces a very high risk of regression… and, indeed, we have regressions. Every week. But it’s quickly detected, corrected, and the software quality is definitely kept under control.

These 20 KLOC of software are shipped today to users for a Beta phase. And we are confident.

6 commentaires sur “Behavior Driven Development using MVVM pattern and GreenPepper tests”

  • Thanks for the article. Is that possible to know how long takes the build (including the 170 GreenPepper pages and the unit tests)?
  • The full build with all the greenpepper pages takes ~30 mn. It's executed nightly. The build with only unit-tests, 5 mn, executed at each commit.
  • "Actually, we notice that writing unit tests on the ViewModel layer is often painful, needs a lot of mocks (doing the same job than underlying services) and boilerplate code. " What's painful about writing unit tests for ViewModel? Can you give us some examples of that?
  • @Florian : Yes, of course. Actually, ViewModel objects have a lot of dependencies upon objects from the Models, Services, and sometimes also the Repositories, and other ViewModels. In our project, the ViewModels are definitely the classes having the more dependencies to others. For each of these dependencies, we have to write some mocks in unit tests setups, or to prepare objects the way we want before each test. Example : User-Story = "the user wants to open a new session". The click is handled by a Command in some ViewModel, which calls some Service. The Service creates and opens the session, and returns back the Session object. To unit-test my ViewModel, I have to mock the Service. But the mock will have to prepare and return a Session object the same way the real Service would do it. And the subsequent assertions in the unit-test will just check that the properties from the Session object are well reflected in the ViewModel => lots of code, but not a strong test. And, for the whole project, we notice that it's the unit-tests on ViewModels that require the most boilerplate code, and bring the less added-value. On the opposite, GreenPepper tests are more end-to-end, ViewModel-to-ViewModel : from a Command to some result on some screen. The added value of the test per line of code in fixture is better. So I prefer to be less demanding on UT with the development team for ViewModels : I do not require the same level of coverage of unit-test, if I know that scenarios are well covered by GreenPepper tests.
  • Hi Mathieu, Thanks for the detailed answer, I see what you meant now. Actually, I was asking the question because I'm a Flex developer and we use the Presentation Model pattern that is a bit similar of the ViewModel. However, our Presentation Model implementation doesn't have any dependency. It does only send messages intercepted by controllers. That's why I was wondering why it could be difficult to test such a simple thing. Your feedback about Greenpepper and unit tests is interesting and looks pragmatic to me. I think I'll look closer to this kind of testing with Flex.
  • Thank you for this clear introduction to the MVVM pattern. I'll try soon! Regarding unit test coverage, using mock is not that painfull when using tools like Moq (http://code.google.com/p/moq/). Creating a mock and/or stub takes two lines of code. Generally speaking, it is not easy to understand where stops BDD and when starts TDD but having much more unit tests than functional tests remains very important for test suite duration. Rémy asked for how long it takes to run the tests ... good question : Behavior tests are longer than unit tests so it is important to keep much more unit tests than functional tests. Well, now, I'll try to make this a reality with MVVM ;o)
    1. Laisser un commentaire

      Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *


      Ce formulaire est protégé par Google Recaptcha