> ## Documentation Index
> Fetch the complete documentation index at: https://docs.varianz.io/llms.txt
> Use this file to discover all available pages before exploring further.

# JUnit 5 and Kotest

> Test JVM services with the VarianzExtension: per-test sessions, stage insertion, sync, and sample assertions in JUnit 5 or Kotest.

The JUnit 5 integration ships as `io.varianz:varianz-junit` — added to `testImplementation` automatically by the Gradle plugin (`includeJunit`, default `true`), or explicitly with Maven ([setup](/sdks/java#maven)).

```java theme={null}
import io.varianz.junit.VarianzExtension;
import io.varianz.junit.VarianzSession;
import static io.varianz.cel.Cel.cel;

@ExtendWith(VarianzExtension.class)
class PaymentFlowTest {

    @Test
    void chargeReturnsDeterministicTxnId(VarianzSession session) throws Exception {
        session.insert(cel("payment/charge",
            "ChargeResult { txnId: \"test-txn-001\" }"));

        session.awaitSyncOrFail();

        // Trigger the service with the session ID in gRPC metadata
        Metadata headers = new Metadata();
        headers.put(Metadata.Key.of("x-varianz-id", Metadata.ASCII_STRING_MARSHALLER),
                    session.sessionId());
        ChargeResponse resp = blockingStub
            .withInterceptors(MetadataUtils.newAttachHeadersInterceptor(headers))
            .charge(chargeRequest);

        assertThat(resp.getTransactionId()).isEqualTo("test-txn-001");

        session.assertSample("charge-result")
            .within(Duration.ofSeconds(10))
            .hasValue("txnId", "test-txn-001");
    }
}
```

**Endpoint resolution**, in order: explicit configuration → system property `-Dvarianz.endpoint` → `VARIANZ_ENDPOINT` → default `http://127.0.0.1:50051`. For a plaintext local registry the JVM also needs `VARIANZ_INSECURE_ALLOW_PLAINTEXT=true` (see [Configuration](/reference/configuration)).

## Session API

| Method                                             | Returns             | Description                                                                                                           |
| -------------------------------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `sessionId()`                                      | `String`            | 32-char hex session ID                                                                                                |
| `insert(Stage)`                                    | `String`            | Insert a stage, returns its ID                                                                                        |
| `insertAwaitingVpd(CelStage, timeoutMs)`           | `String`            | Insert, waiting up to the timeout for the target VPoint's schema to appear — useful against lazily-registered VPoints |
| `awaitSync(timeoutMs)`                             | `SessionSyncResult` | Wait for subscriber confirmation                                                                                      |
| `awaitSyncOrFail()` / `awaitSyncOrFail(timeoutMs)` | `void`              | Wait; throw on timeout or validation failure; see [zero-subscriber semantics](/testing/overview#zero-subscribers)     |
| `assertSample(ident)`                              | `SampleAssert`      | Fluent chain: `.within(Duration)`, `.exists()`, `.hasValue(path, v)`                                                  |
| `resolveStages(vpointNames...)`                    | `List<StageInfo>`   | Inspect which stages are attached (diagnostics)                                                                       |

<Note>
  If a test starts its own in-process gRPC server, that server still needs [`RegistryBinding.init(...)` and the agent](/sdks/java#connecting-to-the-registry) — otherwise the test is connected to the registry but the server isn't, and sync reports zero subscribers.
</Note>

## Kotest

`io.varianz:varianz-kotest` provides the same lifecycle for Kotest specs — client per spec, session per leaf test:

```kotlin theme={null}
class PricingTests : FunSpec({
    val varianz = VarianzExtension()
    extension(varianz)

    test("stage overrides price") {
        val session = varianz.session()
        session.insert(cel("pricing/calculate", """Quote { price: 999.0, currency: "USD" }"""))
        session.awaitSyncOrFail()
        // trigger + assert
    }
})
```

Endpoint resolution is identical to JUnit's — the two extensions share the same client lifecycle.
