Skip to main content
Version: 5.1.0 (Unmaintained)

Discovery

Paramixel can discover Action instances from the classpath using @Paramixel.Factory methods. Discovery is optional — you can also construct and run actions programmatically.

@Paramixel.Factory​

Mark a public static no-argument method that returns Spec<?> or Action<?> with @Paramixel.Factory:

import org.paramixel.api.Paramixel;
import org.paramixel.api.action.Action;
import org.paramixel.api.action.Step;

public class MyTest {

@Paramixel.Factory
public static Action<?> factory() {
return Step.of("my-test", ctx -> {
// test logic
});
}
}

Requirements for a valid factory method:

  • public and static
  • Zero arguments
  • Annotated with @Paramixel.Factory
  • Returns Spec, Action, or null
  • Not annotated with @Paramixel.Disabled

Factories that return null are skipped.

@Paramixel.Disabled​

Exclude a factory method from discovery:

@Paramixel.Factory
@Paramixel.Disabled("temporarily disabled for investigation")
public static Action<?> flakyTest() {
// ...
}

@Paramixel.Tag​

Tag factory methods for selective discovery:

@Paramixel.Factory
@Paramixel.Tag("smoke")
public static Action<?> smokeTest() {
// ...
}

@Paramixel.Factory
@Paramixel.Tag("integration")
public static Action<?> integrationTest() {
// ...
}

Multiple tags are supported:

@Paramixel.Tag("smoke")
@Paramixel.Tag("critical")

@Paramixel.Tag is @Repeatable(@Paramixel.Tags.class). The container annotation @Paramixel.Tags is used internally when multiple tags are declared on the same method. You do not need to use @Paramixel.Tags directly — simply repeat @Paramixel.Tag.

@Paramixel.Priority​

Control discovery ordering. Higher priority classes are ordered earlier:

@Paramixel.Priority(100)
public class CriticalTests {
@Paramixel.Factory
public static Action<?> factory() { /* ... */ }
}

Priority affects scheduling admission order only; concurrent execution does not imply completion order.

@Paramixel.Id​

Assign a stable identifier to a method for AnnotationResolver lookup:

public class MyTest {
@Paramixel.Id("login")
public void login() { /* ... */ }

@Paramixel.Id("logout")
public void logout() { /* ... */ }
}

See AnnotationResolver below.

Selector​

Selector is an interface that describes discovery criteria. Create selectors with static factory methods:

See Selector API for the full API details.

import org.paramixel.api.selector.Selector;

// Match all factories
Selector.all();

// Match a class's package and subpackages
Selector.packageTreeOf(MyTest.class);

// Match a class's exact package (excluding subpackages)
Selector.packageOf(MyTest.class);

// Match a single class
Selector.classOf(MyTest.class);

// Regex-based selectors
Selector.packageRegex("com\\.example");
Selector.classRegex("com\\.example\\..*Test");
Selector.tagRegex("smoke");

All regex-based selectors use Java regex find() semantics. For exact match, anchor with ^...$.

Selector composition​

Combine selectors with logical and, or, and not:

// AND — factory must match ALL selectors
Selector.and(Selector.packageRegex("com\\.example"), Selector.tagRegex("smoke"));

// OR — factory must match ANY selector
Selector.or(Selector.tagRegex("smoke"), Selector.tagRegex("integration"));

// NOT — factory must NOT match the selector
Selector.not(Selector.packageRegex("com\\.example\\.slow"));

and and or accept varargs or a List<Selector>. Compose arbitrarily:

Selector selector = Selector.and(
Selector.packageRegex("com\\.example"),
Selector.or(Selector.tagRegex("smoke"), Selector.tagRegex("critical")),
Selector.not(Selector.classRegex(".*Slow"))
);

Selector query methods​

Every Selector exposes query methods to inspect its criteria:

selector.matchesPackage("com.example.tests"); // true if package matches
selector.matchesClass("com.example.MyTest"); // true if class matches
selector.matchesTag("smoke"); // true if tag matches

Discovery via Runner​

Runner discovers actions from the classpath when a Selector is provided:

import org.paramixel.api.Runner;
import org.paramixel.api.selector.Selector;

Runner runner = Runner.defaultRunner();

// Resolve and run all discovered actions
int exitCode = runner.run();

// Resolve and run actions matching a selector
Optional<Result> result = runner.run(Selector.packageTreeOf(MyTest.class));

Discovery is performed by the runner's internal classpath scanner using the runner's effective configuration. Discovered actions are always combined as a Parallel root, ordered by priority descending, then package name, class name, and factory method name.

AnnotationResolver​

AnnotationResolver resolves @Paramixel.Id annotated methods on a concrete type into named Action instances:

import org.paramixel.api.AnnotationResolver;

AnnotationResolver<MyTest> resolver = AnnotationResolver.create(MyTest.class);

// Instance method
Action<MyTest> login = resolver.byId("login");

// Static method
Action<?> staticAction = resolver.staticById("staticSetup");

Use this to compose test actions from annotated methods on a test class, especially with Instance:

Instance.of(MyTest.class)
.child(resolver.byId("login"))
.child(resolver.byId("verify"))
.child(resolver.byId("logout"))
.resolve();

See AnnotationResolver API for the full API details including caching, method signature requirements, and custom kinds.

Configuration-driven filtering​

Set these keys in paramixel.properties or system properties to filter discovery:

KeyDescription
paramixel.match.package.regexRegex matched against package names
paramixel.match.class.regexRegex matched against fully qualified class names
paramixel.match.tag.regexRegex matched against @Paramixel.Tag values
./mvnw test -Dparamixel.match.tag.regex=smoke
./gradlew paramixelTest -Dparamixel.match.tag.regex=smoke

Running discovered actions​

With Runner​

Runner runner = Runner.defaultRunner();
runner.run(); // discovers and runs all actions

With Selector​

Runner runner = Runner.defaultRunner();
Optional<Result> result = runner.run(Selector.packageTreeOf(MyTest.class));

Exit code​

int exitCode = runner.runAndReturnExitCode(action);
int exitCode = runner.runAndReturnExitCode(selector);
int exitCode = runner.run(); // discovers from classpath

Exit code 0 indicates success; 1 indicates failure. Skipped and aborted outcomes can be configured as failures (see Configuration).