Hot File

Introduction to Testing on iOS

View: 367    Dowload: 0   Comment: 0   Post by: hanhga  
Author: none   Category: iPhone OS   Fields: Other

9 point/3 review File has been tested

In today’s world, there are many tools available that can be used to automatically test the software you write.


In today’s world, there are many tools available that can be used to automatically test the software you write.

Some of these tools are maintained through an open source model, but there is also a core set provided by Apple. With each new release of the iOS SDK, Apple has continued to show their commitment towards improving the tools available for developers to test the code they write. For the iOS developer who is new to automated testing and interested to get started, Apple’s tools are a good place to start.

This tutorial is going to provide instructions for using a tool that Apple provides for automated testing, XCTest. XCTest is Apple’s unit testing framework. Unit testing is the type of automated testing that verifies code at the lowest level. You write Objective-C code that calls methods from your "production" code and verify that the code under test actually does what it's intended to do. Are variables set correctly? Is the return value correct?

Tests written with the XCTest framework may be repeatedly executed against your application's code to help you gain confidence that you are creating a bug free product in that new code changes aren't breaking existing functionality.

By default, every new Xcode project is created with a good starting point for writing unit tests. This includes three things:

  • a separate target for your tests
  • a group for your test classes
  • an example test

Let's dig into the structure of an iOS unit test. An individual unit test is represented as a single method within any subclass of XCTestCase where the method returnsvoid, takes no parameters, and the method name begins with test.

Luckily, Xcode makes creating test cases easy. With new Xcode projects, an initial test case is created for you in a separate file group whose name is suffixed by the word Tests.

I've created a sample project that can be used as a reference for the examples provided in this tutorial. Download the project from GitHub and open it in Xcode.

In the sample project, you can find the group of tests in the folder named JumblifyTests.

To create your first test case, right click the file group, JumblifyTests, and selectNew File. Choose Test Case Class from the iOS > Source section, and give the new subclass a name.

The typical naming convention is to name the test case such that it is the name of the corresponding class under test, suffixed with Tests. Since we'll be testing the JumblifyViewController class, name the XCTestCasesubclass JumblifyViewControllerTests.

In the brand new XCTestCase subclass, you’ll see four methods. Two of these are tests themselves. Can you identify which they are? Remember that test method names begin with the word "test".

If you didn't figure it out, the test methods created by default are testExample andtestPerformanceExample.

Delete both tests, because we're going to write ours from scratch. The other two methods, setUp and tearDown, are overridden from the superclass, XCTestCase. They are unique in that setUp and tearDown are called before and after each test method is invoked respectively. They are useful places to centralize code that should be executed before or after each test method is called. Tasks like common initialization or cleanup go here.

Import the header file of the JumblifyViewController class and add a property of type JumblifyViewController to the XCTestCase subclass.

In the setUp method, initialize the property as shown below.

    [super setUp];
    self.vcToTest = [[JumblifyViewController alloc] init];

We're now going to write a test to test the reverseString: method of the JumblifyViewController class.

Create a test method that uses the instantiated vcToTest object to test the reverseString: method. In this test method, we create an NSString object and pass it to the view controller's reverseString: method. It's common convention to give your test a meaningful name to make it clear what the test is testing.

 (void)testReverseString {
    NSString *originalString = @"himynameisandy";
    NSString *reversedString = [self.vcToTest reverseString:originalString];

At this point, we haven't done anything useful yet, because we haven't tested the reverseString: method yet. What we need to do is compare the output of the reverseString: method with what we expect the output to be.

The XCTAssertEqualObjects function is part of the XCTest framework. The XCTest framework provides many other methods to make assertions about application state, such as variable equality or boolean expression results. In this case, we have stated that two objects must be equal. If they are, the test passes and if they aren't, the test fails. Take a look at Apple’s documentation for a comprehensive list of assertions provided by the XCTest framework.

 (void)testReverseString {
    NSString *originalString = @"himynameisandy";
    NSString *reversedString = [self.vcToTest reverseString:originalString];
    NSString *expectedReversedString = @"ydnasiemanymih";
    XCTAssertEqualObjects(expectedReversedString, reversedString, @"The reversed string did not match the expected reverse”);

If you try to compile the code at this point, you'll notice a warning when you attempt to call reverseString: from the test case. The reverseString: method is a private method of the JumblifyViewController class. This means that other objects cannot invoke this method since it's not defined in the header file of the JumblifyViewController class.

While writing testable code is a mantra that many developers follow, we don't want to unnecessarily modify our code under test. But how do we call the private reverseString: method of the JumblifyViewController class in our tests? We could add a public definition of the reverseString: method to the header file of the JumblifyViewController class, but that breaks the encapsulation pattern.

One solution is to add a private category on the JumblifyViewController class to expose the reverseString: method. We add this category to the XCTestCasesubclass, which means it's only available in that class. By adding this category, the test case will compile without warnings or errors.

@interface JumblifyViewController (Test)
- (NSString *)reverseString:(NSString *)stringToReverse;

Let's run our tests to ensure that they pass. There are several ways to run unit tests for an iOS application. I'm a keyboard shortcut junkie so my most used technique for running my unit tests for my application is by pressing Command-U. This keyboard shortcut will run all the tests for your application. You can also perform the same action by selecting Test from the Product menu.

As your test suite grows, or if you like implement test driven development, you'll find that running your test suite can become too time consuming. Or it might get in the way of your workflow. An very useful command, buried within Xcode's menu, that I've fallen in love with is Command-Option-Control-U. This shortcut triggers a command that runs the test your cursor is currently in. Once you have your test suite fleshed out and finalized, you should always run the entire test suite. Running an individual test is useful as you're writing a new test test or when you're debugging a failing test.

The command to run one test is complemented by Command-Option-Control-G, which reruns the last test run. This can be the entire test suite or only the most recent test you are working on. It's also useful in case you've navigated away from whatever test you're working on and you're still in the process of debugging it.

You can see your test results in a couple of places. One of those places is the Test Navigator on the right.

Another option is by looking at the gutter of the Source Editor.

In either of these two places, clicking the green diamond with the white checkmark will rerun that particular test. In the case of a failed test, you'll see a red diamond with a white cross in the center. Clicking it will also rerun that particular test.

Xcode 6 introduced two new exciting additions to unit testing on iOS and OS X, testing asynchronous functionality and measuring performance of a specific piece of code.

Prior to Xcode 6, there was no good way to unit test asynchronous code. If your unit test called a method that contained asynchronous logic, you couldn't verify the asynchronous logic. The test would complete before the asynchronous logic in the method under test was executed.

To test asynchronous code, Apple has introduced an API that allows developers to define an expectation that must be fulfilled for the test to complete successfully. The flow is as follows, define an expectation, wait for the expectation to be fulfilled, and fulfill the expectation when the asynchronous code has finished executing. Take a look at the below example for clarification.

- (void)testDoSomethingThatTakesSomeTime {
    XCTestExpectation *completionExpectation = [self expectationWithDescription:@"Long method"];
    [self.vcToTest doSomethingThatTakesSomeTimesWithCompletionBlock:^(NSString *result) {
        XCTAssertEqualObjects(@"result", result, @"Result was not correct!");
        [completionExpectation fulfill];
    [self waitForExpectationsWithTimeout:5.0 handler:nil];

In this example, we’re testing the doSomethingThatTakesSomeTimesWithCompletionBlock method. We want to hinge success or failure of our test on the value that is returned in the completion block called by the method under test.

To do this, we define an expectation at the start of the test method. At the end of the test method, we wait for the expectation to be fulfilled. As you can see, we can also pass in a timeout parameter.

The actual assertion of the test is made inside the completion block of the method under test in which we also fulfill the expectation we defined earlier. As a result, when the test is run, the test waits until the expectation is fulfilled or it fail if the timeout expires and the expectation isn't fulfilled.

Another addition to unit testing in Xcode 6 is the ability to measure the performance of a piece of code. This allows developers to gain insight into the specific timing information of the code that's being tested.

With performance testing, you can answer the question "What is the average time of execution for this piece of code?" If there is a section that is especially sensitive to changes in terms of the time it takes to execute, then you can use performance testing to measure the amount of time it takes to execute.

You can also define a baseline execution time. This means that if the code that's being tested significantly deviates from that baseline, the test fails. Xcode will repeatedly execute the code that's being tested and measure its execution time. To measure the performance of a piece of code, use the measureBlock: API as shown below.

 (void)testPerformanceReverseString {
    NSString *originalString = @"himynameisandy";
    [self measureBlock:^{
        [self.vcToTest reverseString:originalString];

Click the informational message that appears.

Set or edit the baseline time of execution for the performance test.


Introduction to Testing on iOS

Introduction to Testing on iOS Posted on 18-01-2016  In today’s world, there are many tools available that can be used to automatically test the software you write. 3/10 367


To comment you must be logged in members.

Files with category

Newsletter Email

File suggestion for you

File top downloads

logo codetitle - library source code to share, download the file to the community
Copyright © 2015. All rights reserved. Develope by Vinagon .Ltd