Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UltronTest & Soft assertions #97

Merged
merged 6 commits into from
Nov 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions composeApp/src/commonTest/kotlin/UltronTestFlowTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@

import com.atiurin.ultron.annotations.ExperimentalUltronApi
import com.atiurin.ultron.core.test.UltronTest
import com.atiurin.ultron.log.UltronLog
import kotlin.test.Test
import kotlin.test.assertTrue

class UltronTestFlowTest : UltronTest() {
companion object {
var order = 0
var beforeAllTestCounter = -1
var commonBeforeOrder = -1
var commonAfterOrder = -1
var afterOrder = -1

}

@OptIn(ExperimentalUltronApi::class)
override val beforeFirstTest = {
beforeAllTestCounter = order
UltronLog.info("Before Class")
}

override val beforeTest = {
commonBeforeOrder = order
order++
UltronLog.info("Before test common")
}
override val afterTest = {
commonAfterOrder = order
order++
assertTrue(afterOrder < commonAfterOrder, message = "CommonAfter block should run after 'after' test block")
UltronLog.info("After test common")
}

@Test
fun someTest1() = test {
var beforeOrder = -1
var goOrder = -1
order++
before {
beforeOrder = order
order++
UltronLog.info("Before TestMethod 1")
}.go {
goOrder = order
order++
UltronLog.info("Run TestMethod 1")
}.after {
afterOrder = order
order++
assertTrue(beforeAllTestCounter == 0, message = "beforeAllTests block should run before all test")
assertTrue(beforeAllTestCounter < commonBeforeOrder, message = "beforeAllTests block should run before commonBefore block")
assertTrue(commonBeforeOrder < beforeOrder, message = "beforeOrder block should run after commonBefore block")
assertTrue(beforeOrder < goOrder, message = "Before block should run before 'go'")
assertTrue(goOrder < afterOrder, message = "After block should run after 'go'")
}
}

@Test
fun someTest2() = test(suppressCommonBefore = true) {
before {
UltronLog.info("Before TestMethod 2")
}.after {
UltronLog.info("After TestMethod 2")
}.go {
assertTrue(beforeAllTestCounter == 0, message = "beforeAllTests block should run only once")
UltronLog.info("Run TestMethod 2")
}
}

@Test
fun simpleTest() = test {
assertTrue(beforeAllTestCounter == 0, message = "beforeAllTests block should run only once")
UltronLog.info("UltronTest simpleTest")
}
}
52 changes: 52 additions & 0 deletions composeApp/src/commonTest/kotlin/UltronTestFlowTest2.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import com.atiurin.ultron.annotations.ExperimentalUltronApi
import com.atiurin.ultron.core.test.UltronTest
import com.atiurin.ultron.log.UltronLog
import kotlin.test.Test
import kotlin.test.assertTrue

class UltronTestFlowTest2 : UltronTest() {
var order = 0
var beforeAllTestCounter = 0

@OptIn(ExperimentalUltronApi::class)
override val beforeFirstTest = {
beforeAllTestCounter = order
order++
UltronLog.info("Before Class")
}

@Test
fun someTest1() = test {
var beforeOrder = -1
var afterOrder = -1
var goOrder = -1
order++
before {
beforeOrder = order
order++
UltronLog.info("Before TestMethod 1")
}.go {
goOrder = order
order++
UltronLog.info("Run TestMethod 1")
}.after {
afterOrder = order
assertTrue(beforeAllTestCounter == 0, message = "beforeAllTests block should run before all test")
assertTrue(beforeOrder > beforeAllTestCounter, message = "Before block should run after 'Before All'")
assertTrue(beforeOrder < goOrder, message = "Before block should run before 'go'")
assertTrue(goOrder < afterOrder, message = "After block should run after 'go'")
}
}

@Test
fun someTest2() = test(suppressCommonBefore = true) {
before {
UltronLog.info("Before TestMethod 2")
}.after {
UltronLog.info("After TestMethod 2")
}.go {
assertTrue(beforeAllTestCounter == 0, message = "beforeAllTests block should run only once")
UltronLog.info("Run TestMethod 2")
}
}
}
2 changes: 1 addition & 1 deletion docs/docs/common/boolean.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 4
sidebar_position: 5
---

# Boolean result
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/common/extension.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 1
sidebar_position: 3
---

# Ultron Extension
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/common/listeners.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 4
---

# Listeners
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/common/resulthandler.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 5
sidebar_position: 7
---

# Result handler
Expand Down
174 changes: 174 additions & 0 deletions docs/docs/common/ultrontest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
---
sidebar_position: 2
---

# UltronTest

`UltronTest` is a powerful base class provided by the Ultron framework that enables the definition of common preconditions and postconditions for tests. By extending this class, you can streamline test setup and teardown, ensuring consistent execution across your test suite.

## Features of `UltronTest`

- **Pre-Test Actions:** Define actions to be executed before each test.
- **Post-Test Actions:** Define actions to be executed after each test.
- **Lifecycle Management:** Execute code once before all tests in a class using `beforeFirstTest`.
- **Customizable Test Execution:** Suppress pre-test or post-test actions when needed.

### Example

Here is an example of using `UltronTest`:

```kotlin
class SampleUltronFlowTest : UltronTest() {

@OptIn(ExperimentalUltronApi::class)
override val beforeFirstTest = {
UltronLog.info("Before Class")
}

override val beforeTest = {
UltronLog.info("Before test common")
}

override val afterTest = {
UltronLog.info("After test common")
}

/**
* The order of method execution is as follows::
* beforeFirstTest, beforeTest, before, go, after, afterTest
*/
@Test
fun someTest1() = test {
before {
UltronLog.info("Before TestMethod 1")
}.go {
UltronLog.info("Run TestMethod 1")
}.after {
UltronLog.info("After TestMethod 1")
}
}

/**
* An order of methods execution is follow: before, go, after
* `beforeFirstTest` - Not executed, as it is only run once and was already executed before `someTest1`.
* `beforeTest` - Not executed because it was suppressed using `suppressCommonBefore`.
* `afterTest` - Not executed because it was suppressed using `suppressCommonAfter`.
*/
@Test
fun someTest2() = test(
suppressCommonBefore = true,
suppressCommonAfter = true
) {
before {
UltronLog.info("Before TestMethod 2")
}.go {
UltronLog.info("Run TestMethod 2")
}.after {
UltronLog.info("After TestMethod 2")
}
}

/**
* An order of methods execution is follow: beforeTest, test, afterTest
* `beforeFirstTest` - Not executed, since it was executed before `someTest1`
*/
@Test
fun someTest3() = test {
UltronLog.info("UltronTest simpleTest")
}
}
```

### Key Methods

- **`beforeFirstTest`**: Code executed once before all tests in a class.
- **`beforeTest`**: Code executed before each test.
- **`afterTest`**: Code executed after each test.
- **`test`**: Executes a test with options to suppress pre-test or post-test actions.

### Key Features of the `test` Method

- **Test Context Recreation:**
The `test` method automatically recreates the `UltronTestContext` for each test execution, ensuring a clean and isolated state for the test context.

- **Soft Assertion Reset:**
Any exceptions captured during `softAssertions` in the previous test are cleared at the start of each new `test` execution, maintaining a clean state.

- **Lifecycle Management:**
It invokes `beforeTest` and `afterTest` methods around your test logic unless explicitly suppressed.
---

### Purpose of `before`, `go`, and `after`
- **`before`:** Defines preconditions or setup actions that must be performed before the main test logic is executed.
These actions might include preparing data, navigating to a specific screen, or setting up the environment.
```kotlin
before {
UltronLog.info("Setting up preconditions for TestMethod 2")
}
```

- **`go`:** Encapsulates the core logic or actions of the test. This is where the actual operations being tested are performed, such as interacting with UI elements or executing specific functionality.
```kotlin
go {
UltronLog.info("Executing the main logic of TestMethod 2")
}
```

- **`after`:** Block is used for postconditions or cleanup actions that need to occur after the main test logic has executed. This might include verifying results, resetting the environment, or clearing resources.
```kotlin
after {
UltronLog.info("Cleaning up after TestMethod 2")
}
```

These methods help clearly separate test phases, making tests easier to read and maintain.

## Using `softAssertion` for Flexible Error Handling

The `softAssertion` mechanism in Ultron allows tests to catch and verify multiple exceptions during their execution without failing immediately. This feature is particularly useful for validating multiple conditions within a single test.
### Example of `softAssertion`

```kotlin
class SampleTest : UltronTest() {
@Test
fun softAssertionTest() {
softAssertion(failOnException = false) {
hasText("NotExistText").withTimeout(100).assertIsDisplayed()
hasTestTag("NotExistTestTag").withTimeout(100).assertHasClickAction()
}
verifySoftAssertions()
}
}
```

The `softAssertion` mechanism does not inherently depend on `UltronTest`.
You can use `softAssertion` independently of the `UltronTest` base class. However, in such cases, you must manually clear exceptions between tests to ensure they do not persist across test executions.
```kotlin
class SampleTest {
@Test
fun softAssertionTest() {
UltronCommonConfig.testContext.softAnalyzer.clear()
softAssertion() {
//assert smth
}
}
}
```

### Explanation

- **Fail on Exception:** By default (`failOnException = true`), `softAssertion` will throw an exception after completing all operations within its block if any failures occur.
- **Manual Verification:** If `failOnException` is set to `false`, you can explicitly verify all caught exceptions at the end of the test using `verifySoftAssertions()`.

This approach ensures granular control over how exceptions are handled and reported, making it easier to analyze and debug test failures.

---

## Benefits of `UltronTest` usage

- Simplifies test setup and teardown with consistent preconditions and postconditions.
- Enhances error handling by allowing multiple assertions within a single test.
- Improves test readability and maintainability.

By leveraging `UltronTest` and `softAssertion`, you can build robust and flexible UI tests for your applications.

10 changes: 0 additions & 10 deletions docs/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import com.atiurin.sampleapp.managers.AccountManager
import com.atiurin.ultron.allure.config.UltronAllureConfig
import com.atiurin.ultron.core.compose.config.UltronComposeConfig
import com.atiurin.ultron.core.config.UltronConfig
import com.atiurin.ultron.core.test.UltronTest
import com.atiurin.ultron.testlifecycle.rulesequence.RuleSequence
import com.atiurin.ultron.testlifecycle.setupteardown.SetUpRule
import org.junit.BeforeClass
import org.junit.Rule

abstract class BaseTest {
abstract class BaseTest : UltronTest(){
val setupRule = SetUpRule("Login user rule")
.add(name = "Login valid user $CURRENT_USER") {
AccountManager(InstrumentationRegistry.getInstrumentation().targetContext).login(
Expand Down
Loading
Loading