
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:
- It uses UIApplication, a system library which source is out of your control.
- The wanted result is to open another application which output is depending on resources out of your control.
- Safari uses network to open the wanted web page.
I am going to tackle this problem with these steps:
- Define URLOpenerProtocol with similar functions to the ones needed in UIApplication.
- Extend UIApplication to conform to this protocol.
- 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.
1 2 3 4 5 6 7 8 9 10 11 12 |
/// MARK: - Non testable implementation struct URLOpener { private let application = UIApplication.shared func openWebsite(url: URL, completion: ((Bool) -> Swift.Void)?) { if application.canOpenURL(url) { application.open(url, options: [:], completionHandler: completion) } else { completion?(false) } } } |
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
1 2 3 4 5 6 7 8 |
struct URLOpener { private let application: UIApplication init(application: UIApplication) { self.application = application } ... } |
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 canOpenUrl & open functions. So let’s define a protocol called URLOpenerProtocol:
1 2 3 4 5 |
/// MARK: - URLOpenerProtocol protocol URLOpenerProtocol { func canOpenURL(_ url: URL) -> Bool func open(_ url: URL, options: [String : Any], completionHandler completion: ((Bool) -> Swift.Void)?) } |
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:
1 |
extension UIApplication: URLOpenerProtocol { } |
Next, we need to change the URLOpener implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
struct URLOpener { private let application: URLOpenerProtocol init(application: URLOpenerProtocol) { self.application = application } /// Opens Safari with the provided URL /// /// - Parameters: /// - url: url that will be opened /// - completion: called after openign is completed /// param is true if website was opened successfully /// param is false if opening failed func openWebsite(url: URL, completion: ((Bool) -> Swift.Void)?) { if application.canOpenURL(url) { application.open(url, options: [:], completionHandler: completion) } else { completion?(false) } } } |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 |
struct MockUIApplication: URLOpenerProtocol { var canOpen: Bool func canOpenURL(_ url: URL) -> Bool { return canOpen } func open(_ url: URL, options: [String : Any], completionHandler completion: ((Bool) -> Void)?) { if canOpen { completion?(true) } } } |
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.
1 2 3 4 5 6 7 8 9 10 11 12 |
func testOpenWebsite() { let mockUIApplication = MockUIApplication(canOpen: true) let urlOpener = URLOpener(application: mockUIApplication) let waitForWebsiteOpen = expectation(description: "Web site was not opened!") urlOpener.openWebsite(url: URL(string: "http://www.google.com")!) { open in waitForWebsiteOpen.fulfill() XCTAssert(open == true, "testOpenWebsite - FAILED to open web site") } waitForExpectations(timeout: 0.1, handler: nil) } |