For anyone who hasn’t used the Viper Architecture pattern before it’s certainly an interesting experience to say the least. Viper stands for.

V – View – this controls the user inputs by passing them on to the presenter. It also receives information back from the presenter so it can update itself.

I – Interactor – normally contains any business logic. I tend to use interactors to do any data fetching from local cache and/or network.

P – Presenter – this handles the inputs from the view and then delegates them out to either the interactor or router.

E – Entity – is your model objects.

R– Router – handles all navigation between view controllers.

That was a quick overview on how each layer of the Viper stack works, but I would like to show you how I use it in my projects, and how it makes unit testing easier. Below is an example of how I would construct a module using Viper. I’m not going to focus on the Entity layer in this section as we do not need any model objects for this example.

Viper project structure

Contract

Not officially part of the Viper architecture, however I use a contract to group all the interfaces together. It makes it easier to see exactly was is included in any given module.

Swift

Dependancy Injection

To link all the parts of the module together I inject all the dependencies when I first need to use the module.

To do this, I add a class function within the router which allows me to do any setup I need. As my example doesn’t need anything extra, my create function does not have any parameters.

Router

Swift

The router is again pretty straightforward. It has one function to setup the module, and then conforms to our routable delegate which handles showing the alert.

Now you know how I create my modules, and the interfaces which bind them together. I will do a run through of my current example and show you how easy it is is unit test each section.

Interactor

I’m going to start off my showing the tests and then how I wrote the code to make them pass.

The interactor tests consists of four tests. Three in which I expect to catch error based on the local validation which I apply. The final test just makes sure a valid username passes. I’ve also mocked the data fetcher just so we’re not hitting the network. The interactor output is also mocked as we’re not interested in passing the results, on we just want to see what they are.

Swift

Now you know what the tests are, this is how I implemented it.

Again very simple logic to strip out any whitespace using an extension on String, and then just checking the count is less than 20. As all the business logic is inside the interactor, once this gets passed back to the presenter, the presenter just needs to tell the view what to do.

Swift

Presenter

As mentioned before the presenter is like a hub to control all communication between view, router and interactor without doing any real business logic. Due to its dependencies on the three interfaces there require some setup to mock all of theses within the tests. However once mocked we can add tests to make sure the presenter is calling the correct parts of the app.

Swift

All these tests are really valuable as they make sure the key layers of the Viper architecture are communicating correctly. When it comes down to the code, its very minimal as all we doing is delegating the responsibility to other parts of the system.

Swift

The finally testable element would be the router which I already mentioned above. I’m not going to show the test for that but we could easily mock the view controller dependancy on the router and make sure it calls present() when showAlert() is called.

We’ve also not added tests for the View layer. This is because all the view is doing is updating UI elements based on the logic from the presenter, which is already tested.

I hope that gives you a better understanding of how to unit test and what to unit test. When breaking your modules down to this scale it is easy to see exactly what the public interfaces are and what areas are testable.

All the code in this post can be found on my Github account.