iOS Unit Testing

by simple example.

Karthikeyan T
IVYMobility TechBytes
7 min readApr 19, 2021

--

If you are new to unit tests in Xcode, then this post will help you get started and by the end, you can able to write a unit test for a sample signup screen.

To test an app, one needs to write three different kinds of test code.

  1. Unit Test — Test the single function without any external dependency. e.g. to test the given email is valid or not.
  2. Integration Test — Test the single function with dependency. e.g., to test that the value from the server response is handled for nil case too.
  3. UI Test — Automate the user behavior and interaction with the app. e.g., On behalf of the user, trigger the “Signup” button action to validate the fields.

Enable Unit Tests in Xcode Project

  1. While creating a new Project, click the checkbox “Include Unit Tests”, “Include UI Tests”. Once created, you can able to see a folder in the project called “ProjectNameTests” and the XCode already creates a default test case class with a template generated to start working with. The XCode creates a new Target for your application. Also, we can create a new test class by opening a new file (⌘ + N), filter by the text “test” and you can able to find the “Unit Test Case Class” and “UI Test Case Class”.
  2. If the Project is created without enabling the UnitTest, then you add the UniTest by tapping “⌘ + N”, filter by the text “test” and you can able to find “Unit Testing Bundle” and “UI Testing Bundle”.

Default UnitTest class — Explained

  1. override func setUpWithError() throws { } — Called before the invocation of each test class method. Therefore you can create a new instance of a class for every test class method.
  2. override func tearDownWithError() throws {} — Called after invocation of each test class method. Therefore you can release/remove the instance which is created in the previous method. It’s very important to use the teardown method and make it clean up the created objects.
  3. func testExample() throws {} — a sample test method. Later, one can rename and add some of your own code to start testing. Also, you can add more test methods and the method name should be unique. The test method is indicated by a diamond shape symbol instead of a line number.
  4. func testPerformanceExample() throws {} — this will give the total time of your test classes.

In the above cases, a new instance is created for every test method. But still, you can create an instance common to all unit test methods. This can be done by creating the class function “override class func setUp() {}”. The order of execution is shown in the below image.

Order of execution

Run Unit Test in Xcode

  1. Click the diamond button on the test method to run a single test method.
  2. Click the diamond button on the class to run all test methods in the test class.
  3. Click the diamonds in Test Navigator (⌘ + 6).
  4. ⌘ click and selects multiple tests, right-click, run multiple test methods or use the shortcut key Ctrl + alt + ⌘ + U.

XCode runs unit test based on the alphabetical order of the method name.

Unit Test

A unit test is a small self-contained method to test some functionality. For example, on the signup page, we will have a function to check whether the given password passes the password policy or not. Now, we need to write a unit test method that will test your password validation function with more sample passwords that will include valid and invalid passwords.

A computed property to validate the given password in the SignupModel
The UnitTest method test_PasswordValidation() will call the isValidPassword() function with the specific input parameters and then will expect back a specific result.
Pictorial representation of how the Unit case works
SignupModel for SignupScreen

In the below example, the unit test was written to test the functions written in the SignupModel with a sample input. Here I have given the valid inputs so all the unit test functions are passed which is indicated by a green diamond shape checkbox.

UnitTest to validate the SignupModel

Don't worry about syntax in the test class. It will be explained in the next section.

In the above example, I have intentionally given the non-identical password. So the unit test function fails where the diamond is marked with a red. Also one can note that the diamond before the class (Line #12) is green if all the UnitTest passes or else it will be red.

Similarly, Test_SignupViewController() written to test the IBOutlets connection, UITextField’s attributes (like placeholder, textContentType, keyboardType, isSecureTextEntry) and UIButton’s IBoutlet, attributes and IBAction for UI Test.

The unit test will run very fast because when testing the function any dependencies that this function might use will be replaced with fake/dummy/mock objects. Dependencies may be of connecting to a database or sending a URLRequest to server for fetch the required data.

Unit Test should be

  1. FAST — achieved by writing a simple code with dummy data.
  2. Independent — Each unit test should be isolated from other unit tests.
  3. Repeatable — Should produce the same result irrespective of the number of times it executed, inputs, and the test environment.
  4. Self Validation — The developer should not need to do a manual check. i.e., whether the unit test is passed or not
  5. Thorough / Timely — Should cover edge cases (like by giving valid/ invalid, legal/Illegal, and large values of inputs).

Test Assertions

The assertion will check for expected values or outcomes in the test methods. Some of the used assertions in the sample project are explained here.

Boolean Assertions

It will test a condition and generates a Boolean result.

  1. XCTAssert(_:_: file: line:) — Checks for the condition is true.
  2. XCTAssertTrue(_:_: file: line:) — Checks for the condition is true.
  3. XCTAssertFalse(_:_: file: line:) — Checks for the condition is false.

The following asserts take 2 mandatory parameters like expression(condition /expression which gives Bool value), message(message to convey). The other two params are default which will print the fileName and lineNumber in the debug area.

Boolean Assertions

Nil and Non-Nil Assertions

It will checks the output of a test condition is either Nil or Non-Nil

  1. XCTAssertNil(_:_:file: line:) — Asserts that a given expression is Nil.
  2. XCTAssertNotNil(_:_: file: line:) — Asserts that a given expression is NotNil
  3. XCTUnwrap(_:_:file: line:) — Asserts that a given expression is NotNil and it returns the unwrapped value.
Nil and Non-Nil Assertions

Equality and Inequality Assertions

Check whether two values are equal or unequal.

  1. XCTAssertEqual(_:_:_: file: line:) — Asserts that two values are equal.
  2. XCTAssertNotEqual(_:_:_: file: line:) — Asserts that two values are not equal.
Equality and Inequality Assertions

Comparable Value Assertions

Compare two values to determine which one is larger or smaller than the other one.

  1. XCTAssertGreaterThan(_:_:_: file: line:) — Asserts that the first expression value is greater than the second expression value.
  2. XCTAssertGreaterThanOrEqual(_:_:_: file: line:)— Asserts that the first expression value is greater than or equal to the second expression value.
  3. XCTAssertLessThan(_:_:_: file: line:) — Asserts that the first expression value is less than the second expression value.
  4. XCTAssertLessThanOrEqual(_:_:_: file: line:) — Asserts that the first expression value is less than or equal to the second expression value.
Comparable Value Assertions

Integration and UI testing will be explained in the upcoming post.

Get the full source code at Github

References: Apple Documentations

Find it a good read?

Recommend this post (by clicking the 👏 button) so other people can see it too… reach me on LinkedIn Karthikeyan

--

--