-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Expand documentation about Singleton Containers and lambdas #6587
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
base: main
Are you sure you want to change the base?
Changes from all commits
b4a1ee9
0676d26
cdbf286
95bdf11
b1dbb4e
182c201
7b1589a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
# Manual container lifecycle control | ||
|
||
While Testcontainers was originally built with JUnit 4 integration in mind, it is fully usable with other test | ||
While Testcontainers was originally built with JUnit 4 integration in mind, it is fully usable with other test | ||
frameworks, or with no framework at all. | ||
|
||
## Manually starting/stopping containers | ||
|
@@ -13,7 +13,7 @@ try (GenericContainer container = new GenericContainer("imagename")) { | |
container.start(); | ||
// ... use the container | ||
// no need to call stop() afterwards | ||
} | ||
} | ||
``` | ||
|
||
## Singleton containers | ||
|
@@ -48,3 +48,79 @@ The singleton container is started only once when the base class is loaded. | |
The container can then be used by all inheriting test classes. | ||
At the end of the test suite the [Ryuk container](https://github.com/testcontainers/moby-ryuk) | ||
that is started by Testcontainers core will take care of stopping the singleton container. | ||
|
||
Please keep in mind that putting the container in a static block will influence the configuration of the container. | ||
You will not be able to use methods such as `forResponsePredicate` by just providing a Lambda expression, you will have to use | ||
anonymous classes, or alternatively provide them from non-abstract class. | ||
This is not due to the limitation of `TestContainers`, but because of how they work under the hood, namely that lambdas get compiled | ||
to static methods on a class level. Since your container is in a static block, the container gets created | ||
before your parent and children classes get initialized and as such you cannot pass the reference to them. | ||
|
||
Therefore, once again - it is advised to use anonymous classes in such case or full predicates coming in from different, non-abstract class. | ||
|
||
Instead of: | ||
|
||
```java | ||
abstract class AbstractContainerBaseTest { | ||
|
||
static final GenericContainer GENERIC_CONTAINER; | ||
|
||
static { | ||
GENERIC_CONTAINER = new GENERIC_CONTAINER( | ||
new ImageFromDockerfile().waitingFor(Wait.forHttp('/path')) | ||
.forStatusCode(200) | ||
.forResponsePredicate(yourLambda -> yourLambda(here)) //This is never going to get executed due to NoClassDefFoundError | ||
); | ||
GENERIC_CONTAINER.start(); | ||
} | ||
} | ||
``` | ||
|
||
You can do an anonymous class: | ||
|
||
```java | ||
abstract class AbstractContainerBaseTest { | ||
|
||
static final GenericContainer GENERIC_CONTAINER; | ||
|
||
static { | ||
GENERIC_CONTAINER = new GENERIC_CONTAINER( | ||
new ImageFromDockerfile().waitingFor(Wait.forHttp('/path')) | ||
.forStatusCode(200) | ||
.forResponsePredicate(new Predicate<String>() { | ||
|
||
@Override | ||
public boolean test(String s) { | ||
return yourConditionHere; | ||
} | ||
}) | ||
); | ||
|
||
GENERIC_CONTAINER.start(); | ||
} | ||
} | ||
``` | ||
Or full predicate coming in from different class: | ||
|
||
```java | ||
abstract class AbstractContainerBaseTest { | ||
|
||
static final GenericContainer GENERIC_CONTAINER; | ||
|
||
static { | ||
GENERIC_CONTAINER = new GENERIC_CONTAINER( | ||
new ImageFromDockerfile().waitingFor(Wait.forHttp('/path')) | ||
.forStatusCode(200) | ||
.forResponsePredicate(PredicateHolder.getPredicate()) | ||
); | ||
GENERIC_CONTAINER.start(); | ||
} | ||
} | ||
|
||
public class PredicateHolder { | ||
|
||
public static Predicate<String> getPredicate() { | ||
return yourLambda -> yourLambda(here); | ||
} | ||
} | ||
``` | ||
Comment on lines
+52
to
+126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is an issue reported in JDK which describe what's happening here. So, static methods from other classes can not be called from a lambda in a static block. But, if those are used as a method reference then it will work. Also, if a container class is created then this issue can be avoided we would encourage to do that class HWContainer extends GenericContainer<HWContainer> {
public HWContainer() {
super("testcontainers/helloworld");
withExposedPorts(8080);
waitingFor(Wait.forHttp("/ping").forResponsePredicate(x -> Util.isContains(x)));
}
} I would like to make a note about this and give the proper context and recommendations. Also, Can you please move the inline code to actual code and use codeblock in the file to display them? I think code examples would be avoided if the note is quite clear. Note: The project is Testcontainers not TestContainers :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey! Thanks for the response. I do firmly believe that this is not about deadlocking per se (even though somewhat related), but rather that static block gets initialized before the initialization of a class - and since the lambda gets compiled to a static field, trying to reference something that does not exist is impossible. The reason why I am thinking that is we will not even get to the class initialization yet, as the container is just getting started. Could you point me in the direction why do you believe that this is the deadlock situation? Also, when you respond I will:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A stray format, will revert.