|With the imminent release of our latest mobile app, I thought I'd summarise how we ensured high levels of quality, and proved that the software was correct. I'm not going to write an article justifying the case for unit testing (it should go without saying that unit testing is a fundamental part of the development process - if not you're doing it wrong), but rather to explain how we implemented unit testng within the software for the app.
The architecture I favour when designing an application, is to firstly reduce the surface area of the client[^]. Simply put, this entails keeping the UI code as sparse as possible, and removing any / all code that is involved with the domain. The UI should ONLY contain code that relates to the UI. While this sounds straight forward, I have lost count of the number of times I've come across code bases where the UI contains code from the domain and / or the data layer.
In relation to a Xamarin Forms mobile app, you should keep the code in the Views as sparse as possible. The UI code should only invoke your domain code, it should NEVER implement it. Your Xamarin Views should contain code for manipulating the various UI controls, populating them with data etc. As soon as there is a need for anything beyond this, then refactor the code and place this code in a completely separate layer of the app. Within the context of a Xamarin Forms app, I created separate folders for such things as the models, services, entities etc. These were completely separate to the Views.
To enforce this separation of concerns, we adopted the MVVM design pattern. I won't go into great detail here about this pattern (as there are many articles out there already). The MVVM pattern stands for
Model -> View -> View-Model
More correctly it could be named VVMM (View -> View-Model -> Model) as this is the order in which they relate to each other (in terms of dependency). The Model should have no knowledge of the View-Model. The View-Model should have no knowledge of the View. This is important when implementing an MVVM application, as it reduces the dependencies between the various parts of the application.
The View in a MVVM designed app is the UI element, or in the case of a Xamarin Forms app, they are the Views. Only UI code should be placed in the Views.
The View-Model is the place where domain logic will reside. All UI controls should be bound to properties in the View-Model. The code that provides your UI controls with data, hides/shows the UI element etc should all be implemented here. This way, you can unit test those rules and ensure that they are correct. And this is done without the need for the UI to be present. This means you don't have to keep using the simulator or physical device to test the domain rules of your app. You should be able to unit test these rules in the absence of the UI, and in complete isolation from other parts of the application. The unit tests should require minimal setup, and any dependencies should be injected into the methods to remove hard-wired dependencies. This is good old fashioned Dependency-Injection, and it is a vital design pattern when implementing unit tests. This ensures the correctness of your domain.
The Model is concerned with the data, and therefore maps your data entities into classes. The Model will contain such things as definitions for customer, order, supplier etc. The Model should not be concerned with how it is used by the View-Model or View. For example, you may have an Order class which contains an Order-date. This is stored within the Model as a Date type. The fact that this date is displayed as a string in the UI is of no concen to the Model. Any conversions needed to map Model properties into UI elements should be implemented by the View-Model (you may have a conversion needed by several elements or Views, so it makes sense to place this conversion code within a View-Model where it can be invoked from multiple places). Again, these conversions can be unit tested with complete independence from the UI by placing them in the View-Model. You can write unit tests against the Model to ensure that the values you set against it match those that are returned. So if you set the Order-date of your Order to a specific date, you can assert that this date is returned by the unit test. This ensures the correctness of your underlying data.
Unit testing a mobile app need not be difficult as long as you have carefully designed and architected the various moving parts and separated the key concerns. Implementing an architecture that supports separating out the various concerns is vital (layering). It's also useful to implement a design pattern that enforces such layering (such as MVC, MVVM). You should aim to keep your UI as sparse as possible, and place all code that is not involved in the UI elsewhere within the application.
"There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult." - C.A.R. Hoare