Skip to content

Junit4 to junit5 migration#17293

Draft
phiz71 wants to merge 45 commits into
masterfrom
test/rest-api-junit5-migration
Draft

Junit4 to junit5 migration#17293
phiz71 wants to merge 45 commits into
masterfrom
test/rest-api-junit5-migration

Conversation

@phiz71
Copy link
Copy Markdown
Member

@phiz71 phiz71 commented Jun 1, 2026

Description

Migrate the whole gravitee-api-management test suite from JUnit 4 to JUnit 5, then remove the JUnit 4
dependencies. After this PR the repository no longer contains a single JUnit 4 import.

Most of the change was produced with the OpenRewrite recipe
org.openrewrite.java.testing.junit5.JUnit4to5Migration (rewrite-testing-frameworks), prettier-formatted, with
manual fixes where the recipe couldn't migrate cleanly. One commit per module for easy review, grouped by area:

  • rest-api (16): fetcher, idp-api, idp-ldap, management-rest, model, portal-rest, repository, rest, security,
    services-dictionary, services-subscriptions, services-sync, services-v3-upgrader, spec-converter,
    standalone-container, service.
  • gateway (17): env, http, repository, security-{keyless,apikey,oauth2,jwt,core},
    services-{endpoint-discovery,healthcheck,debug}, reactor, policy, handlers-api, flow, standalone-container.
  • repository (5): redis, noop, test, mongodb, jdbc.
  • plugin (2): apiservice-handler, apiservice-dynamicproperties-http.
  • Dependency cleanup (final commit): drop junit:junit + junit-vintage-engine now that everything is JUnit 5.

Notable manual fixes on top of the recipe

  • Mockito strictness: classes that used MockitoAnnotations.openMocks/initMocks (lenient) became
    @ExtendWith(MockitoExtension.class) (STRICT_STUBS), surfacing UnnecessaryStubbing/PotentialStubbingProblem.
    Restored the original behaviour with @MockitoSettings(strictness = Strictness.LENIENT).
  • Spring runner: leftover @RunWith(SpringJUnit4ClassRunner.class)/SpringRunner on classes whose @Test
    became JUnit 5 → @ExtendWith(SpringExtension.class) (e.g. AbstractRepositoryTest, AbstractNoOpRepositoryTest,
    InstallationConfigurationTest).
  • assertTrue/assertFalse ambiguity: assertTrue(ctx.getInternalAttribute(...)) resolved to the JUnit 5
    assertTrue(BooleanSupplier) overload → explicit (boolean) cast.
  • Parameterized hierarchy (gateway-flow): @RunWith(Parameterized.class) base classes with a setUp()
    depending on the parameter fields were ported by hand to @ParameterizedTest + @MethodSource.
  • Custom JUnit 4 integration framework (gateway-standalone-container): ApiDeployer TestRule
    BeforeEachCallback/AfterEachCallback extension, ApiDeployerStatement split into deploy()/undeploy(),
    RuleChain → two ordered @RegisterExtension, WireMock WireMockRuleWireMockExtension with
    .configureStaticDsl(true) (so static stubFor(...) keeps targeting the instance) and .getPort()/.getHttpsPort().
  • Shared static state (JdbcPortalNotificationConfigRepositoryTest): the SQL-dialect static field is now
    saved/restored around the test so the new JUnit 5 execution order can't leak it into other tests.
  • Minor: orphan @Override on setUp(), lambda parameter shadowing a local, and a @Test(expected=Exception.class)
    that only passed because @Mock fields were never initialised → rewritten to assert the real behaviour.

Dependency cleanup

  • RepositoryTestSuite (IDE-only @Disabled JUnit 4 suite used by the .run/Repository - * configs) ported to
    the JUnit 5 @Suite/@SelectClasses API → adds org.junit.platform:junit-platform-suite (test) to
    repository-test.
  • Removed junit:junit and junit-vintage-engine from gravitee-apim-parent and repository-elasticsearch
    (both no longer needed).

Additional context

  • Scope: test code only — 831 files changed (828 Java, 3 pom.xml), 41 commits (≈40 per-module + 1 cleanup).
  • Validation: each module compiled and its suite run green locally before committing; repository modules
    (mongo/jdbc/redis) validated against their testcontainers. A full clean reactor build is left to CI.
  • Known pre-existing failures (NOT caused by this PR): ApplicationLogsResourceTest (portal-rest) has 2
    assertions that fail on a non-English JVM locale (they check a Bean Validation @Min message in English);
    confirmed they already fail on the pre-migration code and pass under the CI (English) locale.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@phiz71 phiz71 changed the title rest-api: junit4 to junit5 migration Junit4 to junit5 migration Jun 1, 2026
@phiz71 phiz71 force-pushed the test/rest-api-junit5-migration branch from 5765c5b to faaaa12 Compare June 1, 2026 14:00
phiz71 added 26 commits June 1, 2026 20:44
ran openrewrite recipe org.openrewrite.java.testing.junit5.JUnit4to5Migration
(rewrite-testing-frameworks:RELEASE) scoped to gravitee-apim-rest-api-security
with `-pl <module>` (no `-am`, since `-am` rewrote 371 files across upstream
modules — out of scope for this first-iteration PR).

picked this module to validate the recipe: 5 @RunWith(MockitoJUnitRunner.class)
unit tests, zero @Rule/@ClassRule, no exotic runners, no JerseySpringTest-style
junit 4 base class — smallest viable test bed before iterating on bigger
modules.

the recipe swapped @RunWith(MockitoJUnitRunner.class) for
@ExtendWith(MockitoExtension.class) + @MockitoSettings(strictness = WARN),
converted @test(expected = X.class) to assertThrows, migrated junit imports,
and removed dead exception clauses on void test methods. prettier:write
reformatted one file. 87/87 tests pass (identical to baseline).

manual cleanup on ImageUtilsTest: the original carried a stale
@RunWith(MockitoJUnitRunner.class) but never used any mockito feature, so
the recipe-translated @ExtendWith(MockitoExtension.class) +
@MockitoSettings(strictness = WARN) were redundant. removed the annotations
and the four now-unused mockito/extension imports.
ran openrewrite recipe org.openrewrite.java.testing.junit5.JUnit4to5Migration
(rewrite-testing-frameworks:RELEASE) scoped to
gravitee-apim-rest-api-services-sync with `-pl <module>` (no `-am`, to keep
the diff inside the target module).

picked this module to exercise a recipe transformation the previous
security PR did not cover: @before@beforeeach with mocks initialized
via @Injectmocks. it is the smallest viable target left after excluding
modules with exotic runners or JerseySpringTest-based inheritance.

manual fix on DictionaryManagerTest after the recipe: the original @before
did `cut = new DictionaryManager(); MockitoAnnotations.initMocks(this);` —
the recipe legitimately dropped the initMocks call (MockitoExtension
replaces it) but left the `new DictionaryManager()`, which then overwrote
the @InjectMocks-built instance with one that had no mocks injected. all
five tests failed with "zero interactions with this mock". removed the now
useless @beforeeach entirely: @Injectmocks already builds a fresh instance
per test under MockitoExtension. 5/5 tests pass after the fix (identical
to baseline).
…o junit 5

ran openrewrite recipe org.openrewrite.java.testing.junit5.JUnit4to5Migration
(rewrite-testing-frameworks:RELEASE) scoped to
gravitee-apim-rest-api-services-subscriptions with `-pl <module>` (no `-am`,
to keep the diff inside the target module).

picked this module as the smallest viable target left after excluding
modules with exotic runners or JerseySpringTest-based inheritance. tied
with services-v3-upgrader on @RunWith count (1 each, 0 @rule, 0 exotic),
chose the smaller one per the "less tests = less risk" rule: 1 @test on
50 lines vs 4 @test on 158.

the recipe swapped @RunWith(MockitoJUnitRunner.class) for
@ExtendWith(MockitoExtension.class) + @MockitoSettings(strictness = WARN)
and migrated junit imports. no manual fix required; prettier:write
reformatted one line. 1/1 test passes (identical to baseline).
…junit 5

ran openrewrite recipe org.openrewrite.java.testing.junit5.JUnit4to5Migration
(rewrite-testing-frameworks:RELEASE) scoped to
gravitee-apim-rest-api-services-v3-upgrader with `-pl <module>` (no `-am`,
to keep the diff inside the target module).

picked this module because it is the last remaining candidate strictly
satisfying the constraints (1 @RunWith, 0 @rule, 0 exotic runner). all
modules with more @RunWith are either excluded (service too big,
portal-rest carries JerseySpringTest, model and spec-converter use exotic
runners) or already migrated (security, services-sync,
services-subscriptions).

the recipe swapped @RunWith(MockitoJUnitRunner.class) for
@ExtendWith(MockitoExtension.class) + @MockitoSettings(strictness = WARN),
migrated org.junit.Assert.assertEquals/assertTrue to
org.junit.jupiter.api.Assertions.*, and migrated junit imports. only the
2-argument assertEquals(expected, actual) form is used here, so the JUnit
4 → 5 argument-order trap on the 3-argument variant did not apply. no
manual fix required; prettier:write reformatted the file. 4/4 tests pass
(identical to baseline).
ran openrewrite recipe org.openrewrite.java.testing.junit5.JUnit4to5Migration
(rewrite-testing-frameworks:RELEASE) scoped to
gravitee-apim-rest-api-spec-converter with `-pl <module>`.

this is the first module of the migration that relaxes the "0 exotic
runner" constraint: WSDLToOpenAPIConverterTest used
@RunWith(Parameterized.class). the recipe's ParameterizedRunnerToParameterized
sub-recipe converted it to @ParameterizedTest + @MethodSource("data")
with the data() method preserved as the source. the four other test
classes in the module had no @RunWith and migrated trivially (junit imports
+ one @test(expected = X.class) → assertThrows).

key validation point on this PR: the recipe correctly inverted the
JUnit 4 → JUnit 5 argument order on 3-argument assert variants
(assertEquals(message, expected, actual) → assertEquals(expected, actual,
message), assertNotNull(message, object) → assertNotNull(object, message)).
this was the trap flagged in earlier PRs that had not been exercised yet
— no manual fix needed on that front.

manual cleanup on WSDLToOpenAPIConverterTest: the recipe preserved the
four public @parameter fields plus a generated initWSDLToOpenAPIConverterTest
method that copied the new method parameters back into those fields. with
@ParameterizedTest the parameters are already available as method args
and the fields had no external use (grepped src/main and src/test), so
both the fields and the init method are dead code. removed them; the
method body already referenced the local parameters, not the fields, so
behaviour is unchanged. 13/13 tests pass (identical to baseline).
ran openrewrite recipe org.openrewrite.java.testing.junit5.JUnit4to5Migration
(rewrite-testing-frameworks:RELEASE) scoped to gravitee-apim-rest-api-model
with `-pl <module>`.

14 of 21 test files migrated: 2 @RunWith(Parameterized.class)
(CustomApiKeyTest, NewPreRegisterUserEntityValidatorTest) plus 12 plain
junit 4 files that only needed import updates. the remaining 7 files were
already on junit 5.

the recipe converted both Parameterized tests to @ParameterizedTest +
@MethodSource("data") and migrated org.junit.Assert.* to
org.junit.jupiter.api.Assertions.*. only the 2-argument assertEquals
forms are used here so the JUnit 4 → 5 argument-order trap on the
3-argument variant did not apply.

manual cleanup on both Parameterized tests: the recipe preserved the
public @parameter fields plus a generated init<ClassName> method that
copied the new method parameters back into those fields. as for
spec-converter, the fields had no external use and the parameters are
available as method args, so both the fields and the init method are
dead code. CustomApiKeyTest needed an extra step — its method body still
referenced `this.customApiKeyParam` / `this.violationSize`, which would
have read null fields once the init method was removed; switched those
references to the local parameters. 120/120 tests pass (identical to
baseline).
phiz71 added 19 commits June 1, 2026 20:45
migrate the last junit 4 file (repositorytestsuite) to the junit 5 @suite api
(junit-platform-suite) and remove the junit:junit + junit-vintage-engine
dependencies from the parent pom and repository-elasticsearch.
- gateway-core HttpEndpointInvokerTest: junit.framework.AssertionFailedError -> org.opentest4j.AssertionFailedError
- repository-test SubscriptionRepositoryTest: assertEquals message argument moved to the junit 5 last-arg position
@phiz71 phiz71 force-pushed the test/rest-api-junit5-migration branch from faaaa12 to 9671c6d Compare June 1, 2026 18:46
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 1, 2026

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.

2 participants