Test Driven Development in iOS, SWIFT - Part 2

G. Abhisek
Swift India
Published in
6 min readJun 16, 2018

This blog is a continuation of my first blog on Test Driven Development in iOS. Please do have a look into it to follow up on this. If still, you are lazy for that, then you can download the starter project for this blog UT_Starter2 and get going.

Let us recall our UML diagram for DemoTestsProject that we were building up.

So let us write down test cases.

One important thing that we should keep in mind while Unit Testing is we should always try to test our public methods and properties but avoid testing the private ones.

Wait. What!!! Are you serious dude?

The reason is public methods and properties are what we expose to the outer world and would not want them to go wrong. There is an awesome Stack Conversation regarding this. Please feel free to check it out.

Test cases for PlaceCellDataModel

In order to display a places list, we are using a table view. So referring to our UML diagram, we have a PlaceTableCell representing our UITableViewCell class corresponding to the cell. The PlaceTableCell uses a data model of PlaceCellDataModel type to link its UI. So we will be testing whether data model is getting its attributes set properly or not.

Navigate to PlaceCellDataModel.swift and prepare your test class as below:

PlaceCellDataModelTests

What we did here?

  • We declared the sut i.e System Under Test as PlaceCellDataModel and a place variable that we will be passing in the init() of PlaceCellDataModel.
  • We wrote down a test function i.efunc testAttributes() to test our attributes.
  • We checked for two assertions i.e XCTAssertEqual & XCTAssertNotNil. We have already encountered the first assertion type earlier. The second assertion type checks that our properties shouldn’t be nil, as you won’t be wanting your cells data model to hold nil values even though it could hold an empty string.
  • In our else block, we have used XCTFail("") which is a failed case with a message. As per our code, we are already checking that the attributes should not be nil. If it happens to be nil then it's a failed case.

Got errors. Okay, let us fix that as a part of our GREEN phase of TDD. Ring bells… !! Remember our discussion in the first blog.

Place the below code in PlaceCellDataModel.swift :

PlaceCellDataModel

What we did here?

  • Nothing fancy. We just initialized our cell data model with our requirements.

Test cases for PlaceListViewModel

So there are the following properties that we need to obviously test for PlaceListViewModel :

Output properties - numberOfRows, title

Methods - tableCellDataModelForIndexPath(_ indexPath: IndexPath)

Okay, so if you closely look into init() method of PlaceListViewModel , you will see that we are passing something called PlaceDataFetcherProtocol. What is this again?

A slight Dependency Injection Stuff

We need a network service call or any database call to provide us with a list of Place. Our view model should only be requiring the result of the call i.e list of places or error message. Test cases should be super fast and efficient. We will not be wanting to make real API or Database calls for data. That would be really extensive, won’t it? So a possible solution would be something we call Dependency Injection. So we would define a protocol PlaceDataFetcherProtocol that would consist method to fetch a list of places. So whenever we mock the Network or Database call, we can prepare a mock class conforming to the same protocol and implement the function to provide us a fake data.

Catch your breath. It will be more clear once we implement the code. :)

Let us write down the mock DataFetcher first.

Navigate to PlaceListViewModelTests and paste the following code outside the class declaration unless you want one class to be inside another class. ;)

StubPlaceDataFetcher

What we did here?

  • We created a StubPlaceDataFetcher to stub our data fetcher. This confirms PlaceDataFetcherProtocol that we will encounter next to provide with mock place list.

Now you will be seeing warnings, let us remove them.

Create a new folder named DataFetcher and a new Swift file named as DataFetcher . Place the below code in the file:

PlaceDataFetcherProtocol

What we did here?

We declared a protocol named PlaceDataFetcherProtocol which has our required method fetchPlaces(completion: ([Place]?,_ errorMessage: String?)->()) to provide us with a places list and any error message to be displayed.

Now let us navigate to PlaceListViewModelTests and write down test cases for PlaceListViewModel . Replace PlaceListViewModelTests class with the following code.

PlaceListViewModelTests

What we did here?

  • We will not discuss each of the test case code as they are quite simple and almost identical.
  • The most important pointer to note is the way that we use the StubPlaceDataFetcher. This approach can be used to mock network as well as database fetching layers.

Still, there are a lot of errors as we have not yet configured our PlaceListViewModel. Let us navigate to PlaceListViewModel.swift and place the following code into it.

PlaceListViewModel

What we did here?

  • In the init() method we passed a PlaceDataFetcher type and we have initialized a viewDidLoad closure to inform the view model about the view load of the controller.
  • We then get the places data by calling getPlacesData() which in turn calls the dataFetcher to provide a list of places. It is obvious for you to wonder what is the data source from which place list will be fetched. So for keeping our blog simple, we will fetch the places from a .json file in our Resources folder i.e PlacesList.json.
  • We then configure our tableDataSource by calling configureTableDataSource().
  • After that, we configure our view model outputs.

Let us prepare the Data Fetcher class. Paste the following code in DataFetcher.swift:

DataFetcher

What we did here?

  • As discussed earlier, our fetcher confirmed to the PlaceDataFetcherProtocol .
  • Then we fetch the place list from PlaceList.json and process the raw JSON to our desired type.

Now it's time to link our UI and see the results of our toil. Replace code of PlaceListController and PlaceTableCell with the following:

PlaceTableCell
PlaceListController

The implementation code is pretty straightforward. We just take values from the view models output and populate the table.

Output:

Congrats on the successful unit testing of your module.

What did we learn?

  • Writing test cases for business logic.
  • Mocking data.
  • Mocking data fetching layer.
  • Following TDD concepts and building up the code.

Sample Code: You can find the completed project in the Final folder of DemoTests Repo.

I would love to hear from you

You can reach me for any query, feedback, or just want to have a discussion by the following channels:

Twitter — @gabhisek_dev

LinkedIn

abhisekbunty94@gmail.com

Please feel free to share with your fellow developers.

--

--