Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Continuous Performance Testing of an iOS App Using XCTest

DZone's Guide to

Continuous Performance Testing of an iOS App Using XCTest

Learn how to do performance testing with the XCTest platform to detect performance issues, memory issues, and other bottlenecks in iOS apps.

· Mobile Zone
Free Resource

Download this comprehensive Mobile Testing Reference Guide to help prioritize which mobile devices and OSs to test against, brought to you in partnership with Sauce Labs.

The speed of an iOS app is a key to success in the current competitive market. Users hate slow-running and unresponsive apps so it has become very important to check the performance of an iOS app before it goes to the end users.

Apple announced performance testing support for the XCTest framework at WWDC 2014. XCTest frameworks can measure the performance of the block of code inside the unit or UI tests. In this post, we will see how to perform automated performance testing using the XCTest framework.

Historical Performance Testing in iOS

Apple has various developer tools to check the performance of iOS apps. The most common are Instruments, Leaks, Profiler, and Allocations. These tools can help to detect performance issues, memory issues, and other bottlenecks. Traditionally, an engineer has to babysit and perform all these checks manually over a period of time. This approach is not acceptable in the modern iOS development as a small code change can make an app unresponsive, unstable, and unusable. We can't manually check performance issues all the time. There has to be an automated way to check the performance of an app on regular basis. Fortunately, there are a few ways we can achieve automated performance testing of iOS apps:

  • Running an Instruments check on a periodic basis.
  • Automated performance tests using the XCTest framework.

In this post, we will cover automated performance tests using the XCTest framework as Instrument checks on a regular basis aren't appropriate, as that still involves repeatable tasks.

Performance Testing With XCTest

The XCTest framework has measure blocks which can be applied to any code inside the test method to check the performance of the code.

  • The measure block executes the code multiple times depending on the number of invocations set. The default value is ten, which means the block will run ten times.
  • At the end of the execution, it calculates the average value that can be used as a baseline for future test execution. We can also set the baseline value manually.
  • The performance test will fail if an average time goes higher than the baseline.

Applying Performace Tests at the UI Level

Selecting the candidates for performance testing is the decision of the team, but as per the XCTest documentation, performance tests can be applied to any XCTest regardless the level of the test. It means we can apply measure blocks to unit test as well as UI tests. As per most online articles, performance testing is only applied to unit tests. We can also apply performance tests to the XCUITest so that it can communicate to all the application endpoints. This will also give us the opportunity to explore which service/API is responding faster or slower. Applying performance tests to UI tests has its own pros and cons. XCUITests will cover different areas, but the performance test might become incredibly slow. If we apply performance tests at the unit level, we can get quick results. When and where to apply performance tests is outside the scope of this article, but for this demo, we will apply it for XCUITests.

Performance Tests With Measure Blocks

In order to see how it works in practice, let's create a demo app, XCUITest-Performance, with the UI Test target. Just add a button to the main storyboard with the accessibility identifier "Press" without any IBAction. At this stage, we will have a demo app with a button. In the template XCUITest, create a test for the performance test which has the measure block, and inside the block, we can launch the app and press the button. The typical test looks like this:

func testPerformanceOfApp() {
    self.measure {
     XCUIApplication().launch()
     XCUIApplication().buttons["Press"].tap()
}

We can run the test from Xcode and see that the test will run the code inside the block ten times. When the test runs the first time, it will fail and prompt the developer to set the appropriate baseline value for future test execution. The test will pass or fail according to the result of the average value and baseline value.

Watch this in the GIF below:

Now that we have set the baseline value for our future performance tests, the performance tests will run as our normal unit tests all the time but the test will fail if the average value increases above the baseline. It means we can automatically test the performance of our code along with unit test execution.

Granular Performance Tests With Measure Metrics

In the above test, we have calculated the average time from the launch of the app until a user presses the button. However, we can go granular and set the point from where we have to start and stop the measurement of performance. This means we can apply performance tests to the specific area of the test code within the unit test by using the start/stop method. We can achieve this using the measure Metric method.

Let's apply performance tests only when the button is pressed. We can launch an app in the normal way and then apply the measure Metric method to start and stop measuring the performance. We can add the test like this:

func testPerformanceMeasureMetrics() {
   XCUIApplication().launch()
   self.measureMetrics([XCTPerformanceMetric.wallClockTime], automaticallyStartMeasuring: false) {
      startMeasuring()
      XCUIApplication().buttons["Press"].tap()
      stopMeasuring()

   }
}

Now, if we run this test, we can see that the application is launched only once, but we can calculate the performance by multiple invocations.

The measure metrics method is a great way to optionally set up start and end points for performance measurement during the test execution.

Result Analysis of Performance Tests

The results of the performance tests are represented graphically in Xcode. The typical performance test result will look like this:

The dialogue reports average, baseline, and Max STDDEV values. In the resulting dialogue, we can see that the performance test is 0.779% worse than the previous execution. Those results can be also seen in the console output if we are running the performance tests from a continuous integration server.

Pros and Cons of XCTest as a Performance Test Tool

XCTest is a unit and UI level functional testing tool, however, it is extended to do performance testing of iOS modules. There are some pros and cons of using XCTest as performance testing tool.

Pros

There are certain benefits to developers for using XCTest for performance testing:

  • The XCTest framework is a core Apple framework, so there is no need to add third party dependencies for performance testing.
  • The usual unit tests written using XCTest can be easily extended to performance tests by adding measure blocks around the code snippets. No need to learn an additional performance test tools like Gatling or JMeter, etc.
  • It's easy to write performance tests using Swift. No need to learn additional languages like Scala or Java.
  • Easily pluggable to any continuous integration server as they can be executed using Xcodebuild or Fastlane scan.
  • You have the ability to match the baseline using device types, so no need to worry about comparing the results with valid data sets.
  • You have the ability to use the Xcode IDE for performance testing.

Cons

Along with some benefits, there are certain pitfalls using XCTest for performance testing:

  • The performance test results generated by XCTest aren't helpful for a non-technical person. XCTest performance test reports can not be exported to a readable format.
  • The performance test results can not be shared or hosted easily, unlike Gatling or other performance test tools.
  • As the number of performance tests increases, the test suite becomes slower, so the tests have to be run separately from the development workflow.
  • XCTest can not simulate large number of users to check the performance of an iOS app.
  • XCTest can not alter the hardware device setting like CPU or memory to cover complex behaviors for performance tests.

Tips for Writing Accurate Performance Tests

In Xcode, performance tests aren't treated separately. The XCTest framework allows us to run performance tests along with our unit or UI tests. It becomes the programmer's responsibility to make sure that the test itself is setup correctly and working as expected. There are a few things we can keep in mind while setting up performance tests:

Know What You Are Testing

Before diving into performance testing, we need to understand what and how we are going to test the specific code for performance. It would be a good idea to identify the level at which performance tests can be executed- it might be unit level, integration level, or UI level. We can execute the test at any level as long as the test uses the XCTest framework.

Avoid Conflicts Between Invocations

The performance test executes multiple innovations of the same code block. We have to make sure that each invocation is atomic and independent from another without having a high standard deviation. Anyway, Xcode will report an error if there are any deviations.

Control the Test Data

The predefined test data has a higher chance of producing reliable test results than random data while running a performance test. The random data will produce flaky results, which aren't useful to anyone.

Run Performance Tests Separately in a Different Scheme

As the number of performance tests increases, the time taken by test suites increases significantly. It would be a great idea to configure a separate scheme to run only the performance tests without affecting the normal flow of unit and UI tests.

Run Performance Tests Continuously on a Continuous Integration Server

As we have created a separate scheme for performance tests, it would be fairly easy to configure with any continuous integration server. It would be super easy if you were using the Xcode Server. We can create a separate CI job to run the performance tests on a regular basis, e.g. daily, overnight, etc.

Source Code for This Demo

The source code of the performance test discussed in this article is available on GitHub. You can clone the source code and try to run performance tests by yourself.

Github Repo: XCTest-Performance

Conclusion

Apple has extended the core XCTest framework for performance testing, which can help us to detect the performance bottlenecks in our iOS apps as early as possible in the development cycle. It would be fairly easy for any iOS developer to get started with performance testing without the need to learn a new language or framework. With latest Xcode, it becomes even smoother. Hope you will try this yourself and plug some performance tests into your iOS apps soon!

Analysts agree that a mix of emulators/simulators and real devices are necessary to optimize your mobile app testing - learn more in this white paper, brought to you in partnership with Sauce Labs.

Topics:
xctest ,xcode ,performance testing ,swift ,mobile ,mobile testing ,ios

Published at DZone with permission of Shashikant Jagtap, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}