XCTest is the default unit testing framework in Xcode and is different than XCUITest, which is used for automation testing. Unit testing is done by developers mostly in enterprise-grade projects. Most large projects are divided into smaller parts and given to individual developers. These smaller parts are called stories in agile development. Now, when the developer completes their story and a feature, they also have to write unit tests for that story.
The testers are then responsible for doing manual testing of the whole app to check everything is working as desired. But nowadays automation testing is preferred, as it saves time and effort. Again, Apple has its own framework to do automation testing called XCUITest. Check out this post for Mmore on the difference between XCTest and XCUITest.
In this post, we're first going to learn a little about XCTest. (To learn about this testing framework more in-depth, our earlier post on XCTest is a great starting point.) We'll follow this by checking some complex XCTest cases example and also learn about debugging a test.
What Is XCTest?
XCTest is a built-in unit testing framework in Xcode, which is the default IDE (integrated development environment) for Mac. The code for both is written in the Swift language. As the developer creating the code for the story has already created the code in Swift, they don't need to learn a new language in order to test it.
Apple released XCTest at the WWDC 2016 conference, along with XCUITest. Since it is tightly integrated with the Apple ecosystem, it's very easy to get started with no installation required.
The Setup
We'll start with writing XCTest cases in the Playground, as we did in our earlier post on Swift String. So first, open Xcode and then click on File -> Playground.
In the pop-up, select iOS and then Blank. And then press the Next button.
Next, we need to give the Playground a name, which is UsefulXCTests in our case.
Finally, we'll get to the Playground where we'll start writing our XCTest cases.
Basic Setup With XCTest
We require some more boilerplate code to use XCTest in the Playground, as we did in Swift Struct. Create a file called TestRunner.swift in the Sources folder, and add the below code in it. Here, we need to write a structure called TestRunner. Inside it, the code tells us that it will run all test suites.
Next, in the UsefulXCTests.playground root file, we'll create a class called UsefulXCTests. Then, inside a function of testShouldPass(), we'll create a variable of emojiStr. We're using the XCTAssertEqual() to check if the count is 6.
Next, with TestRunner from the previous part, we'll call the runTests() function in it. Here we're passing UsefulXCTests.
The boilerplate code for PlaygroundTestObserver is required. After this, click on the play button, and it will show the test case to be successful in the console.
Testing Asynchronous Functions With XCTest
As mentioned earlier, we'll do some fairly complex tests in this post. So now let's look at how to test asynchronous functions. Asynchronous functions are the backbone of the internet because most web apps or mobile apps do API calls. These API endpoints are connected to the database and get data from it or put data in it. The API calls generally take one to two seconds because the data is located on another server. For this time, the program doesn't stop and execute other code after it.
First, let's update our UsefulXCTests.playground file. Here, we'll add a function called msgAfterDelay in which we're expecting a message as a String, time as Double, and a callback function. This function calls the callback function after the time delay.
To test the function msgAfterDelay, we have a testdelay function inside our class UsefulXCTests. Here, we'll need to first create an exception, which is exp in our case. After that, we'll call the msgAfterDelay function with a message of theMessage and a time of 1 second. The callback function called callbackFn will pass the message.
In this message, we'll check with the XCTAssertEqual function. We need to complete the exception with the fulfill() function. Also notice that we need a waitForExpectations method with a timeout. This timeout should be greater than the timeout given in the msgAfterDelay function.
We also have a variety of exceptions where we use the XCTestExpectation(). In this, the exp is an array, which means we can check more exceptions. So here, the waitForExpectations method is also changed to the wait method. We'll then need to pass the array of exp in for the parameter.
Also notice that we're checking the emojiStr here again. This tells us that the asynchronous code is tested, along with other simple synchronous code.
Debugging Test Setup
Sometimes the test cases don't run as expected, and we get errors that we don't understand. In those cases, we need to debug a test. The debugging feature is available in Xcode projects, so we'll use the Kingfisher app as our starting point.
Next, we'll add a variable called color with the value purple in the existing KingFisherTest.swift file. We've also added a simple basicColor function, which has a switch statement matching the color. Next, we'll use an XCTAssertEqual function to check if the color is red.
Now we need to include this new function in BasicTests.swift for it to run:
When we run the test, it fails, which was intentional.
Debugging the Test
Suppose in our above test, we don't know why it failed. To learn more, we'll go to the debug tab by clicking on it in the top-left menu. Next, click on the + sign in the bottom-left corner. And then click Test Failure Breakpoint.
This will show that a breakpoint has been created with the name Test Failure. This is a special breakpoint that monitors our test cases for any exceptions when they run.
Now, when we run the test case again, it doesn't throw any error. But it shows the exact reason and the place where there is an issue.
Using this, we can understand the source of the error and add a normal breakpoint with a print statement for the color variable.
Now, when we run the test case again, we'll see the color variable is wrong in the console.
We can then rectify this, and our test case runs successfully.
What We've Learned
In this post, we've talked about unit test cases and how to perform them in Xcode using XCTest. We also learned to do complex XCTest cases example for asynchronous functions using XCTestExpectation in the Playground. And after that, we learned to debug a failing test for an app in Xcode.
But testing the network calls using XCTestExpectation is still a difficult task. Instead of using this or other code, we can use Waldo to perform such tests. You're only required to provide the APK or IPA file. Waldo automatically generates test cases and emails you.
Automated E2E tests for your mobile app
Get true E2E testing in minutes, not months.