Skip to content

Create junit-vintage module #10351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from

Conversation

kcooney
Copy link

@kcooney kcooney commented Jun 5, 2025

This adds a a module for supporting junit4-style tests. The module contains
two rules:

  • Testcontainers - For managing containers
  • TemporaryNetwork - For managing a network

These provide a migration path for users of Testcontainers that have
JUnit4-style tests, so that the Testcontainers project can later remove
dependencies on JUnit4 classes in the core Testcontainers code (see #970).

This PR is a work in progress, intended as a starting point for discussions.

Remaining work:

  • Add tests for TemporaryNetwork
  • Update tests for Testcontainers to verify behavior of the rule when used
    with @ClassRule
  • Add the module name to the following files:
    • ./.github/ISSUE_TEMPLATE/bug_report.yaml
    • ./.github/ISSUE_TEMPLATE/enhancement.yaml
    • ./.github/ISSUE_TEMPLATE/feature.yaml
    • ./.github/dependabot.yml
    • ./.github/labeler.yml
  • Add documentation for the module to ./docs/modules/

@kcooney kcooney requested a review from a team as a code owner June 5, 2025 18:39
@kcooney kcooney marked this pull request as draft June 5, 2025 18:39
@kcooney
Copy link
Author

kcooney commented Jun 5, 2025

FYI: @vlsi and @marcphilipp

@kcooney kcooney force-pushed the testcontainers-rule branch 4 times, most recently from 8264c3b to 3fb74aa Compare June 6, 2025 05:08
}

return ReflectionSupport
.findFields(description.getTestClass(), isTargetedContainer, HierarchyTraversalMode.TOP_DOWN)
Copy link

Choose a reason for hiding this comment

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

I wonder what happens if class Subclass extends AbstractClass and both of them declare @Rule Testcontainers. It might cause for the containers to discover and initialize twice.

I guess each @Rule might "manage" own the class it is located (e.g. @Rule in Subclass should start/stop only the direct containers in Subclass, and the rule in AbstractClass should manage the containers in AbstractClass plus all its superclasses unless they have one more @Rule Testcontainers).

An alternative option would be to just fail in such scenarios so the user removes one of the @Rule Testcontainers.

Copy link
Author

Choose a reason for hiding this comment

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

I wonder what happens if class Subclass extends AbstractClass and both of them declare @Rule Testcontainers. It might cause for the containers to discover and initialize twice.

Then don't do that 😊

Having large test class hierarchies is an anti-pattern. Just like sharing code via delegation is preferred over sharing code via inheritance in non-test code, using delegation (like Rules) is preferable to large test class hierarchies.

I guess each @Rule might "manage" own the class it is located (e.g. @Rule in Subclass should start/stop only the direct containers in Subclass, and the rule in AbstractClass should manage the containers in AbstractClass plus all its superclasses unless they have one more @Rule Testcontainers).

The behavior of this rule mirrors that of the JUnit Jupiter extension, so I'd prefer to keep it as-is to ease migration.

Copy link

Choose a reason for hiding this comment

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

Having large test class hierarchies is an anti-pattern

Testcontainers-java include a lot of test that inherit each other, and you can't prevent everyone from writing such tests.

The behavior of this rule mirrors that of the JUnit Jupiter extension, so I'd prefer to keep it as-is to ease migration

@kcooney , could you please double-check?

As far as I read JUnit5 documentation, I believe JUnit5 does not result in double-start and double-stop of the containers in case users apply @Testcontainers extension for both subclass and its superclass.

https://junit.org/junit5/docs/current/user-guide/#extensions-registration-inheritance
A specific extension implementation can only be registered once for a given extension context and its parent contexts. Consequently, any attempt to register a duplicate extension implementation will be ignored.

Copy link
Author

Choose a reason for hiding this comment

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

Having large test class hierarchies is an anti-pattern

Testcontainers-java include a lot of test that inherit each other, and you can't prevent everyone from writing such tests.

People who write such tests should understand that they inherit the behavior of Rules defined in the base class.

The behavior of this rule mirrors that of the JUnit Jupiter extension, so I'd prefer to keep it as-is to ease migration

@kcooney , could you please double-check?

I literally copied the Container discovery code from the Jupiter extension code.

Copy link

Choose a reason for hiding this comment

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

I literally copied the Container discovery code from the Jupiter extension code.

The difference is that JUnit4 would happily execute both rules (one from a base class, another one from a subclass), so it would result in double start and double stop.
JUnit5 would execute extension only once since it registers the extension class only once even if it is declared for both base class and its subclass.

Copy link
Author

@kcooney kcooney Jun 6, 2025

Choose a reason for hiding this comment

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

I literally copied the Container discovery code from the Jupiter extension code.

The difference is that JUnit4 would happily execute both rules (one from a base class, another one from a subclass), so it would result in double start and double stop. JUnit5 would execute extension only once since it registers the extension class only once even if it is declared for both base class and its subclass.

Again, the perfect is the enemy if the good. There is only so much we can do due to the limitations of JUnit4.

People should not use this rule in a class if a base class has the same rule.

The alternative would be requiring that the rule being redefined in every subclass (more boilerplate), and users would need to avoid shadowing the rules defined in the base class.

FWIW, at Google we had internal rules that worked his way (definitions in the base class affecting behavior of annotated classes in subclasses) and I never heard anyone have a problem due to accidentally defining the rule multiple times in the same class hierarchy.

Let's see what the maintainers say.

Copy link

Choose a reason for hiding this comment

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

Again, the perfect is the enemy if the good. There is only so much we can do due to the limitations of JUnit4.

I'm sorry, have you seen a couple of suggested fixes for the problem I listed in the original message?

Approach A: fail the test if the rule is present more than once in the test hierarchy. A straightforward error message would convey the idea that the users should not use the rule multiple times in a single hierarchy.

Approach B: make sure only the first rule in hierarchy wins so the rest becomes a no-op.
In other words, each rule could enumerate all TestcontainerRule objects within the hierarchy, and execute only in those cases, if the current rule is the first (e.g. it has the minimal hierarchy level, and it is the first based on the field name). The implementation would be trivial, and it would effectively match the behaviour from JUnit5 which registers an extension only once.

Frankly, I do not understand why do you say about "limitations of JUnit4". Both "approach A" and "approach b" are straightforward, they would make the migration easier, I can help with the implementation, and so on.

I assume it would look awkward if I suggest a PR on top of your branch.

@kcooney kcooney changed the title Create Testcontainers Rule Create junit-vintage module Jun 6, 2025
@kcooney kcooney force-pushed the testcontainers-rule branch from f2d9987 to ae0f95f Compare June 6, 2025 16:24
@kcooney kcooney marked this pull request as ready for review June 6, 2025 16:24
@kcooney kcooney force-pushed the testcontainers-rule branch 6 times, most recently from b675a38 to c1081ff Compare June 7, 2025 00:18
@kcooney kcooney force-pushed the testcontainers-rule branch from af9afa5 to 77d45a9 Compare June 7, 2025 21:00
@kcooney kcooney force-pushed the testcontainers-rule branch 7 times, most recently from 26171e4 to d45f926 Compare June 8, 2025 00:00
@kcooney kcooney force-pushed the testcontainers-rule branch from d45f926 to 8c66d21 Compare June 8, 2025 02:29
@kcooney kcooney force-pushed the testcontainers-rule branch from 8c66d21 to 35e5731 Compare June 8, 2025 03:30
@eddumelendez
Copy link
Member

Hi @kcooney, as I previously mention in #10285 (comment) and this demonstrated it again. We will be removing JUnit 4 dependency with no support for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants