How to Test Using Fake Data on iOS
In order to provide high-quality software and avoid regression, implementing unit testing is a must for every iOS application.
Mocking objects is a technique in unit testing that creates fake objects by using the same APIs as the real ones.
This article is wirtten to provide you with the best practices on how to use fake data and write unit tests for the most occuring scenarions on iOS apps.
When writing unit-tests we should always avoid altering real data of the application target and instead use fake data just for the testing purposes.
The following parts will discuss how to write tests by using fake data for commonly used iOS API-s.
User Defaults
In Software development, it is always a good practice to reduce dependencies of the Objects. Dependencies in the best case should be injected into the classes that use them.
But if we check the real-life iOS Development scenarios almost every project uses UserDefaults
by calling it’s API directly for storing or retrieving any data.
Therefore we will try to offer a practical solution for testing UserDefaults
rather than abstracting its API with protocols.
We can create two new functions on UserDefaults
It is always a good practice not to alter application data from unit test target, so therefore we should create another place of saving user data for our testing target.
In this case, we initialize a new object of UserDefaults
with suiteName — testDefaults
that it is completely independent of standard UserDefaults
.
Let’s try to write a simple test that uses UserDefaults
Since this data is basically used only for testing we should avoid leaving that data hanging in our application files, therefore, we create a function responsible for dropping this storage after we are done with the test.
Best place for purging this data will, of course, be the tearDown
function in our unit testing class.
Singelton Objects
Singletons Objects are highly used on iOS on many APIs, we can find them on NSFileManager
, NSApplication
, UIApplication
and in many other places.
Knowing how to test singletons is a useful thing to know for iOS Developers.
In our example, we will use the iAd framework of apple. We will create a file to get a local JSON response instead of real data on requesting ad-attribution details.
A nice feature in iOS is that extensions in swift allow us not only to add new functions for a predefined API but also making them conform to our own custom protocols.
Let’s define an AdvertismentClient
protocol
After that, we conform to this protocol by both default ADClient and our fake advertisement client like following
We then alter dependency to either
private var adClient: AdvertismentClient = ADClient.shared()
or
private var adClient: AdvertismentClient = MockAdClient()
and use it as follows
In this way, we can easily decide when to use real data and when the testing ones, depending if we are unit testing or calling the API from our live application target.
Core data
Core data is still highly used in iOS for caching data. Testing core data entities can be tricky. Below we will explain a good practice of both organizing Core Data Services and Faking Data.
In general in most of the cases is always a good thing to create a service class that is responsible with fetching and writing specific data on the database, rather than using core data code all over the project.
This mainly has two benefits:
- It decouples you from the underlying database that is being used, if you want to replace core data with any other database in the future you’ll have to make changes only in one class.
- By doing this we can easily decide which
CoreDataStack
will get used or any other setup that we may need in some other framework.
We will create a CoreDataStack
protocol and after that two CoreDataStacks
that conform to this protocol one MainCoreDataStack
and one MockCoreDataStack
.
Our DatabaseService then can be initialized by either of them depending on if we are using it on our application target or on our Unit Testing target.
Our main core data stack will have a default setup like follows
When Unit testing always we should avoid changing the state of current ‘real’ objects when testing them.
So when we want to create fake core data entities we should have a separate persistent store and use in-memory store type configuration so the changes made won’t be saved on the disk and they will be completely separated from currently persisted data.
We now will be able to create our database service that gets initialized by default with MainCoreDataStack.
And in our test class, we can initialize it with the fake data stack
We can now write a few simple tests as follows:
By using this approach we can easily test our DatabaseService without affecting any of the data stored by the application target.
Network Requests
When Testing Network Layer we can use the protocol-oriented approach to create a protocol and conform to it by both real NetworkService and MockNetworkService and after that inject dependencies by either using real or mocked service.
In this article though we will be using a really nice open-source library called OHHTTPStubs that will handle mocking and stubbing even better.
The good thing about this library is that it works great with the famous iOS networking library Alamofire.
Stubbing Network request is really easy with OHHTTPStubs you can replace any response for a specific path or host by giving a custom response with a dictionary.
After this, every request that goes from the app to the following URL will return our custom response instead.
let tasksURL = URL(string: “https://jsonplaceholder.typicode.com/todos")!
What’s also really cool about custom responses is that you can easily test if error and edge cases are handled correctly by simply returning an error in the response.
Manually building the dictionary for response is a great feature, but when we want to return a large JSON data with lots of properties it can become messy and hardly maintainable in our test classes.
In those cases, we can use a JSON file to stub the response like following.
Now everytime our app sends the request we will get the response from the file myResponse.json
that we saved in our files.
We should remember though to avoid saving sensitive information in these JSON files because if we ship these files together with the application they can be viewed easily.
You can check my article on the security topic for more.
In Conclusion
Unit testing our application is a must if we want to avoid regression as much as possible and also try to provide a flawless application.
In this article, we have discussed how to provide testing for common cases that occur during iOS Development.
We discussed how to test UserDefaults, Singeltons, Core Data, and Network Requests.
If you liked this article make sure to clap to show your support.
Follow me to view many more articles that can take your iOS Developer skills to a next level.
If you have any questions or comments feel free to leave a note here or email me at arlindaliu.dev@gmail.com.