Better Programming

Advice for programmers.

Follow publication

Unit Testing SDKs on iOS

Leo
Better Programming
Published in
3 min readNov 17, 2021

Photo by @zanilic from Unsplash

If you’re not a Unit Test Master yet, check out my previous article for tips on iOS unit testing.

When developing, we should think about how to make our code testable. Unit testing third-party party frameworks can be difficult, even more so if they’re static because they cannot be easily injected and mocked. This article will cover some techniques that you can use to avoid not testing your SDKs.

Before you start testing 👀

Before we deep dive into our tips, remember to always isolate the frameworks from your project modules and classes, whether by creating a wrapper, facade, adapters, or something else. Our project’s architecture and use cases should not rely on external frameworks because they always change. So remember to isolate them in a separate class that can be easily changed without affecting the classes that implement it.

Static Methods

The most common type of unfriendly frameworks.

Problem

We usually see implementations like this:

We're using the IncogniaSDK as an example.

That kind of implementation limits us from mocking because we cannot simply inherit the desired class and override its static methods, just because they're static.

Solution

Fortunately, we can use protocols and the metatype type from Swift. According to Apple’s documentation:

A metatype type refers to the type of any type, including class types, structure types, enumeration types, and protocol types.

This means that we can declare a variable that will refer to the metatype type of the protocol.

  1. First of all, you should create a protocol that contains the same methods as the framework classes. In our case, we just need to implement the initSdk() method.

2. Use Extension to make your framework’s class conform to the created protocol. No errors should appear, since we’ve created the exact same methods with the same keywords.

3. Now, you should inject the protocol in your class using Swift’s metatype .

Type keyword and the framework’s class using .self.

3.1. Another kind of implementation is to use the concrete framework’s class initializer and use type(of:) to access its methods instead. That implementation should result in the same as the above technique, but written in a different way.

4. To test it, just create a test double that conforms to the created protocol. You should inject this mocked class instead of the original one when testing.

Singletons

The main idea of the Singleton (anti)-pattern is to have just one instance of an object running in your application with the existence of one global point of access. Be careful using them, since they violate the single responsibility principle.

Problem

The below class is a simple implementation of a common Networking class.

Implementing like that makes the code untestable.

Solution

To enable this code to be tested, the class that is implementing our Network services needs to ask for a dependency, not define one itself, so we’ll inject one.

You can opt to use protocols, inheritance, or closures to continue.

1. If you choose inheritance, just declare a variable using your class type. If the property is public, you can do a property injection. Here, we're using the constructor injection. Remember that the NetworkingService class should not be set as final.

Since the methods are not static, you can override them in your mocked class.

2. If you decide to go with protocols or your project requires you to create only final classes, the implementation should be pretty the same as we did with static frameworks. Just create a protocol with the exact same methods as the class that will conform to it.

To test it, just create a spy that conforms to the created protocol:

3. Testing with closures is also a technique if your class allows public properties.

To test it, just call the requestProperty property and the completion should be the "fake" one for testing purposes.

Voilà!

Conclusion

That's it! Unit test your application guarantees the code quality and helps to avoid future bugs. So, always remember:

  • Keep your framework's class as decoupled as possible from the other classes. Frameworks change a lot;
  • DI (dependency injection) makes the class ask for a dependency, which means that you can inject mocked classes, for example;
  • Static methods can't be overridden;

References

Leo
Leo

Written by Leo

Senior iOS Engineer @ TikTok Singapore

Write a response