I am using currently TestContainers singleton pattern in order to reuse my db across all JVM tests and I ended up using an enum for it. So I was wondering if it was possible to extend the @DynamicPropertySource so it can be used at class level on something like this:
@DynamicPropertySource(TestContainers.POSTGRES)
public class MyFlowIT { ... }
The updated annotation's "values"/default property (or a new explicit one) would have to be a new interface close to something like Consumer<DynamicPropertyRegistry>
, since the very same consumer is illegal as an annotation attribute according to spec.
That would allow us to save from having to define the following in each test,
@DynamicPropertySource
static void postgresProperties(DynamicPropertyRegistry registry) {
TestContainers.POSTGRES.registerProperties(registry);
}
For instance, what about accepting an interface like:
interface DynamicPropertyRegistrable {
public void registerProperties(DynamicPropertyRegistry dynamicPropertyRegistry) ;
}
Then the enum would be something like:
public enum TestContainers implements DynamicPropertyRegistrable {
POSTGRES ("POSTGRES", new PostgresInitializer()),
KAFKA ("KAFKA", new KafkaInitializer()),
ACTIVEMQ ("ACTIVEMQ", new ActiveMQInitializer()),
OPENSEARCH("OPENSEARCH", new OpenSearchInitialiser());
@Override
public void registerProperties(DynamicPropertyRegistry dynamicPropertyRegistry) {
this.propertyRegistrar.forEach((property,supplier) -> dynamicPropertyRegistry.add(property, supplier));
}
(...)
Then we could use it as @DynamicPropertySource(TestContainers.POSTGRES). I would really love to save me from defining the register section at the beginning of each of my 300 ITs and annotation composition is less problematic and more elegant than abstracting in parents expressly. With my suggested way, we could also include the db setup as part of a meta-annotation with other annotations. Wouldn't rock something like this?:
@IntegrationTest
public class MyFlowIT { ... }
where my custom annotation would be something like:
@DynamicPropertySource(TestContainers.POSTGRES)
@TestInstance(LifeCycle.PER_CLASS)
@Sql(scripts = "/db/scenarios/common/CleanDB.sql", executionPhase = AFTER_TEST_METHOD)
@SqlMergeMode(SqlMergeMode.MergeMode.MERGE)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface IntegrationTest {}
Comment From: wilkinsona
In the interests of avoiding wasted time, this was originally proposed in the Spring Boot issue tracker. @nightswimmings, making the same proposal here without first addressing the problems that Phil and I already described is unlikely to be an efficient use of everyone's time.
Comment From: nightswimmings
@wilkinsona
I did not understand your comment on the Spring Boot tracker, I assumed you meant that current annotation did not have an attribute specified for it! ( it couldn't be used as the type of the annotation's attribute as @DynamicPropertySource knows nothing about it.) I understood from Phil that I cannot use a Consumer
I am really sorry if what you were trying to tell me is that this approach is impossible by spec. Is this then not feasible?
public @interface DynamicPropertySource {
public DynamicPropertyRegistrable value();
}
Comment From: nightswimmings
And still, I believe there must be a way to do it, by designing the @DynamicPropertySource in a way that it tries to cast the provided instance at runtime.
Comment From: wilkinsona
Is this then not feasible?
No, unfortunately not. If you try it for yourself, you'll see that it does not compile.
It would compile if the attribute was a enum such as your Testcontainers
enum. However, @DynamicPropertySource
can't depend on an application-specific enum. It doesn't know anything about such an enum, and nor should it.
You may be able to implement something similar to what you want by making the attribute a Class<? extends DynamicPropertyRegistrable>
. The test framework would then create an instance of this class and call registerProperties
. However, you wouldn't be able to use your Testcontainers
enum in this case as the test framework would have no way of knowing how to create the instance, i.e. which constant on the enum to use.
Comment From: nightswimmings
Thanks for your patience, @wilkinsona. I really see no creative solution for this, after all your explanations, so I guess we can close it