Skip to content
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

Testcontainers support for various containers does not work in a native-image #36606

Closed
magnus-larsson opened this issue Jul 27, 2023 · 11 comments
Assignees
Labels
theme: aot An issue related to Ahead-of-time processing theme: containers Testcontainers and Docker Compose features type: bug A general bug
Milestone

Comments

@magnus-larsson
Copy link

magnus-larsson commented Jul 27, 2023

I have put together a minimal example that demonstrates how the use of the @ServiceConnection annotation with Spring Boot 3.1.2 and testcontainers breaks native tests.

When running ./gradlew nativeTest, I get the following error:

org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsNotFoundException: No ConnectionDetails found for source '@ServiceConnection source for MongoDbTestBase.database'

To reproduce the problem, run the following commands:

git clone https://github.com/magnus-larsson/sb312-tc-native-bug.git
cd sb312-tc-native-bug
./gradlew nativeTest

Note: Running ./gradlew test works as expected.

To workaround the problem, do the following:

  1. Edit src/test/java/se/magnus/microservices/core/product/MongoDbTestBase.java
  2. Comment out @ServiceConnection
  3. Remove comments from the method setProperties that is annotated with @DynamicPropertySource

With these changes in place, both ./gradlew test and ./gradlew nativeTest works as expected.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jul 27, 2023
@philwebb

This comment was marked as outdated.

@philwebb philwebb added the status: waiting-for-feedback We need additional information before we can continue label Jul 27, 2023
@magnus-larsson
Copy link
Author

The git repo is available at https://github.com/magnus-larsson/sb312-tc-native-bug.

I noted that the issue is marked with status:waiting-for-feedback, is there any more info required from my side?

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Jul 27, 2023
@wilkinsona wilkinsona removed the status: feedback-provided Feedback has been provided label Jul 27, 2023
@snicoll snicoll added the for: team-attention An issue we'd like other members of the team to review label Aug 3, 2023
@mhalbritter mhalbritter added type: bug A general bug theme: aot An issue related to Ahead-of-time processing and removed for: team-attention An issue we'd like other members of the team to review status: waiting-for-triage An issue we've not yet triaged labels Aug 8, 2023
@mhalbritter mhalbritter added this to the 3.1.x milestone Aug 8, 2023
@mhalbritter
Copy link
Contributor

mhalbritter commented Aug 8, 2023

Hey @magnus-larsson, sorry for taking my time to look at this issue. Thanks for the report and for the reproducer. You don't need to provide more information, we have everything we need.

I can reproduce this. The 3 registrations are returned in org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories#getConnectionDetails:

List<Registration<S, ?>> registrations = getRegistrations(source, required);

but this call always returns null in a native image:

ConnectionDetails connectionDetails = registration.factory().getConnectionDetails(source);

On the JVM this works for the MongoContainerConnectionDetailsFactory.

@mhalbritter
Copy link
Contributor

The MongoContainerConnectionDetailsFactory checks for the presence of the com.mongodb.ConnectionString class, which is not registered for reflection in a native image, and this check returns false.

@mhalbritter
Copy link
Contributor

mhalbritter commented Aug 8, 2023

Until we fix this, you can use this workaround:

@ImportRuntimeHints(MyHints.class)
public abstract class MongoDbTestBase {

  // ... snip ...

	static class MyHints implements RuntimeHintsRegistrar {
		@Override
		public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
			hints.reflection().registerType(TypeReference.of("com.mongodb.ConnectionString"));
		}
	}

}

@mhalbritter mhalbritter changed the title Spring Boot's @ServiceConnection annotation breaks native tests Testcontainers support doesn't work in a native-image Aug 8, 2023
@mhalbritter mhalbritter added the theme: containers Testcontainers and Docker Compose features label Aug 8, 2023
@scottfrederick scottfrederick changed the title Testcontainers support doesn't work in a native-image Testcontainers support for MongoDB does not work in a native-image Aug 22, 2023
@magnus-larsson
Copy link
Author

It looks like Spring Boot 3.1.4 solved this issue.

I don't need the workaround described above to make nativeTests work after upgrading to Spring Boot 3.1.4.

@wilkinsona
Copy link
Member

Thanks for the update, @magnus-larsson.

I'm surprised that it works for you with Spring boot 3.1.4. As far as I can tell, nothing is generating the reflection hint for com.mongodb.ConnectionString so it shouldn't be available through reflection in a native image.

Updating sb312-tc-native-bug to Spring Boot 3.1.4 and running ./gradlew nativeTest seems to confirm this. All 12 tests fail with exceptions similar to this:

Caused by: org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsNotFoundException: No ConnectionDetails found for source '@ServiceConnection source for MongoDbTestBase.database'
        at org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactories.getConnectionDetails(ConnectionDetailsFactories.java:89)
        at org.springframework.boot.testcontainers.service.connection.ConnectionDetailsRegistrar.registerBeanDefinitions(ConnectionDetailsRegistrar.java:71)
        at org.springframework.boot.testcontainers.service.connection.ConnectionDetailsRegistrar.lambda$registerBeanDefinitions$0(ConnectionDetailsRegistrar.java:66)
        at java.base@17.0.8/java.util.ArrayList.forEach(ArrayList.java:1511)
        at org.springframework.boot.testcontainers.service.connection.ConnectionDetailsRegistrar.registerBeanDefinitions(ConnectionDetailsRegistrar.java:66)
        at org.springframework.boot.testcontainers.service.connection.ServiceConnectionContextCustomizer.customizeContext(ServiceConnectionContextCustomizer.java:66)
        at org.springframework.boot.test.context.SpringBootContextLoader$ContextCustomizerAdapter.initialize(SpringBootContextLoader.java:435)
        at org.springframework.boot.SpringApplication.applyInitializers(SpringApplication.java:610)
        at org.springframework.boot.SpringApplication.prepareContext(SpringApplication.java:390)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
        at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137)
        at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58)
        at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46)
        at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1409)
        at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:545)
        at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137)
        at org.springframework.boot.test.context.SpringBootContextLoader.loadContextForAotRuntime(SpringBootContextLoader.java:119)
        at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInAotMode(DefaultCacheAwareContextLoaderDelegate.java:217)
        at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
        ... 68 common frames omitted

I think we still need to contribute a hint here.

@wilkinsona
Copy link
Member

Looking at the code, I think the factories for Neo4j, Redis, and Zipkin as well as the various different R2DBC factories are also affected.

@magnus-larsson
Copy link
Author

@wilkinsona: You are right; I tested it on another project. Native tests still fail in the sb312-tc-native-bug project when using Spring Boot 3.1.4!

@mhalbritter mhalbritter changed the title Testcontainers support for MongoDB does not work in a native-image Testcontainers support for various containers does not work in a native-image Oct 25, 2023
@mhalbritter mhalbritter self-assigned this Oct 25, 2023
@mhalbritter mhalbritter modified the milestones: 3.1.x, 3.1.6 Oct 25, 2023
@wilkinsona
Copy link
Member

I think this has caused #38392.

@wilkinsona wilkinsona reopened this Nov 20, 2023
@philwebb philwebb self-assigned this Nov 20, 2023
philwebb added a commit that referenced this issue Nov 21, 2023
Update `ContainerConnectionDetailsFactory` hint registration logic
so that types are optional on the classpath.

See gh-36606
Fixes gh-38392
@philwebb
Copy link
Member

The fix for #38392 was merged in f68df82

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
theme: aot An issue related to Ahead-of-time processing theme: containers Testcontainers and Docker Compose features type: bug A general bug
Projects
None yet
Development

No branches or pull requests

6 participants