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.
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.
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.
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.
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.
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.
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.
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.
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.
- Cheshire Half Marathon 2020 September 27, 2020With most of 2020 being a complete right off, it meant I could knuckle down and get plenty of good training in. Races should have been on the cards with Manchester marathon, Holkham triathlon and Valencia marathon being the main goals. However, like most events...
- Helvellyn September 13, 2020Hannah and I were due to get married on the 29th August and were supposed to be spending two weeks hiking around Canada. Then the coronavirus happened and all our plans went out the window. Instead of sitting home doing nothing, we decided to take...
- Uploading content, React, GraphQL, Rails and Active Storage April 26, 2020There are some great blog posts out there on all these topics but I found a lot to be out of date. The versions I’m using are below. Rails 6.0 React 16.3.1 Apollo 3.0.0 Rails To start off with we will set up our backend...
- Full-stack introduction using Rails, React, Docker and Heroku February 9, 2020Coming from mainly a mobile background, I’ve always been interested in learning new technologies that I wouldn’t use in my day to day field. I find it fascinating that especially with today’s technology it is easier for an individual to write an entire end to...
- Malaga Marathon 2019 December 21, 2019All the training these last few months had been leading up to one goal – to run a sub-3-hour marathon in the Malaga marathon. The training had been going great, and with a 1:22 in the Tatton Half, I felt like I had a great...
- Run Tatton Half Marathon 2019 November 9, 2019It’s been a while since my last run event, with the Chester half being back in May. With my marathon training now in full swing for Malaga, I was going to treat this as a fast training run. I had no real goal time in...
- Property Wrappers October 31, 2019First introduced in Swift 5.1, property wrappers allow us devs to add our own implementation details to a property before they are initialized. Currently, by delaying initialization we can declare a property as lazy, and then return our property when it’s first requested. Property wrappers...
- Shropshire Triathlon 2019 September 15, 2019A step up was on the cards for this race, as this would be my first shot at an Olympic distance triathlon after my sprint distance race that I did back in April. I did not have the best prep going into the race, as...
- Running Xcode Tests from CI August 3, 2019The ability to run a suite of tests is a great way to cut down on manual testing time, whilst still having the knowledge that any new changes have not broken the codebase. There are a number of different options when it comes to writing...
- Method Swizzling June 29, 2019When anyone mentions swizzling I automatically get worried. Method swizzling is an Objective C runtime feature which allows for the switching of method implementations. This means any function can theoretically be switch out for another one at run time. The Objective C runtime as a...