Skip to main content
Version: 6.2.3 (Unmaintained)

Test Shapes

Paramixel is useful when a test has a shape that should be visible in code, execution, and reports. These patterns use existing 6.1.0 APIs only.

Simple sequence​

Use a sequence when steps must run in order.

import static org.paramixel.api.action.Sequential.sequential;
import static org.paramixel.api.action.Step.step;
import static org.paramixel.api.action.Assert.assertTrue;

Action spec = sequential("login smoke")
.child(step("open login page", ctx -> openLoginPage()))
.child(step("submit credentials", ctx -> submitCredentials()))
.child(assertTrue("user is signed in", true))
.build();
login smoke
├── open login page
├── submit credentials
└── user is signed in

Lifecycle with setup and teardown​

Use a lifecycle shape when setup and cleanup belong to a specific subtree.

import static org.paramixel.api.action.Scope.scope;
import static org.paramixel.api.action.Sequential.sequential;
import static org.paramixel.api.action.Step.step;

Action spec = scope("database workflow")
.before(step("start database", ctx -> startDatabase()))
.body(sequential("checks")
.child(step("migrate schema", ctx -> migrateSchema()))
.child(step("verify query", ctx -> verifyQuery()))
.build())
.after(step("stop database", ctx -> stopDatabase()))
.build();
database workflow
├── before: start database
├── body: checks
│ ├── migrate schema
│ └── verify query
└── after: stop database

Parallel branches​

Use parallel branches when independent scenarios can run concurrently.

import static org.paramixel.api.action.Parallel.parallel;
import static org.paramixel.api.action.Step.step;

Action spec = parallel("browser matrix")
.parallelism(3)
.child(step("chrome", ctx -> runChrome()))
.child(step("firefox", ctx -> runFirefox()))
.child(step("webkit", ctx -> runWebkit()))
.build();
browser matrix
├── chrome
├── firefox
└── webkit

Generated scenarios​

Use generated scenarios when plain Java code should create many related actions.

import static org.paramixel.api.action.Sequential.sequential;
import static org.paramixel.api.action.Step.step;

var users = List.of("ada", "grace", "linus");

Action spec = Each.sequential("user checks", users,
user -> sequential("check " + user)
.child(step("load", ctx -> loadUser(user)))
.child(step("verify", ctx -> verifyUser(user))))
.build();
user checks
├── check ada
│ ├── load
│ └── verify
├── check grace
│ ├── load
│ └── verify
└── check linus
├── load
└── verify

Compatibility-style tests​

Use a compatibility shape when the same behavior must be checked across versions, configurations, or environments.

import static org.paramixel.api.action.Scope.scope;
import static org.paramixel.api.action.Sequential.sequential;
import static org.paramixel.api.action.Step.step;

var versions = List.of("Java 17", "Java 21", "Java 25");

Action spec = Each.parallel("compatibility matrix", versions,
version -> scope(version + " scenario")
.before(step("start " + version, ctx -> start(version)))
.body(sequential("checks")
.child(step("write", ctx -> write(version)))
.child(step("read", ctx -> read(version))))
.after(step("stop " + version, ctx -> stop(version))))
.parallelism(3)
.build();
compatibility matrix
├── Java 17 scenario
│ ├── before: start Java 17
│ ├── body: checks
│ │ ├── write
│ │ └── read
│ └── after: stop Java 17
├── Java 21 scenario
└── Java 25 scenario

Failure handling​

Use the default dependent sequence when later steps should stop after an earlier failure. Use an independent sequence when all children should run so the result tree can collect every outcome.

import static org.paramixel.api.action.Sequential.sequential;
import static org.paramixel.api.action.Step.step;

Action failFast = sequential("checkout")
.child(step("create cart", ctx -> createCart()))
.child(step("submit payment", ctx -> submitPayment()))
.child(step("verify receipt", ctx -> verifyReceipt()))
.build();

Action collectAll = sequential("cleanup checks")
.independent()
.child(step("delete user", ctx -> deleteUser()))
.child(step("delete cart", ctx -> deleteCart()))
.child(step("delete invoice", ctx -> deleteInvoice()))
.build();
checkout
├── create cart
├── submit payment
└── verify receipt (skipped if an earlier dependent child fails)

cleanup checks
├── delete user
├── delete cart
└── delete invoice (all children are attempted)