Behavior Driven Development in iOS

https://upload.wikimedia.org/wikipedia/commons/d/dc/Tightrope_walking.jpg

When writing unit tests, developers often implement the tests inside-out. This means tests for data access layer are written first, followed by business logic layer and finally, the user interface layer.

The problem is that the further we move away from the app behavior, the more isolated we become from its functionality. Behavior Driven Development allows us to look at the app from the user’s perspective instead of the developer’s. In this post I am going to implement a register user functionality using the principles of Behavior Driven Development.

The project name is “People” and it consists of a Single View Application, Unit Test Project and a UI Test Project. Instead of starting with the database layer we will start with the features/behaviors we want to implement. Some of the features are listed below:

  • As a user I should be able to register an account successfully
  • As a user if I use an existing user name then I should get an error message
I strongly believe that if you are not doing test first development then you are losing a lot of benefits of writing unit tests. TDD and BDD not only allows you to test your application but also dictates the overall architecture of the app.

Test First Development

Don’t even think about writing a single line of code in your app! We are going to start by writing unit tests. We will begin by writing our UI test which will represent the behavior of a registering a new user.

Notice the descriptive name for the test case as well as the unit test. We are testing for an actual feature, which will drive us toward our goals. If you run this test it is going to fail with flying colors since the view currently does not contain any controls.

It is at this point you will hop over to the Storyboard and add the required controls to pass the unit test. The interface screenshot is shown below:

Registration Screen

In order to access the controls in your test, you will need to provide an identifier using accessibility label as shown in the screenshot below:

Setting the Accessibility Label

Also, notice that there is no tapAndType function in the XCTest framework. The tapAndType function is a custom extension method which allows to quickly select and set the text of the UITextField.

Even with setting the identifiers, the test will still fail. The reason is that we have not yet implemented the code for the register button event and the user has not been taken to the login screen.

At this point you will return back to the RegistrationTableViewController and implement the button click event as shown below:

None of this code will work since you don’t have any class called User or an instance called dataAccess. This will lead you to implement the User class as shown below:

You may also write a unit test in iOS Unit Test Project to make sure that you can create an instance of the User class as shown below:

I don’t think that the above unit test adds any value to our quest of implementing the requested feature. The unit test is merely testing that we can create an instance of the class.

You should always write your unit tests to test the complexity of your business domain and not the language features.

This will lead us to the Data Access Layer, which we will cover in the next section.

Data Access Layer

To keep things simple we will be using UserDefaults to store our data. We will start by implementing a unit test as follows:

The test will not even compile because we do not have the saveUser and getUsers functions on our DataAccess class. Let’s add them as shown below:

The above code took multiple iterations and a lot of failing unit tests. Even now, the test will fail because User is not NSCoding compliant. Let’s update the User class so it conforms to the NSCoding protocol.

Now, if you run the test again it should pass because all the pieces are in place. Although the data access test may pass the outer most test will still fail since after the registration the user is not transferred to the Login screen.

Segues

Before we start adding segues take a look at the following code implemented earlier.

This is the final version of the saveButtonClicked code. The original implementation is shown below:

As you can see the original version did not performed any segues dynamically. The segue was created from the button control to the LoginTableViewController using Storyboard. This allowed the test to pass but the test was later broken when we moved on to the next feature.

  • As a user if I use an existing user name then I should get an error message

Now, you can see how test first approach has helped us design a better app.

Cleaning Up

One of the most important principles of unit testing is that it should always revert the state back to the original after it has been completed. This means if your unit test adds data to the database then at the end of the test it should remove the data from the database. This will make sure that the database is in initial state when the next test is run.

One of the techniques in UI Testing is to pass the arguments from the test to the application to which can be used to perform cleanup action.

The app is going to read the arguments and cleanup the resources before the test is executed.

Check out the video below which shows the unit test running in action.

Unit Test Running

Unit tests become very important in larger applications where the business domain is complicated. Although initially unit test will require more time to setup but it will pay in dividends as the project matures in the future.

You can download the complete source code here.

If you liked the article and want to support future articles then please follow the link below to buy my Udemy courses.

Thank you and happy coding!

Azam