testcontainers/testcontainers-java

GenericContainer run from Jupiter tests shouldn't require JUnit 4.x library on runtime classpath

bmuschko opened this issue Β· 63 comments

I tried out the JUnit 5 support using the following build script. I'd expect that TestContainers doesn't require the JUnit 4.x library. As you can see in the build script below, the legacy dependency has been excluded.

apply plugin: 'java-library'

sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
version = '1.0'

repositories {
    jcenter()
}

dependencies {
    def junitJupiterVersion = '5.3.1'
    testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion"
    testImplementation "org.junit.jupiter:junit-jupiter-params:$junitJupiterVersion"
    testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion"
    testImplementation 'org.testcontainers:junit-jupiter:1.10.1'
}

// IMHO shouldn't be required
configurations.all {
   exclude group: 'junit', module: 'junit'
}

tasks.withType(Test) {
    useJUnitPlatform()
}

In my test case, I am creating a GenericContainer.

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@Testcontainers
public class TestContainerJunit5Test {
    @Container
    private GenericContainer appContainer = new GenericContainer();

    @Test
    void tryItOut() {
        // do something
    }
}

At runtime I get the following exception. My guess is that GenericContainer still uses JUnit 4.x classes.

java.lang.NoClassDefFoundError: org/junit/rules/TestRule
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at java.lang.Class.getDeclaredFields0(Native Method)
	at java.lang.Class.privateGetDeclaredFields(Class.java:2583)
	at java.lang.Class.getDeclaredFields(Class.java:1916)
	at org.junit.platform.commons.util.ReflectionUtils.getDeclaredFields(ReflectionUtils.java:1106)
	at org.junit.platform.commons.util.ReflectionUtils.findAllFieldsInHierarchy(ReflectionUtils.java:886)
	at org.junit.platform.commons.util.ReflectionUtils.findFields(ReflectionUtils.java:874)
	at org.junit.platform.commons.util.AnnotationUtils.findAnnotatedFields(AnnotationUtils.java:320)
	at org.junit.platform.commons.util.AnnotationUtils.findAnnotatedFields(AnnotationUtils.java:297)
	at org.junit.jupiter.engine.descriptor.ExtensionUtils.registerExtensionsFromFields(ExtensionUtils.java:92)
	at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.prepare(ClassTestDescriptor.java:154)
	at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.prepare(ClassTestDescriptor.java:74)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$0(NodeTestTask.java:80)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:80)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:66)
	at java.util.ArrayList.forEach(ArrayList.java:1257)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:110)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:170)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:154)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:90)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:92)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$100(JUnitPlatformTestClassProcessor.java:77)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:73)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
	at com.sun.proxy.$Proxy2.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:131)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:155)
	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:137)
	at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ClassNotFoundException: org.junit.rules.TestRule
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 80 more

Hi @bmuschko,

We very much agree, and we do plan to remove the dependency, but it will be a breaking change and we will do it in Testcontainers 2.0.
We were planning to start 2.0 earlier but discovered a few low hanging fruits (like the JUnit 5 extension, or OkHttp transport), this is why it got delayed. But now probably is a good time to finally focus on 2.0 :)

/cc @rnorth @kiview

Also see #87 with some more (historic) discussions regarding this topic.

Great, thanks for confirming. Looking forward to 2.0.

I ran into this issue as well and it's currently a blocker for me. Is there any estimated date when 2.0 will be released? Where can I find roadmap?

The product looks very promising btw :) looking forward to 2.0 as well so I can finally start using it

Sorry, we haven't updated the roadmap for quite some time πŸ™‚
Why exactly is it a blocker for you?

wow, that was a quick response :)
we switched to Junit5 and are explicitly excluding Junit4 from all dependencies to make sure people don't use it accidentally or out of habit

I see, makes sense for some projects.
We would love to remove this dependency rather sooner than later, however since it will be a breaking change, we haven't decided on when to actually do it yet.

Any updates on this one?

@evonier no updates since the last acknowledgement of the problem and the promise to target it for 2.x version.

Updating testcontainers from version 1.11.4 to version 1.12.0 fixed the same problem for me

Updating testcontainers from version 1.11.4 to version 1.12.0 fixed the same problem for me

But things still derive from the JUnit 4 TestRule πŸ€”

Is there any update ? With v 1.12.5 the exclusion of junit dependency still results in class problems.

As a hint: at my work, I use a test container jdbc url for creating the datasource. And therefore, the docker container "run" creation is delegated to the test container driver itself. No explicit coding with @ClassRule and/or @Rule within my unit/integration tests. From that point of view, the dependency to junit4 does not make really sense to me.

no updates since the last acknowledgement of the problem and the promise to target it for 2.x version.

@rfelgent
As this issue is still open, this answer by @bsideup is still valid. The reasons are grounded in the way our code has been structured so far.

With v 1.12.5 the exclusion of junit dependency still results in class problems.

You can create your wrapper maven project, exclude JUnit4 and provide necessary JUnit classes separately.

Here's a workaround if no dependency on JUnit 4 is a must: create fake TestRule and Statement classes under your test root. Since GenericContainer doesn't really use them unless run by JUnit 4, this hack works fine.

package org.junit.rules;

@SuppressWarnings("unused")
public interface TestRule {
}

and

package org.junit.runners.model;

@SuppressWarnings("unused")
public class Statement {
}

@detouched
Thanks. I also needed to include another empty class for my WebDriverContainer.

package org.junit.rules;
public class ExternalResource {
}

Was getting the following exception:


java.lang.NoClassDefFoundError: org/junit/rules/ExternalResource

	at java.base/java.lang.ClassLoader.defineClass1(Native Method)
	at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
	at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
...
	at org.testcontainers.containers.Network.<clinit>(Network.java:22)
	at org.testcontainers.containers.BrowserWebDriverContainer.configure(BrowserWebDriverContainer.java:158)
	at org.testcontainers.containers.GenericContainer.doStart(GenericContainer.java:305)
	at org.testcontainers.containers.GenericContainer.start(GenericContainer.java:300)

Is there any intention to fix this in the near future?

we switched to Junit5 and are explicitly excluding Junit4 from all dependencies to make sure people don't use it accidentally or out of habit

Same here πŸ‘ We also excluded all JUnit 4 dependencies to be forced to use Jupiter.

@pixelstuermer since this is not a major issue, atm we're focusing on other, bigger ones.

That said, we do plan to decouple the core from JUnit 4 in 2.0 (with probably an experimental API in 1.x that will allow the migration path), but it will take a bit of time.

@pixelstuermer since this is not a major issue, atm we're focusing on other, bigger ones.

That said, we do plan to decouple the core from JUnit 4 in 2.0 (with probably an experimental API in 1.x that will allow the migration path), but it will take a bit of time.

OK thanks. Nice to hear that you will keep an eye on this πŸ‘

Any updates on this? It is really causing our team to struggle as there are junit 4 annotation but they don't work

20fps commented

Same here folks, I hope this issue will be addressed soon.
Migrated projects to the latest Spring Boot 2.4.2 where JUnit 4 is removed, so I hope we can get JUnit 5 support for testcontainers with JUnit 4 exluded.

Since this requires some more fundamental changes to the existing codebase and architecture, we can't give an ETA on this feature. @bsideup already outlined some aspects in this issue and further inquiries won't expedite the development or release of this feature.

Given the amount of duplicate issues asking for the same thing, I think this issue can be considered major?

@gastaldi what makes it major? Does it break anything? Or blocks some usage?

Since GenericContainer depends on a class from JUnit 4, we can't easily remove the dependency, meaning that we either need to work on 2.0 or introduce a new API (something that we considered) that won't have said classes in the class hierarchy, so it can be excluded.

FWIW the current "problem" with JUnit 4 is that both JUnit 4 and JUnit 5 annotations are present and sometimes people use org.junit.Test instead of Jupiter's (unless we miss some other major issues with having JUnit 4 in our dependencies)

There are workarounds (see above), there are codestyle rules that prevents the usage of org.junit.* annotations. Given that, I struggle to agree that the issue is major, and that we should spend our (currently very reduced due to personal reasons) time on it and not on other major issues/features.

WDYT?

It introduces security issues, for one. For JUnit 5 projects, JUnit 4 dependency offers extra attack surface.

Hey @bsideup!

Since this introduces some API changes (major changes I believe) I agree this needs to be targeted to 2.0 (along with support for Podman, but that's another discussion πŸ˜„).

My use case is simple: we are planning to use TestContainers to bootstrap databases when running tests with Quarkus. Because our tests require JUnit 5, having the JUnit 4 JAR in the classpath may be confusing, but we can workaround it somehow.

See the full thread here: https://groups.google.com/g/quarkus-dev/c/EQpAI9kBrGk/m/URMR8OibBAAJ

@lprimak please read that CVE before using the "security issues" argument. Just having a jar on your classpath does not introduce any security issues. Plus, that CVE was about the TemporaryFolder rule, and too permissive access that allows reading temporary files created by that rule by other users.
This is hilarious to refer to it as a security risk of having JUnit 4 as a dependency πŸ˜‚

please read that CVE before using the "security issues" argument

oh come on now :) I am talking about attack surface, not that one particular issue.

Guys come on, the Testcontainers team are working on this in their own free time, if you really want to expedite it, then you should help contribute to the project or get your company to sponsor them.

If not, if the issue is really critical to you, you can always use the workaround by @detouched here: #970 (comment)

@gastaldi There are no plans to support Podman in 2.0 (unless you know something that I don't, e.g. some generosity by Red Hat to join us as a Gold Sponsor and push the idea forward).
As we previously communicated many times, Podman should provide an API that is compatible with Docker, as they claim they do.


My use case is simple: we are planning to use TestContainers to bootstrap databases when running tests with Quarkus.

Nice! Although I am afraid in this case even the new experimental API in 1.x won't help as you would expose the Testcontainers dependency to your users (otherwise you would just use the stub workaround), and excluding junit4 would make the end users struggle to understand why new GenericContainer fails the compilation for them when used in Quarkus... 2.0 is closer than ever (yes, I know that I said something similar 2 years ago, lol), so this period of junit4 dependency shouldn't be long :)

Thanks @bsideup !

Yeah, I was reading the outcome of that discussion about podman after commenting in this issue and I think this API is planned for Podman 3.0 but I haven't tested yet.

BTW TestContainers is fantastic and you guys are doing an amazing job!
Keep up the good work! πŸ‘πŸ»

@gastaldi thank you! :) We are here for you, our amazing users ❀️

May I suggest creating a "junit4-workaround" module within testcontainers that could be easily added to any projects that may need it. I can submit a PR with this.

@svpace How do you imagine such a module to be implemented if Testcontainers core classes depend on JUnit4?

shade plugin?

Basically #970 (comment) and #970 (comment). Exclude junit4 and add workaround module with the empty classes. Not a perfect solution, but I've been using this setup locally for some time without issues so far.

Quarkus users can use this dep https://mvnrepository.com/artifact/io.quarkus/quarkus-junit4-mock

Module with some empty JUnit4 classes to allow Testcontainers to run without needing to include JUnit4 on the class path

Thanks for sharing @cameronbraid ! Looks like there are no dependencies and just dummy classes based on https://github.com/quarkusio/quarkus/tree/main/core/junit4-mock so seems it would work fine for anyone to use it, even if not using Quarkus.

Unfortunately all versions are compiled for Java 11 so it won’t work for us poor Java 8 users 😏

Quarkus users can use this dep https://mvnrepository.com/artifact/io.quarkus/quarkus-junit4-mock

Module with some empty JUnit4 classes to allow Testcontainers to run without needing to include JUnit4 on the class path

Hello @detouched and everyone else,

your workaround works fine for my gradle build. When I try to execute the tests with Intellij there is always coming the error below:

Supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath:
class org.testcontainers.containers.FailureDetectingExternalResource, unresolved supertypes: org.junit.rules.TestRule

Has anyone an idea how I can execute the tests with a testcontainer via IDE?

With best regards
Henni

Up. Are there any upcoming news? We're looking forward to get rid of junit4 completely in our company test dependencies. And the only one is testcontainers (which brings that legacy into our test class path).

P.S. I ❀️ testcontainers, this library helped to move testing to another level.

I have created a pull request for this #5826

kiview bsideup i would want to fix this problem:

  • use junit5 in tests
  • add a new rule in checkstyle - forbid use junit4

is it correct? or do you have another opinion?

Hi @bmuschko,

We very much agree, and we do plan to remove the dependency, but it will be a breaking change and we will do it in Testcontainers 2.0. We were planning to start 2.0 earlier but discovered a few low hanging fruits (like the JUnit 5 extension, or OkHttp transport), this is why it got delayed. But now probably is a good time to finally focus on 2.0 :)

/cc @rnorth @kiview

More than four years have gone since that comment. Still there.

Hello @detouched and everyone else,

your workaround works fine for my gradle build. When I try to execute the tests with Intellij there is always coming the error below:

Supertypes of the following classes cannot be resolved. Please make sure you have the required dependencies in the classpath:
class org.testcontainers.containers.FailureDetectingExternalResource, unresolved supertypes: org.junit.rules.TestRule

Has anyone an idea how I can execute the tests with a testcontainer via IDE?

With best regards Henni

That happened to me when adding those classes as Kotlin code and fixed when added as Java code. I copied the following classes from JUnit 4 repository to my repository:

I had to add also org.awaitility:awaitility dependency.

Hi @bmuschko,

We very much agree, and we do plan to remove the dependency, but it will be a breaking change and we will do it in Testcontainers 2.0. We were planning to start 2.0 earlier but discovered a few low hanging fruits (like the JUnit 5 extension, or OkHttp transport), this is why it got delayed. But now probably is a good time to finally focus on 2.0 :)

/cc @rnorth @kiview

Good plan! How long it will continue? Five years ago..

let's just acknowledge that testcontainers is a mediocre project where nobody cares about transitive dependencies or other minor things.

let's just acknowledge that testcontainers is a mediocre project where nobody cares about transitive dependencies or other minor things.

While above is a bit harsh, it is really regrettable that 5 years since there is absolutely no progress on this issue.
TestContainers should be on version 5 by now, but still we are "afraid" to release version 2.
Do not be afraid folks, let's move on from this and on to version 2..3.. 5...!

:( Another release of testcontainers, and yet it's not 2.0 :( :(

perlun commented

For reference, this has become an issue in the project I'm working on. Somehow, IntelliJ is picking up that the project has junit4 on the class path (?) and tries to load the junit-vintage-engine.

image

The workaround in my case was to add testFixturesApi "org.junit.vintage:junit-vintage-engine:5.9.0" to the Gradle config. It's a bit of an ugly hack, but posting this here in case it helps someone else.

Upstream IntelliJ issue: https://youtrack.jetbrains.com/issue/IDEA-233706/Failed-to-resolve-junit-vintage-engine-4.12.9-when-trying-to-run-JUnit-5-test#focus=Comments-27-8188989.0-0

Wish this could be resolved, testcontainers is the only part still pulling in Junit4 for us, it would be nice not having to tell junior developers anymore that half the test related annotations IntelliJ provides should not be used.

@void-spark If using Gradle, you can save your junior developers from themselves with

configurations.all {
    resolutionStrategy.dependencySubstitution {
        substitute(module("junit:junit"))
            .using(module("io.quarkus:quarkus-junit4-mock:3.0.0.Final"))
            .because(
                "We don't want JUnit 4; but is an unneeded transitive of testcontainers. " +
                    "See https://github.com/testcontainers/testcontainers-java/issues/970"
            )
    }
}

Which will yield

+--- org.testcontainers:testcontainers -> 1.17.6
|    +--- junit:junit:4.13.2 -> io.quarkus:quarkus-junit4-mock:3.0.0.Final
|    +--- \\ other dependencies

Since the "mock" doesn't contain all the common annotations/classes like @Test, @Before etc and only the handful of classes directly referred to by testcontainers, the completions are unlikely to be a problem.

And if you're worried about what's in it, can check out https://github.com/quarkusio/quarkus/tree/main/core/junit4-mock

Ugh. That's such an ugly hack :)
It's better to use Checkstyle, maven enforcer or ArchUnit for such things.

See https://stackoverflow.com/questions/61629824/preventing-the-use-of-junit4-libraries-in-a-project for some good answers

perlun commented

Ugh. That's such an ugly hack :)
It's better to use Checkstyle, maven enforcer or ArchUnit for such things.

In one way yes, but the nice part about the hack is that you don't even get @Test, @Before etc (as mentioned above) on your tests' classpath => editors like IntelliJ won't even attempt to suggest it to the user. In that way, it's more of a zeroconf approach, which is kind of nice.

I excluded JUnit 4 by adding the following to build.gradle

configurations {
    testCompile {
        exclude group: 'junit', module: 'junit'
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testRuntime {
        exclude group: 'junit', module: 'junit'
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

That may compile but doesn’t actually work.

Nope, still no 2.0, any timelines yet? :)

It's April 2024. It's time for 2.0 to fix this issue.This was opened in 2018, 6 years ago. Do you remember those days? No AI back then. No python. (oh wait, there was Python)

We also encountered this problem in our projects.

We couldn't find any junit4-mock dependency specifically for Testcontainers (besides the mock for quarkus mentioned above) so we simply wrote our own.
Feel free to use it :)

This is one of the things that always will be with us I think, I can not image how many times I bumped into something "junit4 vs junit5" - go solve your dependencies. It's been 7 years since Junit5 was released but the ghost will be us for a long time.

Let's get rid from 4, legacy will be legacy

erdi commented

I have to admit that I find it very surprising that this has not been addressed since Nov 2018 in a project with the impact, reach, success and motivation like Testcontainers.

Seems the only working solution for projects working with Testcontainers and disallowing Junit4 to guarantee that developers won't use junit4 in their tests is to use IllegalImport check of CheckStyle (https://checkstyle.org/checks/imports/illegalimport.html) and ban both junit4 specific packages:

  • junit.*
  • org.junit.matchers.*
  • org.junit.rules.*
  • org.junit.runner.*
  • org.junit.runners.*
  • org.junit.validator.*

and separately specific classes:

  • org.junit.After
  • org.junit.AfterClass
  • org.junit.Assert
  • org.junit.Before
  • org.junit.BeforeClass
  • org.junit.Rule
  • org.junit.Test

In my case I opted by excluding all JUnit 4 dependencies and copy some of those required interfaces in the test sources.

Definitely I don't love it, but it seemed to me this was the best approach to ensure we only work with JUnit 5.

Screenshot 2024-06-18 at 15 01 51

Seems the only working solution for projects working with Testcontainers and disallowing Junit4 to guarantee that developers won't use junit4 in their tests is to use IllegalImport check of CheckStyle (https://checkstyle.org/checks/imports/illegalimport.html) and ban both junit4 specific packages:

  • junit.*
  • org.junit.matchers.*
  • org.junit.rules.*
  • org.junit.runner.*
  • org.junit.runners.*
  • org.junit.validator.*

and separately specific classes:

  • org.junit.After
  • org.junit.AfterClass
  • org.junit.Assert
  • org.junit.Before
  • org.junit.BeforeClass
  • org.junit.Rule
  • org.junit.Test