Unit Testing and UI Testing in iOS

Shilpriya Shah
Simform Engineering
7 min readAug 7, 2023

--

Get started with unit testing and UI testing for iOS

Testing is an integral part of any project. We perform some developer-level testing as well as QA Testing. However, sometimes a single feature change forces us to test the whole flow to ensure nothing breaks. But what if Xcode could automatically test this flow and notify us of any issues? Implementing UI/Unit test cases in our Xcode project can make this a reality. It’s a game-changer!

Table of Contents:

  • Configure Unit and UI tests in new and existing projects
  • Understand the life cycle of the test case
  • Add a Unit test case with an example
  • Add a UI test case with an example
  • Best Practices
  • Conclusion

Configure Unit and UI test

New Project:

Creating a new project is super easy. Just tick the “Include Test” checkbox ☑️ when adding the project name, and it will automatically set up 2 Bundle targets for you: one for Unit Test and one for UI Test.

[Add Test Files]
[Test Targets]

Existing Project:

If you are already working on a project, follow these steps:

Step 1: Select file -> New -> Target.

[Add New Target]

Step 2: Write test in Search filter -> Select UI Testing Bundle for adding UI Testing / Unit Testing Bundle for adding Unit Testing.

[Select UI/Unit Test Target]

Step 3: Give the name of your testing bundle.

Note: In practice, use the UI/Unit Test Bundle name following the App Name convention. For instance, if your app is named “ClassicChess,” then the Unit Test Bundle name should be “ClassicChessTests,” and the UI Test Bundle name should be “ClassicChessUITests.”

[Add Target Name]

You have just added your First Test bundle. Few more steps to go.

Understand the life cycle of the test case

Now, quickly open the test file inside your newly created Unit test bundle. Can you see some predefine methods just as below?

[Test File Initial Look]

Let’s understand each of them:

1. setUpWithError(): Call every time before the test method call; use this method to initialize any object.

2. tearDownWithError(): Call every time after test method execution; use this method for any cleanup.

3. testExample(): This is one example method of how you can create a test method.

Note: Every test case method that you want to define should start with a test prefix.

For example, testValidUserName(), testPhoneVerification(), otherwise it will not identify as a test method.

4. testPerformanceExample(): Use this to check the performance of the test case.

Add a unit test case with an example

How to write a test case

Let’s understand that with a password validation example.

[Unit test case for the password validation]
  1. Import your project target and ensure it’s testable. This will allow the file to test the specified target. Here, our demo project name is TestCaseDemo.
  2. We have developed a System Under Test (SUT) for testing password validation in the Signup view model. The SUT is essentially an object of the class we wish to test.
  3. To ensure proper setup, we initialize all necessary objects in the setUp method, including the sut object for our example.
  4. Think about how you will separate your test case into three different sections: Given/When/Then, and create a test case based on that.
  • Given: We provide the test input, which, in this case, is a password.
  • When: We specify the test conditions, which involve checking whether the password is valid or not.
  • Then: We use the predefined methods XCTAsset offers to verify if the test case passes or fails.

5. After completing the test execution, it’s crucial to clear all objects. In this scenario, we clear the signupViewModel sut, which we had initialized in the setUp method.

How to run a test case

[Check how the test case can be run]

Run single test case: Click on the diamond button on the left side of the test method name, e.g., testPasswordValidation(), testEmailValidation().

Run all test cases: Click the diamond button on the left side of the class name declaration.

Check if the test case passes or fails

[different symbol of a test case to check its pass or fail]

Once the test case runs, you can see the diamond color is changed:
- If a diamond is green, your test case passes.
- If a diamond is red, there is some issue, and your test case fails. You can also see a failure message alongside assert, which is not passed.

Add UI test case with an example

For the UI test case, we will take the example of a simple login screen with two text field emails and a password. On the click of the login button, it will redirect to the welcome screen. We are going to do UI Testing for login success flow.

Set Accessibility Identifier to your component

An accessibility identifier becomes essential when accessing a specific UI component for a test case.

[How to set accessibility Identifier from storyboard]
[How to set accessibility Identifier programatically]

How to record elements of UI test case

Open the UI test case file. Create test method testLoginSuccess(), put a cursor inside this method, and now click the 🔴 button on the debugger menu.

[Record Test flow]

This will automatically launch the application, and whatever you touch/type on your device, its code will be automatically added to your test method.

Recording is to find the element. You can record the flow of your test case and get the required element with auto-generated code.

Note: While recording, there will be some unnecessary code for your specific test, make sure to clean them up.

Now, cleanup the code and write a login success test case:

[UI test case for the login success flow]

Code Explanation:

  1. Grouped all the accessibility identifiers in the struct to avoid hardcode values and manage them well.
  2. Grouped all the failure messages in a struct to avoid hardcode values and manage them well.
  3. setUpWithError(): will call every time on a new test case where we have launched the app continue after failure = false means on any test case fail execution will be stopped.
  4. testLoginSuccessFlow(): to check whether login success flow is correct or not by ensuring at different steps, specific UIElement exists or not.

Now run the test case:

Congratulations on successfully creating your first UI test case for the login flow! The test has passed, indicated by the green diamond, after executing the test case. Remember to perform this test whenever you make changes to the Authentication module. You can simply click on the diamond button to ensure that nothing is broken.

Best practices

  1. Ensure every UI element has an accessibility identifier for easy UI testing. Group them together in a struct to avoid hardcoded strings and maintain organization.
  2. Remember to clear any objects created in the setUp method during the tearDown method.
  3. Utilize the XCTAssert method with a descriptive message to understand why the assert fails.
  4. Write test cases for both positive and negative scenarios to ensure proper handling of all scenarios.
  5. Check code coverage and get the report of the test directly in Xcode, First, go to Edit Schema > Test > Test Plan > Configuration> Enable Code Coverage for a specific Target, now go to Report Navigator Command + 9. Click on the tests and then go to coverage.
  6. Ensure that each test method remains independent of the others.
  7. Utilize the Given-When-Then (or Arrange-Act-Assert) format for organizing test cases effectively.
  8. Use Command + U to run all test cases andCommand + Option + Control + G to run the latest test.

Conclusion

Hope you found this article useful and helps you to start your Unit — UI testing journey. Try it yourself, and explore more. There are various XCTAssert methods you can use as per the requirements.

Thanks for reading.
Happy coding! 👩‍💻 😇

For more updates on the latest tools and technologies, follow the Simform Engineering blog.

Follow Us: Twitter | LinkedIn

--

--