Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refactoring WPF MVVM for increased testability

I've inherited a functional but messy WPF MVVM application. To my annoyance, there were virtually no unit tests which makes the adoption MVVM somewhat pointless. So I decided to add some.

After grabbing the low-hanging fruit, I'm starting to run into trouble. There's a lot of highly interdependent code, especially inside the properties and methods that are used in the Views. It's common, for instance, for one call to set off a chain of "property change" events which in turn set off other calls and so forth.

This makes it very hard to test, because you have to write enormous mocks and set up a large number of properties to test single functions. Of course you only have to do it once per class and then re-use your mock and ViewModel on each test. But it's still a pain, and it strikes me this is the wrong way to go about things. It would be better, surely, to try and break the code down and make it more modular.

I'm not sure how realistic that is in MVVM. And I'm in a vicious circle because without good tests, I'm worried about breaking the build by refactoring to write good tests. The fact it's WPF MVVM is a further concern because nothing tracks the interdependence between View and ViewModel - a careless name change could completely break something.

I'm working in C# VS2013 and grabbed the trial version of ReSharper to see if it would help. It's fun to use, but it hasn't so far. My experience of unit testing is not vast.

So - how can I approach this sensibly, methodically and safely? And how can I use my existing tools (and look at any other tools) to help?

like image 379
Bob Tway Avatar asked Oct 31 '14 10:10

Bob Tway


1 Answers

  1. Start at the heart -- business logic

Your application solves some real world problems and this is what business logic represents. Your view models wrap around those business logic components even if they (components) don't exist just yet (as separate classes).

As a result, you should assume that view model is lightweight, data preparation/rearrangement object. All the heavy lifting should be done within business logic objects. View model should be served with raw data, display ready.

  1. Modularize

Having this important assumption in mind (VM = no BL) move business logic components to separate, possibly modular projects. Organizing BL in modular way will often result in projects structure similar to:

  • Company.Project.Feature.Contract - interfaces, entities, DTOs relating to feature
  • Company.Project.Feature - implementation of contract
  • Company.Project.Feature.Tests - tests for feature implementation

Your goal with #1 and #2 is to reach a state where abandoning WPF and replacing it with console-interface should only require writing console interface and wiring it with BL layer. Everything WPF related (Views, ViewModels) could be shift-deleted and such action should not break a thing.

  1. IoC for testability

Get familiar with dependency injection and abstract where needed. Introduce IoC container. Don't make your code rely on implementation, make it rely on contract. Whenever view model takes dependency to BL-implementation, swap it with BL-abstraction. This will be one of the necessary steps to make view models testable.

This is how I would start. By no means this is exhausive list and I'm convinced you'll run into situations where sticking to it won't be enough. But that's what it is -- working with legacy code is not an easy task.

like image 197
k.m Avatar answered Oct 11 '22 09:10

k.m