XCUITests — Test Listeners

Nishith Shah
Quality Engineering University
3 min readJun 22, 2020

What is Test Listener in XCUITests?

The listener is defined as an interface that modifies the default Test’s behavior. As the name suggests Listeners “listen” to the event defined and behave accordingly.

In XCUITests, the terminology of listeners is quite different compare to the TestNG testing framework.

In XCUITests, listeners are known as Observers. In Java, we have an interface to implement methods, however, in Swift, we have a protocol to conform methods.

Essentially Swift protocols are very similar to Java interfaces except for
It can be used by conforming to the XCTestObservation protocol.

Now, What is XCTestObservers?

The XCTestObservation protocol defines methods that are called in response to significant events in the progress of test runs. It allows to customize your XCUITests reports and log.

There are many types of notification methods of XCTestObservation are called in the following sequence for a test bundle:

testBundleWillStart(_:) is called exactly once per test bundle. Called immediately before any tests in a test bundle begin.

testSuiteWillStart(_:) is called exactly once per test suite. Called immediately before a test suite starts executing.

testCaseWillStart(_:) is called exactly once per test case. Called immediately before a test case starts executing.

testCase(_:didFailWithDescription:inFile:atLine:) is called zero or more times per test case, at any point between test case start and finish. Called when a test case reports a failure.

testCaseDidFinish(_:) is called exactly once per test case. Called immediately after a test case finishes executing.

testSuite(_:didFailWithDescription:inFile:atLine:) is called zero or more times per test suite, at any point between test suite start and finish. Called when a test suite reports a failure.

testSuiteDidFinish(_:) is called exactly once per test suite. Called immediately after a test suite finishes executing.

testBundleDidFinish(_:) is called exactly once per test bundle. Called immediately after all tests in a test bundle have finished executing.

Progress events are delivered in the following sequence:

testBundleWillStart:
— — — testSuiteWillStart:
— — — — — — testCaseWillStart:
— — — — — — testCase:didFailWithDescription:…
— — — — — — testCaseDidFinish:
— — — testSuite:didFailWithDescription:…
— — — testSuiteDidFinish:
testBundleDidFinish:

How to use?

Let’s create a class same like this,

import XCTestpublic class TestObserver: XCTestObservation {    func testBundleWillStart(_ testBundle: Bundle) {}
func testSuiteWillStart(_ testSuite: XCTestSuite) {}
func testCaseWillStart(_ testCase: XCTestCase) {}
func testCase(_ testCase: XCTestCase, didFailWithDescription description: String, inFile filePath: String?, atLine lineNumber: UInt) {}
func testCaseDidFinish(_ testCase: XCTestCase) {}
func testSuiteDidFinish(_ testSuite: XCTestSuite) {}
func testBundleDidFinish(_ testBundle: Bundle) {}
}

Now, we have to register this class for Test Observer and also have to remove it once Test Bundle Finish.

How to Add TestObserver?

Observers can be any object that conforms to the XCTestObservation protocol. Register new observers with the addTestObserver(_:) method and remove them with the removeTestObserver(_:) method.

If an NSPrincipalClass key is declared in the test bundle’s Info.plist file, XCTest automatically creates a single instance of that class when the test bundle is loaded. You can use this instance as a place to register observers or do other pretesting global setup before testing for that bundle begins.

Important
Observers must be registered manually. The NSPrincipalClass instance is not automatically registered as an observer even if the class conforms to XCTestObservation.

  1. In your test target, find the Info.plist and add a key: Principal class (or NSPrincipalClass) with the class name of your test observer as follows: $(PRODUCT_NAME).TestObserver --Note the ${PRODUCT_NAME} simply provides the name of the test bundle, which is part of the full class name for a Swift class.
  2. Create a class as follow:
class TestObserver: NSObject, XCTestObservation {
// This init is called first thing as the test bundle starts up and before any test
// initialization happens
override init() {
super.init()
// We don't need to do any real work, other than register for callbacks
// when the test suite progresses.
// XCTestObservation keeps a strong reference to observers
XCTestObservationCenter.shared.addTestObserver(self)
}
func testBundleDidFinish(_ testBundle: Bundle) {
XCTestObservationCenter.shared.removeTestObserver(self)
}
}

Can we Add Multiple Observers?

Try By Yourself!!!
Don’t forget to remove all registered test observers.

Conclusion

XCTests Observation provides a very easy way to handle Test Listeners the same as other Testing Framework. However, you need to add your own logic to generate report and logs which are more suitable for you.

What do you think? Do you use Observation to achieve your testing goal? Let me know — along with any questions, comments, or feedback that you might have on https://www.linkedin.com/in/nshthshah.

--

--