A Complete guide to Unit Testing in Swift- Part II
Writing testable code, mocking & unit testing
In the first part, we covered the basics of Unit Testing. We created a basic calculator and wrote test cases for its functionality. In this part we will dive more into a real world scenario.
Unit Testing & Testable Code
Another terminology that is synonymous with Unit Testing is writing Testable Code.
Your code can be clear, understandable and self-documented but still untestable at the same time.
So what does writing testable code means? While writing tests, we prefer our code to be an isolated unit. One that takes an input & provides an output. One that follows SOLID, DRY or KISS principles. If our code performs lots of unrelated operations, then the complexity of writing tests increases.
One example of this in iOS is the misuse of UIViewController.
Above UIViewController is responsible for fetching some data from api, process the logic behind setting the label and then finally set the label with the data. In this case, if you want to write a unit test for testing the condition to update the label, it becomes impossible.
In an ideal scenario, View Controller should be concerned only about operations related to view, not about business logics such as fetching data from api or conditions to update the label.
Writing Testable Code
So how can we make it testable? Let’s refactor our code first.
We moved business logic to its separate class “LabelDataService” which conforms to “DataService” protocol. We could have moved the logic to fetch data from api to another class but for the sake of simplicity, lets work with above code. Here we can easily test the condition to update the label in a similar way that we did to test calculator functionality.
But what if you want to write unit tests for “fetchDataFromApi()” method? Implementing a test case where you fetch data from a real api everytime your test runs might not be feasible scenario. There might be numerous api related operations in your project and running tests for them would take a long time to complete. Also tests might fail or pass depending upon the server or network conditions. So in real life, to simulate these type of scenario we use “Mocks”.
Simply put, Mocks are the fake implementation of your real code. Lets see an example where we mock “fetchDataFromApi()” method.
We replaced the real implementation of “fetchDataFromApi” with a fake one which returns either success or error. Initializer for this mock requires a boolean value to determine whether to simulate success or error condition.
Now we can easily write test cases which doesnot depends upon the network request.
Lets recall what we did. We refactored our code to make it testable, created a mocks to simulate api request and learned about writing unit tests using that mocks.
In the next and final part, we are going to learn how to write unit tests for asynchronous code, delegates & closures.