Writing testable code in iOS

Suryakant Sharma
5 min readApr 20, 2019

In this article, we will look at various ways to make our code more intuitive and testable. I will walk you through various techniques and explain with the help of real-world examples. Let’s dive in 🚀.

  1. Input/output refactoring

Let’s look the code below for removing the items from cache when size became more than maxSize.

in the above code, there is no input comes and no output return because the result is written in the cache.

Can we refactor the above code for testability one way is to subclass the FileManager and inside this create an array of removed items and assert with our logic if the oldest item doesn’t get removed on size exceed, let’s do this by another way of separating input-output by defining the CleanUpPolicy Protocol so that we can mock it later for unit test.

In the above code, we had refactored the Unified/Unique input/output. Well its time to prove the point that we had done some major thing to do our unit testing easier

see how easy it to test our complex logic in a few numbers of logic.

2. The state is kept local / avoid singletons

let say we want to save the user’s balance depending upon his remaining balance after buying.

In above example we are saving user balance to system API userDefault here, let say we want to test the user balance, you have to rely on reading shared preferences which are time-consuming and break the Fast of the FIRST principle. Let’s make use of singleton local and inject it in the argument.

But wait, what we had achieved from this? Because we had made singleton variable to local variable with help of default argument 😊.we can subclass UserDefaults and override the set method for getting the output of our function to check is it working as per our expectation. Let look this in code.

3. Dependency Injection

Although we had also use dependency injection in the previous example it is important from testing purpose.

dependency injection is a technique whereby one object (or static method) supplies the dependencies of another object. A dependency is an object that can be used (a service). An injection is the passing of a dependency on a dependent object (a client) that would use it.

Dependency mess

No matter how good your code is but it's hard to test until there is dependency mess is there in your code e.g using singletons without injecting or using a global /static variable.

There are three types of injection you can use in our code

  1. Constructor injection or initializer injection in swift

2. Property injection

3. Function injection

Let’s look at some examples of this by designing the appraisal system

I think the example is self-explanatory in which company’s employees get bonuses according to the performance rating.

One thing is to note that we are wrapping the result in separate enum of two types instead of returning many types which make our output unified. Which make testing easier.

Our Appraisal class uses dependency injection well. Let see how easy it to test

Great. Let some other cool things with method injection.

We have a cart with products selected from our app by the user and can apply the coupon to take discount fairly simple 😐. We are injecting the coupon and product at the right place by method injection. Let write test case for this.

4. Dependency Inversion Principle/ Protocol Oriented Approach

In object-oriented design, the dependency inversion principle is a specific form of decoupling software modules. When following this principle, the conventional dependency relationships established from high-level, policy-setting modules to low-level, dependency modules are reversed, thus rendering high-level modules independent of the low-level module implementation details. The principle states:

  1. High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g. interfaces/protocols).
  2. Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.

Let’s look at this in code.

Document opener class is a high-level module which is dependent on low-level module UIApplication. which is not obeying the dependency inversion principle. In our testing methodology, there is no way to test the url made from our logic. Let make this depending upon the protocol and use our old friend default argument here for reducing unnecessary boilerplate code.

We had made DocumentOpener class unaware of UIApplication class by making a protocol of URLOpening which had the method we required. The thing to note here is we make low-level module UIApplication conform by the protocol which does nothing except conforming, we don’t have to write the required function again because UIApplication class have this function already. Now both low level and high-level module depends upon interfaces/protocols. Let’s finally test DocumentOpener class by mocking URLOpener.

Our MockURLOpener had to conform URLOpening protocol that's it. You can write test easily and confidently. Congrats you had learn many tools which help you writing testable code.🎉

Conclusion

You should be aware of things when writing the code like your code can be mocked or reachable for verifying the output. Make sure your dependency doesn’t create the mess .

Thanks for reading! 🚀

Resources

--

--