Testing with UIKit — What is the right approach?

Duy Bui
Nerd For Tech
Published in
4 min readFeb 3, 2021

--

Before getting to the point, I would like to grab some coffee and recall our old friend: UIKit. The advantages of SwiftUI are quite obvious, the performance is better, the UI code is cleaner, easier to understand but still not reliable enough to convince all. Not to mention the majority of projects usually start from an iOS version less than 13 so, like it or not, UIKit still goes along with us in a few next years. So today, I am going to review it by discussing testing-related matters around this old framework

Image Credit: @timbennettcreative

Difficulties and no standard approach

UIKit is a kind of Apple’s built-in framework and we are out of control of it. We almost don’t know how it works and have to trust it. Sound familiar? Yeah, it sounds like I am talking about 3rd party framework. With these kinds of frameworks, we get usually confused and don’t know how to test this.

The popular approach I have seen the most is the Snapshot testing. That means we make sure the UI appear as we expect or check whether the flow is going right. But is it enough? Another problem is sometimes we even get lost in testing the framework — which is not our job.

In addition, there are many articles, tutorials, and debates on the Internet about what design patterns should be used to replace MVC. They might be MVVM, MVP, or VIPER, etc. But most of them lack the most important part, testing, and painfully, no one cares about it. Obviously, we — iOS developers, are missing something.

Behavioral testing approach

Below are some of my experience and knowledge when approaching UIKit testing. Besides the Snapshot testing, we can test our implementations when injecting them into this framework. That's called Behavioral testing. We should test the interaction with the framework instead of test how the framework works. For example,
- We don’t test: if the viewDidLoad is actually invoked ❌
- We should test: What we are going to do when the framework tells us that the view is loaded ✅

This approach is super hard and takes a lot of time and effort. Why? Because we have to learn more than others — not only its APIs on production but also corresponding methods for interaction in the test stage. And usually, these kinds of methods are the ones we rarely use.

For example:
- We want to send a request to the server once the view is load -> viewDidLoad()
- We need a way to get this signal when it has the job done — so we have to know: loadViewIfNeeded()

func test_doSomething_afterViewDidLoadGetsCalled() {
let myService = Service()
let sut = MyViewController(service: myService)
sut.loadViewIfNeeded()
XCAssertEqual(myService.callCount, 1)
}

Another example when I want to test my behavior feature after pulling to refresh, I need to know more rather than only production code.

refreshControl?.allTargets.forEach { target in
refreshControl?.actions(forTarget: target,
forControlEvent: .valueChanged)?.forEach {
(target as NSObject).perform(Selector($0))
}
}

Naming convention

Last but not least, naming convention. Sometimes, you need to name a function or a block of code in a general way, or it can show the behavior of the user at a higher level rather than a very specific name. This will separate the test part with the implementation details.

For example, to know the date of birth of a user, we have two options, the date picker or the user manually fills in the text field. Therefore, we should not name it like:
- sut.selectFromDatePicker or sut.getValueFromTextField
- we should name: sut.simulateUserGetsDateOfBirthValue

And simulateUserGetsDateOfBirthValue is an extension of our components. So from that, if there is a change from clients, we just need to change in the extension part — not touch anything in the test suite.

private extension MyViewController {
func simulateUserGetsDateOfBirthValue() {
textfield.getText()
}

Conclusion

The above is just one of the huge rocks that have blocked our UIKit test path. There are many other obstacles that we need to overcome.

As you can see, this is the knowledge that is not easy to acquire and learn. Figure out them on my own is not enough, I think and at that point, I really need a mentor who can show me these things. And luckily, I found a team called Essential iOS Developer which two guys: Caio and Mike. I am not advertising for anyone, I just tell the truth. They are the best mentors I have ever had. They change my mindset A LOT.

Ok, that’s all. I hope you love this article and share it with your friends. I always open to discuss anything related to iOS. See you in the next article.

--

--