The framework is built from plain, instantiable classes with instance methods - no static utilities. This page is the reference for the main entry points.

Hop fixtures (hop-junit-core)

@HopPipeline / @HopWorkflow

Method annotations that declare which artifact to run. Both are method-level only and not repeatable - declare one pipeline/workflow per test method. The single-level meta-annotation alias for @HopParameters is supported by @HopParameters, not by the pipeline/workflow annotations.

Attribute Description

value()

The artifact path, resolved against projectHome().

projectHome()

The Hop project home; defaults to src/main/repositories.

runConfiguration()

The Hop run configuration used to build the engine; defaults to the local engine.

parameters()

Per-test @HopParameters, merged last (highest precedence).

The extension injects a HopPipelineFixture / HopWorkflowFixture:

@Test
@HopPipeline(value = "load/T_LOAD.hpl", projectHome = "src/test/resources")
void loads(final HopPipelineFixture pipeline) {
    pipeline.with("load.table", "ORDERS")
            .withConnection("SRC_ORACLE", src)
            .execute()
            .assertSuccess()
            .assertLogContains("Partition created");
}

Parameters: @HopParameters / @HopParameter

Merged in ascending precedence: class-level batch (unless the method-level occurrence opts out with inherit = false), then method-level batch, then the parameters declared on the pipeline/workflow annotation. A single-level alias works by putting @HopParameters on another annotation.

Assertions: HopPipelineExecution / HopWorkflowExecution

execute() returns a fluent execution object:

  • assertSuccess() / assertFailure() - Hop Result error count assertions, with the Hop log appended to the failure message;

  • assertLogContains(String) - substring assertion on the Hop log text;

  • logText() / result() - direct access;

  • assertThat(Consumer) - group custom assertions.

Database facade (hop-junit-database-core)

@Database

Repeatable method annotation declaring a named database; the same test body runs on both backends. Attributes: name(), init() (@DbInit[]), expected() (@DbExpected[]). The backend is resolved from database.default.implementation (default H2) through java.util.ServiceLoader; the database.default.h2.mode property (default ORACLE_EXTENDED) selects the H2 compatibility mode.

@DbInit / @DbExpected

@DbInit seeds the database: *.sql resources run as raw statements (schemas/DDL), anything else is loaded as a DBUnit dataset and applied with operation() (CLEAN_INSERT by default). Optional knobs: format() (AUTO infers from the extension), supportedDatabases() (empty = all), admin() (run through the admin connection - Oracle provisioning), caseSensitive().

The default AUTO resolution maps the resource extension to a dataset format: .yaml/.yml to YAML, .xml to flat XML (or full XML for the FLAT_XML/FULL_XML formats), .csv to CSV, .json to JSON. .xls/.xlsx map to XLS, which is available only when Apache POI is on the test classpath:

<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi-ooxml</artifactId>
  <scope>test</scope>
</dependency>

The version is managed by the hop-junit parent POM, so no <version> is needed. Without POI, seeding an .xls/.xlsx dataset fails with a clear message telling you to add those dependencies. YAML, XML, CSV and JSON need no extra dependency (DBUnit brings them transitively).

@DbExpected verifies a post-condition dataset in afterEach, only when the test passed. Knobs: format(), comparison() (STRICT/ORDERED/ROWCOUNT), ignoreColumn() (table.column or bare column), ignoreTable(), caseSensitive(). It supports the same formats as @DbInit.

JdbcConnectionSpec / @JdbcNamed

A named database is injected as a JdbcConnectionSpec:

void test(@JdbcNamed("SRC") final JdbcConnectionSpec src) {
    // H2: jdbcUrl() is the plain in-memory URL.
    // The record exposes name(), username(), password(), jdbcUrl(), implementation() and schema().
    // databaseTypeName() returns the Hop DatabaseMeta type: "H2" or "ORACLE".
    spec -> src.databaseTypeName();
}

BaseDatabaseExtension (SPI)

Implement this class (package io.yupiik.hop.junit.database) and register it in META-INF/services/io.yupiik.hop.junit.database.BaseDatabaseExtension to provide a new backend:

  • name() - matches DatabaseImplementation.current() ("H2", "ORACLE", ...);

  • findDatabases(context) - the ordered, de-duplicated descriptors the method declares;

  • createDatabase(context, descriptor) - open the physical database, return spec + connection;

  • seedDataset(managed, inits) / verifyDataset(managed, expected) - the dataset engine hooks;

  • optionally beforeDatabases, resetDatabase, provisionAdmin for backend-specific lifecycle.

The base class implements the whole JUnit lifecycle (schema creation, seeding, verification after a passing test, close + reset) and the @JdbcNamed parameter resolution. The isolated Hop working tree per test is prepared by the @HopPipeline / @HopWorkflow extensions (HopTestWorkDirectories), not by the database backend, so the two responsibilities stay decoupled.

Configuration surface

Property Default Description

database.default.implementation

H2

Which registered backend the @Database facade uses.

database.default.h2.mode

ORACLE_EXTENDED

H2 compatibility mode for @Database descriptors.

oracle.image

pinned digest

Testcontainers image for the Oracle backend. The key derives from the backend's simple class name minus DatabaseExtension, lowercased (OracleDatabaseExtension -> oracle.image), so any Testcontainers backend can be targeted.

project.build.directory

target

Base dir for the isolated Hop work tree. When set by the IDE (rather than Maven), pass it through surefire with systemPropertyVariables so HopTestWorkDirectories respects the configured build directory.