openrewrite / openrewrite/rewrite-testing-frameworks

JUnit4to5Migration removes junit:junit without adding Jupiter when tests extend junit.framework.TestCase

Open
#1,113 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Java
Stars
100
Forks
105
Avg merge
2h 25m
Merged PRs (30d)
9

Description

Hi! Thanks for all the work on these recipes — migrating a 20-year-old codebase with them was genuinely impressive, and this was the one place I got stuck.

org.openrewrite.java.testing.junit5.JUnit4to5Migration removes junit:junit but never adds JUnit Jupiter when a project's tests extend junit.framework.TestCase, leaving a project that no longer compiles. The recipe reports BUILD SUCCESS.

This is squarely within the recipe's own scope: JUnit4to5Migration includes
org.openrewrite.java.testing.junit5.MigrateJUnitTestCase, whose own display name is
"Migrate JUnit 4 TestCase to JUnit Jupiter" and which operates on
junit.framework.TestCase / junit.framework.Assert. Those classes ship inside the
junit:junit 4.x artifact, so this is an ordinary JUnit 4 codebase — no JUnit 3
support is being requested here.

There's also a nasty second-order effect: rewrite:run binds after
process-test-classes, so once the project stops compiling, OpenRewrite can no longer
be run to fix it.

Reproduction — a JUnit 4 project

pom.xml:

<dependencies>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
  </dependency>
</dependencies>

src/test/java/com/example/LegacyTest.java:

package com.example;

import junit.framework.TestCase;

public class LegacyTest extends TestCase {
    public void testAddition() {
        assertEquals(4, 2 + 2);
    }
}

Run:

mvn org.openrewrite.maven:rewrite-maven-plugin:6.46.1:run \
  -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-testing-frameworks:3.44.0 \
  -Drewrite.activeRecipes=org.openrewrite.java.testing.junit5.JUnit4to5Migration
Actual
[WARNING] Changes have been made to pom.xml by:
[WARNING]     org.openrewrite.java.dependencies.RemoveDependency: {groupId=junit, artifactId=junit}
[WARNING] Changes have been made to src/test/java/com/example/LegacyTest.java by:
[INFO] BUILD SUCCESS

pom.xml afterwards has zero <dependency> elements, while the test source now
imports org.junit.jupiter.api. mvn test-compile:

[ERROR] LegacyTest.java:[3,29] package org.junit.jupiter.api does not exist
[ERROR] LegacyTest.java:[5,1]  static import only from classes and interfaces
[ERROR] LegacyTest.java:[8,6]  cannot find symbol
[ERROR] LegacyTest.java:[10,9] cannot find symbol

A project declaring junit:junit:3.8.2 behaves identically — the dependency version is
not the trigger.

Expected

The composite should leave the project compilable: junit-jupiter added in test scope
when junit:junit is removed and no JUnit 5 dependency is present.

Root cause

AddJupiterDependencies builds its internal AddDependency with
onlyIfUsing = org.junit..*:

$ javap -p -c org.openrewrite.java.testing.junit5.AddJupiterDependencies | grep 'ldc.*String'
...
11: ldc  #37   // String org.junit..*
 4: ldc  #31   // String org.junit.jupiter
 6: ldc  #33   // String junit-jupiter
 8: ldc  #35   // String 5.x

A test that only extends junit.framework.TestCase uses no org.junit.* type, so the
guard never fires. The sibling RemoveDependency: junit:junit in the same recipe list
has no such guard and always runs.

Because AddDependency is a ScanningRecipe, its scanner evaluates the pre-edit LST,
so the fact that MigrateJUnitTestCase / UpdateTestAnnotation later rewrite the
sources to org.junit.jupiter.api.Test doesn't help. Tests annotated with
org.junit.Test match the guard and are unaffected — which is why this only shows up
on the junit.framework.TestCase style.

Suggested fix

Broaden the guard in AddJupiterDependencies so it also matches junit.framework..*
alongside org.junit..*. Happy to open a PR with that plus a test case next to the
existing ones if you think that's the right shape — didn't want to presume, since
making the RemoveDependency conditional instead is also defensible.

Not a duplicate of

I looked for existing reports first:

  • #506 — a JUnit 4 project where Jupiter wasn't added; the log there shows
    AddDependency firing correctly with onlyIfUsing=org.junit..*, so the cause was
    different and specific to that project.
  • #457 — Mockito / mockito-all ordering, partially addressed in 216983b. Related
    in being another onlyIfUsing sensitivity, different path.
  • #1103, #1097, #1110 — same family of "JUnit4to5 leaves X behind",
    different X.
  • #1108 — the retained TestCase(String) constructor. Directly adjacent: on this
    repro, adding the dependency by hand then exposes #1108, so a
    junit.framework.TestCase codebase currently needs two manual interventions to get
    through what looks like a single-command migration.

Environment

  • rewrite-maven-plugin 6.46.1, rewrite-testing-frameworks 3.44.0 (both from Maven Central)
  • JDK 21.0.12.1, Maven 3.9.16, surefire 3.5.4

Found while reviving a 2006 Swing/JDBC desktop application (a German Kindergarten
card-index app recovered from SourceForge CVS) and migrating it from Java 8 to Java 21.
Thanks again for the recipes — the rest of that migration was almost entirely automated.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with org.openrewrite.java.testing.junit5.AddJupiterDependencies and the JUnit4to5Migration composition, then inspect the existing tests for this recipe. Reproduce the junit:junit project described in the issue and run mvn test-compile; done means a junit.framework.TestCase migration adds junit-jupiter in test scope and the project remains compilable.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
build-system, testing-qa
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.