Skip to content

feat: add flagd-api-testkit module#1742

Merged
aepfli merged 13 commits intoopen-feature:mainfrom
open-feature-forking:main
Mar 18, 2026
Merged

feat: add flagd-api-testkit module#1742
aepfli merged 13 commits intoopen-feature:mainfrom
open-feature-forking:main

Conversation

@aepfli
Copy link
Copy Markdown
Member

@aepfli aepfli commented Mar 14, 2026

Summary

Adds a new Maven module tools/flagd-api-testkit — a compliance testkit for verifying implementations of the flagd-api Evaluator interface.

Design goals

  • No testcontainers — pure in-memory evaluation testing
  • Bundled artifacts — Gherkin feature files and flag configuration are sourced from the open-feature/test-harness submodule and packaged into the release JAR; consumers need no submodule of their own
  • Minimal consumer setup — implement one method, register one SPI file

Consumer setup

Extend AbstractEvaluatorTest and implement create():

public class MyEvaluatorTest extends AbstractEvaluatorTest {
    @Override
    public Evaluator create(String flagsJson) throws Exception {
        MyEvaluator evaluator = new MyEvaluator();
        evaluator.setFlags(flagsJson);
        return evaluator;
    }
}

Register via SPI in src/test/resources/META-INF/services/dev.openfeature.contrib.tools.flagd.api.testkit.EvaluatorFactory:

com.example.e2e.MyEvaluatorTest

No @Before, no @ConfigurationParameter, no Cucumber imports in the consumer class. The EvaluatorFactory is discovered via java.util.ServiceLoader inside the testkit's @Given("an evaluator") step.

Feature files (10 files, split by concern)

File What it tests
evaluation.feature Basic static resolution (all types)
zero-values.feature Zero/falsy/empty values, static + targeted
no-default-variant.feature Null/undefined defaultVariant edge cases
errors.feature FLAG_NOT_FOUND, TYPE_MISMATCH
fractional.feature Fractional bucketing
semver.feature Semantic version operators
string.feature starts_with / ends_with
targeting.feature Targeting key
evaluator-refs.feature $ref evaluator reuse
metadata.feature Flag metadata

Reference implementation

tools/flagd-core serves as the reference consumer — all 62 Cucumber scenarios pass.

Checklist

  • New module tools/flagd-api-testkit with version.txt, CHANGELOG.md, README.md
  • Added to root pom.xml and release-please-config.json
  • Test-harness submodule at tools/flagd-api-testkit/test-harness
  • Feature files and flags sourced from submodule, bundled into JAR via maven-resources-plugin
  • flagd-core updated to use testkit as reference consumer (62/62 scenarios pass)

@aepfli aepfli requested a review from a team as a code owner March 14, 2026 15:05
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a dedicated testkit for the flagd-api's Evaluator interface, streamlining the process of verifying implementations. By providing a self-contained, in-memory testing solution with bundled test scenarios and a simplified integration model, it significantly reduces the overhead for developers to ensure compliance and correctness of their flagd-api evaluators.

Highlights

  • New Module: flagd-api-testkit: A new Maven module tools/flagd-api-testkit has been introduced, designed as a compliance testkit for verifying implementations of the flagd-api Evaluator interface.
  • In-Memory Evaluation: The testkit focuses on pure in-memory evaluation, explicitly avoiding the use of testcontainers for a lightweight testing approach.
  • Bundled Test Artifacts: Gherkin feature files and flag configurations are sourced from the open-feature/test-harness submodule and bundled directly into the testkit's release JAR, eliminating the need for consumers to manage their own submodules.
  • Simplified Consumer Setup: Consumers can integrate the testkit with minimal effort by extending AbstractEvaluatorTest, implementing a single create() method, and registering their class via a Java Service Provider Interface (SPI).
  • Reference Implementation: The tools/flagd-core module has been updated to serve as a reference consumer for the new testkit, with all 62 Cucumber scenarios passing.
Changelog
  • .gitmodules
    • Added a new git submodule entry for tools/flagd-api-testkit/test-harness.
  • pom.xml
    • Included the new tools/flagd-api-testkit module in the root POM.
  • release-please-config.json
    • Added release configuration for the tools/flagd-api-testkit module.
  • tools/flagd-api-testkit/.gitignore
    • Added ignore rules for generated feature and flag resources within the testkit.
  • tools/flagd-api-testkit/CHANGELOG.md
    • Created the initial changelog file for the flagd-api-testkit module.
  • tools/flagd-api-testkit/README.md
    • Added comprehensive documentation for the flagd-api-testkit, detailing its purpose, usage, and internal workings.
  • tools/flagd-api-testkit/pom.xml
    • Created the Maven POM for the flagd-api-testkit, defining dependencies, properties, and build plugins for submodule management and resource copying.
  • tools/flagd-api-testkit/src/main/java/dev/openfeature/contrib/tools/flagd/api/testkit/AbstractEvaluatorTest.java
    • Added an abstract JUnit Platform Suite class to provide common Cucumber runner configuration for testkit consumers.
  • tools/flagd-api-testkit/src/main/java/dev/openfeature/contrib/tools/flagd/api/testkit/ContextSteps.java
    • Added Cucumber step definitions for building and manipulating evaluation contexts.
  • tools/flagd-api-testkit/src/main/java/dev/openfeature/contrib/tools/flagd/api/testkit/EvaluationSteps.java
    • Added Cucumber step definitions for flag evaluation, assertion of results, reasons, variants, error codes, and metadata.
  • tools/flagd-api-testkit/src/main/java/dev/openfeature/contrib/tools/flagd/api/testkit/EvaluatorFactory.java
    • Added a Service Provider Interface (SPI) for creating Evaluator instances within the testkit.
  • tools/flagd-api-testkit/src/main/java/dev/openfeature/contrib/tools/flagd/api/testkit/EvaluatorInitSteps.java
    • Added Cucumber step definitions responsible for initializing the evaluator and resetting scenario context.
  • tools/flagd-api-testkit/src/main/java/dev/openfeature/contrib/tools/flagd/api/testkit/EvaluatorState.java
    • Added a class to manage shared state across Cucumber scenarios for the testkit.
  • tools/flagd-api-testkit/src/main/java/dev/openfeature/contrib/tools/flagd/api/testkit/EvaluatorUtils.java
    • Added utility methods for type conversion used within the testkit's step definitions.
  • tools/flagd-api-testkit/src/main/java/dev/openfeature/contrib/tools/flagd/api/testkit/TestkitFlags.java
    • Added a utility class for loading bundled flag configurations from the classpath.
  • tools/flagd-api-testkit/test-harness
    • Added a reference to the open-feature/test-harness git submodule.
  • tools/flagd-api-testkit/version.txt
    • Added a version file for the flagd-api-testkit module.
  • tools/flagd-core/pom.xml
    • Updated the flagd-core POM to include the flagd-api-testkit as a test dependency and defined its version property.
  • tools/flagd-core/src/test/java/dev/openfeature/contrib/tools/flagd/core/e2e/FlagdCoreEvaluatorTest.java
    • Added a compliance test suite for FlagdCore by extending AbstractEvaluatorTest.
  • tools/flagd-core/src/test/java/dev/openfeature/contrib/tools/flagd/core/e2e/FlagdCoreInitSteps.java
    • Added Cucumber initialization steps for FlagdCore to register it as the evaluator under test.
  • tools/flagd-core/src/test/resources/META-INF/services/dev.openfeature.contrib.tools.flagd.api.testkit.EvaluatorFactory
    • Added SPI registration for FlagdCoreEvaluatorTest to enable its discovery by the testkit.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new flagd-api-testkit module, which is a significant and well-designed addition for ensuring compliance of flagd-api Evaluator implementations. The use of a git submodule for test harnesses and bundling them into the JAR is a clever approach to simplify consumer setup. The SPI-based discovery of the evaluator factory is also a great design choice for minimizing boilerplate code for consumers.

My review focuses on improving the robustness and maintainability of the new testkit. I've identified a bug in context creation where value types are ignored, a point where brittle reflection can be replaced with public API usage, and a seemingly leftover file that causes a compilation error. Addressing these points will make the testkit more reliable and easier to maintain.

aepfli and others added 4 commits March 14, 2026 16:08
Adds a new Maven module tools/flagd-api-testkit that provides:
- Reusable Cucumber step definitions (EvaluationSteps, ContextSteps)
  for testing any implementation of the flagd-api Evaluator interface
- Gherkin feature files and flag configurations bundled into the
  release JAR via the test-harness git submodule
  (evaluation, fractional, semver, string, targeting, evaluator-refs,
   metadata features)
- TestkitFlags utility to load bundled flag JSON from classpath
- No testcontainers required — pure in-memory evaluation testing

Consumers only need to provide a single @given("an evaluator") step
that wires up their Evaluator implementation, plus a Cucumber suite runner.

Integrates tools/flagd-core as the reference consumer:
- FlagdCoreInitSteps provides the @given("an evaluator") step
- FlagdCoreEvaluatorTest runs the 62 testkit Cucumber scenarios

Build pipeline follows the flagd-core/schemas pattern:
- exec-maven-plugin inits the test-harness submodule
- maven-resources-plugin copies gherkin/flags into src/main/resources
  so they are packaged into the release JAR

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
…pattern

Cucumber forbids extending step definition classes, so the abstract
base class approach is replaced with a @FunctionalInterface factory:

- Add EvaluatorFactory — @FunctionalInterface taking flagsJson String
- Add EvaluatorInitSteps — concrete testkit class owning @given and @before
- Add factory field to EvaluatorState with get/setFactory
- Remove AbstractEvaluatorInitSteps

Consumer setup is now two annotations and a lambda:

    public class MySetup {
        private final EvaluatorState state;
        public MySetup(EvaluatorState state) { this.state = state; }

        @before
        public void registerFactory() {
            state.setFactory(flagsJson -> {
                MyEvaluator e = new MyEvaluator();
                e.setFlags(flagsJson);
                return e;
            });
        }
    }

Flags are loaded inside the testkit (EvaluatorInitSteps.anEvaluator),
passed as a String to the factory so the consumer can call setFlags
on their concrete evaluator type.

FlagdCoreInitSteps updated to use the @before factory pattern.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
Replaces the @Before-hook factory registration pattern with Java SPI
(ServiceLoader), eliminating all Cucumber knowledge from the consumer.

Design:
- EvaluatorFactory is the SPI interface (@FunctionalInterface)
- EvaluatorInitSteps.anEvaluator() discovers the factory via
  ServiceLoader.load(EvaluatorFactory.class) — no state.getFactory()
- AbstractEvaluatorTest implements EvaluatorFactory (abstract create())
  and carries all @suite / Cucumber annotations; GLUE_PROPERTY_NAME
  only needs the testkit package
- AbstractEvaluatorSetup removed — no longer needed

Consumer setup is now two artifacts:
1. One class extending AbstractEvaluatorTest, implementing create():
     public class MyEvaluatorTest extends AbstractEvaluatorTest {
         public Evaluator create(String flagsJson) throws Exception {
             MyEvaluator e = new MyEvaluator();
             e.setFlags(flagsJson);
             return e;
         }
     }
2. META-INF/services/...EvaluatorFactory containing the class name

No @before, no @ConfigurationParameter, no Cucumber imports in
the consumer class.

FlagdCoreEvaluatorTest updated as the reference implementation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
…ranch

Points the testkit submodule to the feat/evaluator-testkut branch commit
which adds evaluator/gherkin/ and evaluator/flags/ — the source files
for the bundled testkit features and flag configuration.

See: open-feature/flagd-testbed#344

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
aepfli and others added 2 commits March 14, 2026 16:15
…ile error

- ContextSteps: use EvaluatorUtils.convert() for proper type conversion instead of always creating string values; fix MutableContext.add() compile error by using map constructor approach; add missing IOException and Map imports
- EvaluationSteps: replace private field reflection with ImmutableMetadata.asUnmodifiableMap()
- pom.xml: pin explicit versions for junit-platform-suite (1.14.3) and cucumber-junit-platform-engine (7.34.3) to fix compile-scope resolution
- Remove FlagdCoreInitSteps.java (no longer needed with SPI pattern)

Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…fixes)

Bumps to ad47bad which fixes gherkin lint violations:
- no-background-only-scenario in evaluation, evaluator-refs, string, targeting
- no-homogenous-tags in no-default-variant

Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
@aepfli
Copy link
Copy Markdown
Member Author

aepfli commented Mar 14, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new flagd-api-testkit module, which provides a compliance testkit for flagd-api Evaluator implementations. The design is well-thought-out, using a git submodule for test harnesses, Maven resource plugin to bundle test files, and Java SPI for easy integration by consumers. The changes also include updating flagd-core to use this new testkit as a reference implementation. The code is clean and the documentation is clear. I have a few minor suggestions to improve code quality and maintainability.

…ws, remove stale annotations

- pom.xml: remove hardcoded versions for junit-platform-suite and cucumber-junit-platform-engine; versions are managed by junit-bom/cucumber-bom in parent; keep explicit compile scope for junit-platform-suite
- EvaluationSteps: narrow throws Throwable -> throws IOException on givenAFlag and resolvedValueEquals
- EvaluationSteps: remove unused @SuppressWarnings(unchecked) and throws ClassNotFoundException from resolvedMetadataContains
- EvaluatorUtils: remove unused @SuppressWarnings(unchecked) from convert()

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
@aepfli
Copy link
Copy Markdown
Member Author

aepfli commented Mar 14, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new flagd-api-testkit module, which is a compliance testkit for flagd-api Evaluator implementations. The changes are well-structured and the new module is a great addition for ensuring compliance. I've provided a few suggestions to improve maintainability and code clarity in the new module.

…fy Object conversion

- pom.xml: extract assertj and jackson-databind versions into properties for maintainability
- EvaluatorUtils: remove redundant value.equals("{}") check; Jackson handles "{}" correctly in the else branch

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
@aepfli
Copy link
Copy Markdown
Member Author

aepfli commented Mar 14, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new flagd-api-testkit Maven module, which serves as a compliance testkit for flagd-api Evaluator implementations. The design is solid, leveraging a git submodule for test harnesses and Java's SPI for extensibility, which allows for minimal setup by consumers. The new module is well-documented and includes a reference implementation update in flagd-core.

My review has identified a few critical issues in the pom.xml of the new module concerning invalid dependency and plugin versions that could cause build failures. Additionally, I've provided a suggestion to refactor a small portion of the code for better clarity and efficiency. Overall, this is a great addition to the project.

Bumps to 9a522b5 which removes the Background block from
no-default-variant.feature to satisfy the no-background-only-scenario
gherkin lint rule.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
Copy link
Copy Markdown
Contributor

@chrfwow chrfwow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

aepfli and others added 2 commits March 16, 2026 11:55
- Add FlagType enum (BOOLEAN, STRING, INTEGER, FLOAT, OBJECT) with
  Gherkin name mapping and fromString() factory
- EvaluatorState.flagType is now FlagType instead of String
- EvaluationSteps: parse incoming Gherkin type token via FlagType.fromString(),
  switch on enum constants instead of string literals
- EvaluatorUtils: add FlagType overload of convert() delegating to String variant;
  String overload retained for metadata type resolution (resolvedMetadataContains)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
@toddbaert toddbaert self-requested a review March 17, 2026 19:57
v3.1.0 of open-feature/flagd-testbed includes the evaluator testkit
gherkin features and flag configuration (PR open-feature#344), so the submodule
no longer needs to track a feature branch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
Setting branch = v3.1.0 in .gitmodules so Renovate tracks version tags
for auto-updates rather than following main.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
@aepfli aepfli merged commit 20e7181 into open-feature:main Mar 18, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants