Testing part I: Testing View Controllers

How many times in your career have you heard the phrase: “If it works, don’t change it”. That’s like saying I’m afraid of my code and fear is not a good way to drive your development. You are likely to change your code in the future while searching for a bug or adding a new feature, and you will probably be in a rush when doing it. If you want your code to allow fast changes, you will need to do refactors. The answer to how to eliminate the fear you feel when refactoring is testing.

That comes into play even harder with View Controllers, they are the gateway of the events that come from the user interface, the framework or other boundaries of our systems. We want to know that they are responding correctly to all these events. The VCs are also responsible for some logic like creating the instance of a view model according to the business logic, or validating the data introduced by the user using the interface. In a lot of systems the VCs are even responsible for implementing the flows of the interfaces by pushing other VCs to the UINavigationController associated with them.

This article is oriented to any developer that wants to find an easy, fast and reliable way to test the VCs of his system.

The system under test

The system under test (SUT) is a subpart of your system that you want to test. In this case the central piece will be a View Controller. Defining the SUT is one of the key parts of testing. We don’t want certain parts of the system to interfere with our test because they are asynchronous, non deterministic or simply because they are covered in another suite of tests. I call the parts of the system that I don’t want to test but the SUT communicates with the SUT Boundaries.

Of course we can’t eliminate the fact that our VC is communicating with these SUT Boundaries. We have to find ways to determine if the SUT is communicating correctly with them, and if it responds correctly to the events that come from them. Our tests will consist of generating events to the SUT and waiting for it to generate the correct response.

We can communicate events to a VC through a boundary or its public interface. The  VC can respond to these events by returning a result or communicating with one of its boundaries. In the last post I wrote we were studying how to separate our view logic from the VC implementing a Login VC. I created a more realistic but very simple implementation we will be using to exemplify the concepts I’m explaining. Take a look at this UML that explains both the design and the boundaries that the SUT works with:

You should isolate your VC from the View Boundary. The view is the specific implementation of how to represent the user interface on the screen, we have to focus on how the VC interacts with the view not how the view is shown.

We will use this boundary to simulate the user events and we will verify that the VC interacts with it in the correct way. At the login boundary we have the implementation of how to log in our system. It should never be the responsibility of the VC to know how to login. That would increase the VC size, make this part of the code not reusable and break the Single Responsibility Principle. Remember the VC’s responsibility is to manage the view, not login a user. For the same reason it isn’t supposed to know where we store the last username that was logged in, that is implemented by the JPLastLoginStorage.

So the SUT consists in our LoginViewController and the LoginViewModel. Could the SUT be bigger than that? Yes, for example, if in the future I refactor the VC to have an object that does the data verification I wouldn’t change my tests. That’s because verify the data is part of the responsibility of the VC, your tests have to give you freedom to organize your SUT.

Dependency injection and testing tools

To create events from the boundaries and verify that the VC communicates with them as expected we need to use test doubles. Test doubles are used to substitute the real objects implementing the boundaries with objects accomplishing what we need during the test. If you are not familiar with these concepts I recommend you these articles:

We will inject these objects to the VC. We want our VC to use the test doubles when we are testing and the real objects while it is on production. As the SUT shouldn’t be aware if it is being tested or not, we will inject the boundaries. We will be using two techniques to do that: injection through initializer for the login boundaries and injection through property for the view.

By making the injection through property you give the option to the user to change the boundary when he wants. To avoid that, I always use injection through initializer for all the boundaries except the View. This way I will be sure that the boundaries are not changed throughout the object’s lifetime when it’s in production (if that’s the intended behavior). Here’s the initializer of the VC:

Now we can inject test doubles during the tests or the real boundaries during production. I will write a post in the future in how to manage the Dependency Injection in big architectures, stay tuned. We inherit from UIViewController a read write property for the view, so we don’t need to inject anything for this boundary at the initializer. Inside a test we will be doing the injection of the view like this:

Observe that I’m calling viewDidLoad after setting the view. That is automatically done by UIKit after loadView but not when injecting the view using the property.

And what kind of test doubles should we use for the boundaries? That depends on the boundaries you are replacing, I prefer to explain that using examples. The most used technique nowadays is mocking, I use it for the most simple cases but I prefer do my own test doubles.

For mocking I use OCMockito but OCMock is also a good alternative. OCMock implements more features like partial mocking but I prefer the readability of OCMockito. For write verifications I use OCHamcrest also for the understanding of my tests, reading this kind of assertions is very close to the natural language. Oks, now I think we are ready to see examples!

The first test

The first test verifies that the VC creates the correct instance of the view. For this test we don’t need any test double we simply get the view from the VC and we verify that the created view is an instance of the LoginView.

Create the View is one of the responsibilities of the VC, by testing that I create the correct view I assert that the correct interface has been build. Testing the view is not impossible but I don’t recommend it most of the times. Test the frames and structure of the view makes our tests very fragile. Sometimes I test parts of them that are more abstract or that don’t change, for example part of the datasources of the table views: how many rows it returns or that the returned cell is an instance of a concrete class.

Create the correct View Model

We use a View Model to provide to the view the data it needs to be shown to the user. The VC creates this view model and set it to the view so we need to be sure that it contains the expected data. To fake the view we use a type of test double called Spy.

Never use a mock to replace your views in a test. The  mocks that are created on the fly like the ones we create with libraries like OCMockito are proxies, so they don’t have all the infrastructure of the view, they only apparent to have the implementation of all its methods. The problem comes when some code try to access to iVar inside the instance of the view.

I learn that the hard way by having unexpected crashes in my tests when a method from UIViewController was accessing to an iVar that was a random memory space. Thanks to @cestebanez and @rafalopezdiez, they actually found the problem and send me this wonderful article about NSProxies. I recommend you to read it all but the part I’m speaking about is the “ARE PROXIES SAFE?” point. So my view spy is simply a subclass of my view with methods to spy what events it’s receiving, here is its interface:

Using this Spy I can test that the view model the view receives is the correct one depending on the situation. This test case verify that when the last login boundary returns a user name the view receive it in the View Model after view did load:

Observe that I’m using a mock as a test double for the last login boundary. It’s a good example where I use mocks, the interface is very simple, without blocks for asynchronous events and makes my test very readable.

Simulate events from the interface

The view communicates with the VC using his delegate. If you are not familiar on how to separate your view logic from the VC you can take a look to this article explaining the problem and a possible solution. Once your view is separated you can simulate the events that comes from the interface using the delegate of the view. Here you can see a case where we are testing that when a user set’s an empty password in the view and tap on login the SUT will ask the view to show the correct error.

As a detail, I never test that the delegate of the view is the VC. That’s because my goal is to test the behavior. Testing this fact would be look inside the black box. If you want to be able to refactor your code without touching your tests you have to be very careful to don’t test the implementation. The fact that the VC is the delegate of the view is only a detail. What you want to test is that if a user puts a password that is too short the view will receive the order of showing an error. you don’t care about how that has been done, you only simulate a situation and wait for the consequences.

 Multi step use cases

When doing a login the user taps on login with a correct user and password in the view and the SUT asks the Login Boundary to login. When you call the login method you send two blocks to the boundary, one for failing that returns the kind of error and one for succeeding that returns a User. Take a look to the interface of the login handler:

This kind of use case implies multiple tests, one to test that the SUT calls the Login boundary with the correct data(Username and password), and one for every possible return of the login.

To test the interaction with the Login Boundary (JPLoginHandler) we have created an other type of test double, the fake objects. The JPLoginHandlerFake has some logic inside, it succeeds if you use a specific user and password defined in its interface. It also defines an user that returns an unreachable service error.

Its interface also offers a method called simulateEndLogin. This way we can simulate when the boundary makes his response. Take a look to its interface and you can find its implementation at this link.

Now let’s see some examples of the tests that test the multiple step login:

You should keep your test small and you should test every step that conforms the responsibility of the VC, this way it will be more easy to read and discover what is happening when they fail.

Testing VC’s interface flows like UINavigationController

It’s a reality that in a lot of view controllers we implement parts of the interface flows. For me that’s not the correct way to go and I will write an article about how to implement the flows separately from the VCs that implements part of it.

If you have this kind of situation you can be interested in how to test it. The more frequent case is use the self.navigationController instance to push other VC’s to the flow.

The best way to test that is simulate the situation by using a fake UINavigationController that registers which VC are pushed to it. To exemplify this test I created a VC that pushes itself by calling an interface method, this will be our SUT:

So we want to test that when we call simulatePushViewController a new instance of the view controller is pushed. We do that by using our fake navigation controller. You can find an implementation of this fake navigation controller in my demo repository. Here is how the test looks:

By doing it this way the SUT hasn’t to be aware that he is being tested. We test it that way because we don’t have any way of inject the UINavigationController as it’s a readonly property. An other approach is do swizzling of the navigationController property but that means change the implementation of the SUT while testing and I’m very strict with never do that. If you change what you test you are not testing it ;).

Conclusions

Testing a VC it’s not only posible but can be easy. I implement my VCs using TDD without problems and I tend to have a 100% testing coverage in these components. Testing is one of the fields that is evolving faster nowadays, if you follow debates like TDD is dead you noticed that we are far from founding the best way of doing it.

From my point of view is about making the good choices and balance the cost and the benefits. I think that a well designed VC is a good target for testing. Of course what I show here is only a simple VC but I hope it will give you some techniques to test bigger ones. For now you can find the complete suite of tests of JPLoginViewController at my demo Github repo. Take a look to them and let me know what you think.

I’ll be extending these techniques in further posts I want to thank the whole iOS team from Tuenti and especially @cestebanez for all I have learn working with them. A lot of the concepts I use during this post come from them and César has been our iOS testing leader since I joined this team 🙂

I would also say that I’m creating a little toolbox to help writing the less code possible when testing view controllers and I will present it in further posts. It’s a way to create safe UIView mocks. I also have an article about how to test container view controllers, follow me at twitter and I will announce when it’s ready 🙂 And remember  I will be happy to discuss all these techniques if anyone is interested, only make a comment, mail me or tweet me :)

Edit: I want to thanks Jorge Ortiz for helping me with my english mistakes, I’m learning from him and my english teacher the fast as I can to improve my articles and make them available for everyone 🙂

This entry was posted in iOS, Testing, View Controllers and tagged , , . Bookmark the permalink.

Leave a Reply