openrewrite / openrewrite/rewrite-testing-frameworks
JUnit4to5Migration removes junit:junit without adding Jupiter when tests extend junit.framework.TestCase
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
AddDependencyfiring correctly withonlyIfUsing=org.junit..*, so the cause was
different and specific to that project. - #457 — Mockito /
mockito-allordering, partially addressed in 216983b. Related
in being anotheronlyIfUsingsensitivity, 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.TestCasecodebase currently needs two manual interventions to get
through what looks like a single-command migration.
Environment
rewrite-maven-plugin6.46.1,rewrite-testing-frameworks3.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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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