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.
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 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.
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.