> For the complete documentation index, see [llms.txt](https://docs.konsist.lemonappdev.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.konsist.lemonappdev.com/advanced/dynamic-konsist-tests.md).

# Dynamic Konsist Tests

From static to dynamic

On this page, we explore the domain of static tests and then progress to the flexible world of dynamic tests. As a starting point, let's dive into the traditional approach of static Konsist tests.

## Why Use Dynamic Tests?

With static tests, the failure is represented by a single test:

<figure><img src="https://3782930697-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FRYeSMx6WDKivnwWx7PdP%2Fuploads%2FQzLafIcOwtxHBXlFwyWE%2Fimage.png?alt=media&amp;token=9d2f8936-63d6-407a-aa65-0ab8f03229f9" alt=""><figcaption></figcaption></figure>

From this failure, a developer discerns the breached rule and needs to dive into the test logs to determine the cause of the violation (to pinpoint the use case breaking the given rule).

In contrast, dynamic tests immediately highlight the root issue since every use case is represented by its own distinct test:

<figure><img src="https://3782930697-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FRYeSMx6WDKivnwWx7PdP%2Fuploads%2FJoWolHSkAcGMSiZYUYrs%2Fimage.png?alt=media&amp;token=0d4db855-f2b8-487e-a7d4-708fe59ab05f" alt=""><figcaption></figcaption></figure>

Utilizing dynamic tests over static ones makes it simpler to pinpoint failures. Consequently, it reduces the time and effort spent on parsing long error logs, offering a more efficient testing experience.

{% hint style="info" %}
Take a look at [sample projects](https://github.com/LemonAppDev/konsist/tree/develop/samples/starter-projects). Every Gradle-based [JUnit5](https://junit.org/junit5/) and [Kotest](https://kotest.io/) project has an additional dynamic test (`SampleDynamicKonsistTest`) preconfigured. Check out the project and run the test.
{% endhint %}

Let's begin by creating a static test and then delve into the steps to transition towards dynamic tests.

## Static Tests

Static tests are defined at compile-time. This means the structure and number of these tests are fixed when the code is compiled. When navigating the universe of Konsist tests, the standard approach is to execute several validations all bundled within a single test.

To paint a clearer picture: imagine you have a rule (let's represent it with the tool icon 🛠️) ensuring that all use cases should be placed in a specific package. One static test (represented by the check icon ✅) can guard this rule, making sure that everything is in the right place:

```mermaid
%%{init: {'theme':'forest'}}%%
flowchart LR
    S1["🛠️ RULE use case package"]-->S3
    S3["✅ TEST Verify use case package (All use cases)"]
```

In most projects, the intricacy arises from a multitude of classes/interfaces, each with distinct duties. However, to simplify our understanding, let's use a straightforward and simplified example of a project with just three use cases:

<figure><img src="https://3782930697-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FRYeSMx6WDKivnwWx7PdP%2Fuploads%2Fz9dCKpOVXyKw633407BN%2Fimage.png?alt=media&amp;token=d527d882-8072-4f7d-af24-ddb077ebffe2" alt=""><figcaption></figcaption></figure>

The goal is to verify if every use case follows these two rules:

* verify if every use case has a test
* verify if every use case is in `domain.usecase` package

A typical approach would be to write two Konsist tests:

{% tabs %}
{% tab title="JUnit" %}

```kotlin
class UseCaseKonsistTest {
    @Test
    fun `use case should have test`() {
        Konsist
            .scopeFromProject()
            .classes()
            .withNameEndingWith("UseCase")
            .assertTrue { it.hasTestClasses() }
    }

    @Test
    fun `use case should reside in domain dot usecase package`() {
        Konsist
            .scopeFromProject()
            .classes()
            .withNameEndingWith("UseCase")
            .assertTrue { it.resideInPackage("..domain.usecase..") }
    }
}
```

{% endtab %}

{% tab title="Kotest" %}

```kotlin
class UseCaseKonsistTest : FreeSpec({
    val useCases = Konsist
        .scopeFromProject()
        .classes()
        .withNameEndingWith("UseCase")

    "use case should have test" {
        useCases.assertTrue(testName = this.testCase.name.name) { it.hasTestClasses() }
    }

    "use case should reside in ..domain.usecase.. package" {
        useCases.assertTrue(testName = this.testCase.name.name) { it.resideInPackage("..domain.usecase..") }
    }
})
```

{% endtab %}
{% endtabs %}

Each rule is represented as a separate test verifying all of the use cases:

```mermaid
%%{init: {'theme':'forest'}}%%
flowchart LR
    S1["🛠️ RULE use case package"]-->S3
    S2["🛠️ RULE Verify use case has test"]-->S4
    S3["✅ TEST Verify use case package (All use cases)"]
    S4["✅ TEST Verify use case has test (All use cases)"]
```

Executing these tests will generate output in the IDE:

<figure><img src="https://3782930697-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FRYeSMx6WDKivnwWx7PdP%2Fuploads%2F0dbQgUWQX4XJFB35AYc2%2Fimage.png?alt=media&amp;token=dd4092f1-cd98-486a-b3c7-74ba512b1ae2" alt=""><figcaption></figcaption></figure>

While the current setup using static, predefined tests is functional, dynamic tests offer an avenue for improved development experience and flexibility.

## Dynamic Tests

Dynamic tests are generated at runtime based on conditions and input data. In this scenario, the dynamic input data is the list of use cases that grows over the project life cycle.

The objective is to generate dynamic tests for each combination of rule and use case (`KoClassDeclaration`) verified by Konsist. With three use cases and two rules for each, this will yield a total of six separate tests:

```mermaid
%%{init: {'theme':'forest'}}%%
flowchart LR
    D1["🛠️ RULE Verify use case package"]
    D1 --> D1T1
    D1 --> D2T1
    D1 --> D3T1

    D2["🛠️ RULE Verify use case has test"]
    D2 --> D1T2
    D2 --> D2T2
    D2 --> D3T2

    D1T1["✅ TEST Verify use case package (CategorizeGroceryItemsUseCase)"]
    D1T2["✅ TEST Verify use case has test (CategorizeGroceryItemsUseCase)"]

    D2T1["✅ TEST Verify use case package (AdjustCaloricGoalUseCase)"]
    D2T2["✅ TEST Verify use case has test (AdjustCaloricGoalUseCase)"]

    D3T1["✅ TEST Verify use case package (CalculateDailyIntakeUseCase)"]
    D3T2["✅ TEST Verify use case has test (CalculateDailyIntakeUseCase)"]
 
 
```

Let's convert this idea into a dynamic test:

{% tabs %}
{% tab title="JUnit 5" %}
JUnit provides built-in support for dynamic tests through its core framework. This ensures that developers can seamlessly employ dynamic testing capabilities.

{% hint style="info" %}
The `org.junit.jupiter:junit-jupiter-api:x.y.z` dependency is required to enable JUnit 5 dynamic tests.
{% endhint %}

```kotlin
class UseCaseKonsistTest {
    @TestFactory
    fun `use case test`(): Stream<DynamicTest> = Konsist
        .scopeFromProject()
        .classes()
        .withNameEndingWith("UseCase")
        .stream()
        .flatMap { useCase ->
            Stream.of(
                dynamicTest("${useCase.name} should have test") {
                    useCase.assertTrue(testName = "${useCase.name} should have test") {
                        it.hasTestClasses()
                    }
                },
                dynamicTest("${useCase.name} should reside in ..domain.usecase.. package") {
                    useCase.assertTrue(testName = "${useCase.name} should reside in ..domain.usecase.. package") {
                        it.resideInPackage("..domain.usecase..")
                    }
                },
            )
        }
}
```

The IDE will display the tests as follows:

<figure><img src="https://3782930697-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FRYeSMx6WDKivnwWx7PdP%2Fuploads%2FO1QGIHy1jZmelFt9rn9a%2Fimage.png?alt=media&amp;token=927c09a1-5469-48b9-984f-b0c7fa191ea3" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
For dynamic tests such as JUnit 5, it is recommended that the test name is explicitly provided using `testName` argument (see [Explicit Test Names](/advanced/dynamic-konsist-tests/explicit-test-names.md)). At the moment test names are duplicated. This aspect has to be further investigated.
{% endhint %}
{% endtab %}

{% tab title="Kotest" %}
Kotest natively supports dynamic tests. Developers can effortlessly integrate and utilize dynamic testing features without needing additional configurations or plugins.

```kotlin
class UseCaseKonsistTest : FreeSpec({
    Konsist
        .scopeFromProject()
        .classes()
        .withNameEndingWith("UseCase")
        .forEach { useCase ->
            "${useCase.name} should have test" {
                useCase.assertTrue(testName = this.testCase.name.name) { it.hasTestClasses() }
            }
            "${useCase.name} should reside in ..domain.usecase.. package" {
                useCase.assertTrue(testName = this.testCase.name.name) { it.resideInPackage("..domain.usecase..") }
            }
        }
})
```

The IDE will display the tests as follows:

<figure><img src="https://3782930697-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FRYeSMx6WDKivnwWx7PdP%2Fuploads%2FqZeeXVxnbcjfKKJGdiRo%2Fimage.png?alt=media&amp;token=024972ad-f834-4d87-a3f0-8ec950f86265" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
For dynamic tests such as Kotest, it is recommended that the test name is explicitly provided using `testName` argument (see [Explicit Test Names](/advanced/dynamic-konsist-tests/explicit-test-names.md)).
{% endhint %}
{% endtab %}

{% tab title="JUnit 4" %}
In JUnit 4, the concept of dynamic tests (like JUnit 5's `@TestFactory`) does not exist natively, thus dynamic tests are not supported.
{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.konsist.lemonappdev.com/advanced/dynamic-konsist-tests.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
