Mock UIApplication for unit tests in Swift

This time I wanted to talk about how to mock UIApplication, or any other library for unit testing. When you are writing unit tests you want to be in control. With a specific input the output must always be what you expect. That is the main reason why you shouldn’t do network requests in unit tests. You don’t want your tests to fail for poor network connection. That’s also why you have to do some extra work with system or 3rd party libraries. But how can you trust an output of a function that uses a system library? You cannot inject the dependency for the class since the source is out of your control. How can you work around this problem? How to make the module testable so that you control the outputs?

In this post I am going to go through this situation using UIApplication as an example. I’ll do this by implementing a struct called URLOpener. URLOpener has one job: open Safari browser with a specific url. First, I’ll implement a non testable version. Then I’ll show you step by step how to refactor this implementation so that you can test it.

URLOpener has all the problems listed in the first paragraph:

  1. It uses UIApplication, a system library which source is out of your control.
  2. The wanted result is to open another application which output is depending on resources out of your control.
  3. Safari uses network to open the wanted web page.

I am going to tackle this problem with these steps:

  1. Define URLOpenerProtocol with similar functions to the ones needed in UIApplication.
  2. Extend UIApplication to conform to this protocol.
  3. Use URLOpenerProtocol in testing to create a mock object you can use with URLOpener.

You could argue if this functionality is something that you want to test in unit testing. The good news is, this same technique can be used with other libraries as well. UIApplication is familiar for most of the iOS developers and that’s why I chose it as an example.

What is UIApplication

“UIApplication is the centralized point of control and coordination for apps running in iOS.” That is what the documentation says about it. Every app has exactly one instance to it and it is created in the AppDelegate when the app launches. You can use UIApplication to launch other applications such as Email and Safari. Or you can use it to check if user has accepted remote notifications for you application.

Let’s get started

In this article we’ll use UIApplication to open Safari with a specific URL. The problem with this goal is that we don’t want to launch Safari in unit tests. So how can we still write tests for a module that has dependency for UIApplication, without actually using the real one? Let’s see how hard can it be to mock UIApplication for testing!

Non testable URLOpener

URLOpener is a struct that we will use to open the Safari. First, let’s check the simple non testable implementation.

URLOpener has one let variable which is an instance of UIApplication. It also has a function openWebsite. The function takes an URL that we want to open as parameter. It also has an optional completion block, which is called after website is opened or failed to open. UIApplication is used to check wether the URL can be opened, and to open the URL in Safari.

The problem is that UIApplication.shared is stored in a variable. We don’t have any control to the variable outside the struct. Even if this implementation works as specified, we cannot write tests for it. So let’s change it.

Inject UIApplication

Normally, we would use dependency injection in a situation like this. Dependency injection means that you inject a class to another when you initialise it. Later you can replace this injected object with a mocked one in testing. We’ll use the same technique here. But to make it work with UIApplication, we need to do some protocol magic. First let’s refactor the code so that we can inject UIApplication.
 
Create the init function:

Now we can give the UIApplication as parameter. Notice that we also changed the definition of the variable application. It is not constructed inside the class, but set inside the initialiser function. This is not quite what we want, so let’s refactor the code a little bit more. We want to get rid of the direct dependency for UIApplication. Let’s replace it with a protocol, so we can use the protocol in testing.

Protocol definition

First, let’s check what functions and variables we need from the library. In this case we need  canOpenUrlopen functions. So let’s define a protocol called URLOpenerProtocol:

You can find the function definitions from the UIApplication implementation. All we have to do is to copy them inside the protocol.

Next, we need to make UIApplication conform to this protocol:

You don’t need to put anything inside the implementation. Both of the functions already has the default implementation inside UIApplication. How easy was that?!

Next, we need to change the URLOpener implementation:

Here is the complete implementation of the testable URLOpener. We replaced the UIApplication type with the URLOpenerProtocol. We can do this since UIApplication now conforms to the protocol. We also changed the parameter in the initialiser to URLOpenerProtocol. Reason for this is that we don’t need to use UIApplication in testing. We can replace it with something that conforms to URLOpenerProtocol. Let’s see how to do that.

Mock UIApplication in testing

Now let’s see how to implement the MockUIApplication for testing.

Mock UIApplication for unit testing

Mock UIApplication


MockUIApplication implements the functions from URLOpenerProtocol. It also defines the wanted behaviour. There is also one extra variable called canOpen. We use canOpen to define the return value of the canOpenURL function.
 
Now that mock UIApplication is ready, all that we need to do is to write the tests!

Testing URLOpener

I will not go through unit testing in general here. You can check it from my previous block post: How to unit test ViewModel in Swift. The post is about unit testing view model, but also goes through the basics of unit testing.

So, we have a function called testOpenWebsite. First, we create an instance of MockUIApplication and set canOpen to true. Next, we’ll create the URLOpener instance and give MockUIApplication as parameter. We replaced the UIApplication with the mock object. This means that now we are in control of its behaviour.
 
Rest of the code is normal unit testing. We create an expectation and wait for it to complete. Inside the openWebsite completion block, we call fullFill for the expectation and also assert the return value. If everything went fine the test passes and we can give our selves a little tap on the shoulder.
 
That’s all we need to do to mock UIApplication for unit testing!

Conclusion

It’s pretty simple to mock UIApplication or some other library for unit testing. Define a protocol with needed variables and functions. Make the original class to conform to the protocol. Replace the dependency with the protocol in the testable class. Create mock object in unit testing, that conforms to the protocol. Then inject it to the class that you are testing.
 
I recently used this same technique to unit test a view model. It had to present different data for users who had agreed for remote notification and for the ones that had not. This is a very useful tool that you can use also with cocoa pods libraries. You might have heard that you shouldn’t mock 3rd party libraries, but I disagree. Always consider case by case whether something needs to be tested. If it was worth testing in the first place, it’s worth to update the tests if the framework is updated.
 
I hope you liked the article and that I was able to help you to write better tests. Incase you have some questions, comment, tweet or email me and I’ll get back to you.
 
Thanks for reading and have a great day my friend!