Skip to content

Listeners

Aleksei Tiurin edited this page Aug 20, 2023 · 4 revisions

The framework has 2 types of listeners: UltronLifecycleListener & UltronRunListener

UltronLifecycleListener

This one allows you to listen all stages of Operation execution.

abstract class UltronLifecycleListener {
    /**
     * executed before any action or assertion
     */
    override fun before(operation: Operation) = Unit

    /**
     * called when action or assertion failed
     */
    override fun afterFailure(operationResult: OperationResult<Operation>) = Unit
    /**
     * called when action or assertion has been executed successfully
     */
    override fun afterSuccess(operationResult: OperationResult<Operation>) = Unit
    /**
     * called in any case of action or assertion result
     */
    override fun after(operationResult: OperationResult<Operation>) = Unit    
}

Operation object contains all info about operation (name, description, type, timeout)

OperationResult object contains all info about operation result (success, all exceptions that occured and exception that was thrown, description etc) and also has a reference to Operation.

All listener methods will be executed before an exception will be thrown. It gives you a guarantee that all exceptions in your tests will be processed as you want.

Log operation example

For instance, here is a listener that logs everything to Ultron log.

class LogLifecycleListener : UltronLifecycleListener() {
    override fun before(operation: Operation) {
        UltronLog.info("Start execution of ${operation.name}")
    }

    override fun afterSuccess(operationResult: OperationResult<Operation>) {
        UltronLog.info("Successfully executed ${operationResult.operation.name}")
    }

    override fun afterFailure(operationResult: OperationResult<Operation>) {
        UltronLog.error("Failed ${operationResult.operation.name} with description: \n" +
                "${operationResult.description} ")
    }
}

You can create you own custom listener in the same way.

class CustomLifecycleListener : UltronLifecycleListener() {...}

Add new listener for Espresso, EspressoWeb and UI Automator Ultron operations using UltronConfig.addGlobalListener(). For Compose use another one - UltronComposeConfig.addListener()

abstract class BaseTest {
    companion object {
        @BeforeClass @JvmStatic
        fun configureUltron() {
            UltronConfig.addGlobalListener(CustomLifecycleListener())
            UltronComposeConfig.addListener(CustomLifecycleListener())
        }
    }
}

Configuration

Basically we already know how to add new listener. But there are other options to configure Ultron listeners.

First of all Ultron by default already has LogLifecycleListener that writes some usable info to logcat.

Lifecycles

Ultron has 4 different lifecycles that watch for different operations.

  • UltronEspressoOperationLifecycle
  • UltronWebLifecycle (WebView operations)
  • UltronUiAutomatorLifecycle
  • UltronComposeOperationLifecycle

It is possible to add listener for any of these lifecycles.

UltronUiAutomatorLifecycle.addListener(CustomLifecycleListener())

In this case CustomLifecycleListener will be applied only for UI Automator operations.

Global listeners

It possible to add or delete global listener. The changes will be applied for Espresso, EspressoWeb and UI Automator lifecycles.

abstract class BaseTest {
    companion object {
        @BeforeClass @JvmStatic
        fun configureUltron() {
            UltronConfig.addGlobalListener(CustomLifecycleListener())
            UltronConfig.removeGlobalListener(CustomLifecycleListener::class.java)
        }
    }
}

Exclude operation from listeners monitor

Ultron allows it to exclude operation from all listeners. This option is based on operation type.

For example, you've created a new operation

enum class CustomUltronOperations : UltronOperationType {
   ASSERT_HAS_ANY_CHILD
}
fun UltronUiObject2.assertHasAnyChild() = apply {
    executeAssertion(
            assertionBlock = { uiObject2ProviderBlock()!!.childCount > 0 },
            name = "Assert $selectorDesc has any child",
            type = CustomUltronOperations.ASSERT_HAS_ANY_CHILD,
            description = "UiObject2 assertion '${CustomUltronOperations.ASSERT_HAS_ANY_CHILD}' of $selectorDesc during $timeoutMs ms",
            timeoutMs = timeoutMs,
            resultHandler = resultHandler
    )
}

And you would like to exclude it from listeners for any reason no matter why.

Add single line to Ultron configuration function.

abstract class BaseTest {
    companion object {
        @BeforeClass @JvmStatic
        fun configureUltron() {
            ... 
            UltronConfig.operationsExcludedFromListeners.add(CustomUltronOperations.ASSERT_HAS_ANY_CHILD)
        }
    }
}

UltronRunListener

Allows you to add listener for Test Lifecycle. See RunListener.

It is available in case you use ultron-allure and set testInstrumentationRunner.

testInstrumentationRunner = "com.atiurin.ultron.allure.UltronAllureTestRunner"

It could be used, for instance, to attach your custom application log to Allure Report.

class AppLogAttachRunListener() : UltronRunListener() {
    override fun testFailure(failure: Failure) {
        val logFile: File = AppLogProvider.provide()
        val fileName = AttachUtil.attachFile(
            name = "app_log_file",
            file = logFile,
            mimeType = MimeType.PLAIN_TEXT
        )
    }
}

Add custom RunListener to Allure config.

@BeforeClass @JvmStatic
fun configureUltron() {
    ...
    UltronAllureConfig.addRunListener(AppLogAttachRunListener())
}