Search code examples
javaspringspring-bootjunit-jupiterdata-jpa-test

Upgrade from SpringBoot 2.6 -> 2.7 - No qualifying bean of type 'org.springframework.boot....TestEntityManager' available


I updated from SpringBoot 2.6.12 to 2.7.11 and as a consequence our RepositoryTests were not working anymore. The @autowired TestEntityManager can't be filled by a qualifying bean after the upgrade. I tried a lot of solutions from other threads and websites but most of them were quite old and it seems like the solution for 2.7 has to be a different one.

Furthermore we were using H2 as embedded database before and the error was mostly the same, so the root-problem seems not be related to the new embedded test DB we use now (Zonky).

I would be very grateful for help.

RepositoryTest:

package eu.X.api.pod.repository;

import eu.X.api.pod.model.PodUrlEntity;
import eu.X.api.pod.repository.PodRepository;
import io.zonky.test.db.AutoConfigureEmbeddedDatabase;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.TestPropertySource;

import java.util.Optional;

import static io.zonky.test.db.AutoConfigureEmbeddedDatabase.DatabaseProvider.ZONKY;

@DataJpaTest
@AutoConfigureEmbeddedDatabase(provider = ZONKY)
@TestPropertySource(locations = "classpath:application-test.properties")
class PodRepositoryTest {

    @Autowired
    private TestEntityManager entityManager;
    @Autowired
    private PodRepository podRepository;

    @Test
    void whenFindUrlByTrackIdAndUniqueContactId_thenReturnUrl() {
        // given
        PodUrlEntity podUrlEntity = PodUrlEntity.builder()
                                                .trackId("12345678")
                                                .contactId("276a15LnuT")
                                                .url("de-200/2022/01/10/0170/img_de-200_20220110092858_10815_STP-DE-200-DPKC-5001.png")
                                                .build();
        entityManager.persist(podUrlEntity);
        entityManager.flush();

        // when
        Optional<String> found = podRepository.findUrlByTrackIdAndUniqueContactId(podUrlEntity.getTrackId(),
                                                                                  podUrlEntity.getContactId());
        // then
        assertEquals(podUrlEntity.getUrl(), found.get());
    }

}

Stacktrace:


> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :compileTestJava UP-TO-DATE
> Task :processTestResources UP-TO-DATE
> Task :testClasses UP-TO-DATE
> Task :test FAILED
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/Users/X/.gradle/caches/modules-2/files-2.1/eu.X.X.lib.spring/lib-spring-web/1.2.0/8a53726f79df95d1695115732c517efba0aca950/lib-spring-web-1.2.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/Users/X/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.2.11/4741689214e9d1e8408b206506cbe76d1c6a7d60/logback-classic-1.2.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
13:39:24.980 [Test worker] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
13:39:24.987 [Test worker] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
13:39:25.016 [Test worker] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [eu.X.X.api.pod.repository.PodRepositoryTest] from class [org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper]
13:39:25.024 [Test worker] INFO org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [eu.X.X.api.pod.repository.PodRepositoryTest], using SpringBootContextLoader
13:39:25.027 [Test worker] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [eu.X.X.api.pod.repository.PodRepositoryTest]: class path resource [eu/X/X/api/pod/repository/PodRepositoryTest-context.xml] does not exist
13:39:25.027 [Test worker] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [eu.X.X.api.pod.repository.PodRepositoryTest]: class path resource [eu/X/X/api/pod/repository/PodRepositoryTestContext.groovy] does not exist
13:39:25.027 [Test worker] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [eu.X.X.api.pod.repository.PodRepositoryTest]: no resource found for suffixes {-context.xml, Context.groovy}.
13:39:25.029 [Test worker] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [eu.X.X.api.pod.repository.PodRepositoryTest]: PodRepositoryTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
13:39:25.077 [Test worker] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an 'annotation declaring class' for annotation type [org.springframework.test.context.ActiveProfiles] and class [eu.X.X.api.pod.repository.PodRepositoryTest]
13:39:25.119 [Test worker] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [C:\Users\X\Desktop\X-microservices-kafka\X-pod-service\build\classes\java\main\eu\X\X\api\pod\PodServiceApplication.class]
13:39:25.120 [Test worker] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration eu.X.X.api.pod.PodServiceApplication for test class eu.X.X.api.pod.repository.PodRepositoryTest
13:39:25.122 [Test worker] DEBUG org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper - @TestExecutionListeners is not present for class [eu.X.X.api.pod.repository.PodRepositoryTest]: using defaults.
13:39:25.122 [Test worker] INFO org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.event.ApplicationEventsTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener, io.zonky.test.db.EmbeddedDatabaseTestExecutionListener, io.zonky.test.db.event.EventPublishingTestExecutionListener, io.zonky.test.db.flyway.OptimizedFlywayTestExecutionListener]
13:39:25.138 [Test worker] INFO org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@4c168660, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@52b56a3e, org.springframework.test.context.event.ApplicationEventsTestExecutionListener@fd0e5b6, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@4eed46ee, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@36b0fcd5, org.springframework.test.context.support.DirtiesContextTestExecutionListener@4fad94a7, io.zonky.test.db.EmbeddedDatabaseTestExecutionListener@475835b1, io.zonky.test.db.flyway.OptimizedFlywayTestExecutionListener@6326d182, org.springframework.test.context.transaction.TransactionalTestExecutionListener@5241cf67, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@716a7124, org.springframework.test.context.event.EventPublishingTestExecutionListener@77192705, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@226642a5, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@7e809b79, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@5cc126dc, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@625e134e, org.springframework.boot.test.autoconfigure.webservices.client.MockWebServiceServerTestExecutionListener@72bd06ca, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@89c10b7, io.zonky.test.db.event.EventPublishingTestExecutionListener@5dbe30be]
13:39:25.141 [Test worker] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: context [DefaultTestContext@3d8bbcdc testClass = PodRepositoryTest, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@52500920 testClass = PodRepositoryTest, locations = '{}', classes = '{class eu.X.X.api.pod.PodServiceApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{classpath:application-test.properties}', propertySourceProperties = '{org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@3a1dd365, org.springframework.boot.test.autoconfigure.actuate.metrics.MetricsExportContextCustomizerFactory$DisableMetricExportContextCustomizer@315f43d5, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@351584c0, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@a0d8ffa4, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@4c4748bf, [ImportsContextCustomizer@117e0fe5 key = []], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@20f12539, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@13cf7d52, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, io.zonky.test.db.EmbeddedDatabaseContextCustomizerFactory$EmbeddedDatabaseContextCustomizer@8a4aa01f, org.springframework.boot.test.context.SpringBootTestArgs@1, org.springframework.boot.test.context.SpringBootTestWebEnvironment@0], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]], class annotated with @DirtiesContext [false] with mode [null].

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                        

2022-10-14 13:39:25.459  INFO 22352 --- [    Test worker] e.g.s.a.p.repository.PodRepositoryTest   : Starting PodRepositoryTest using Java 17.0.4.1 on DE03-0101WL6NL6 with PID 22352 (started by X in C:\Users\X\Desktop\X-microservices-kafka\X-pod-service)
2022-10-14 13:39:25.460  INFO 22352 --- [    Test worker] e.g.s.a.p.repository.PodRepositoryTest   : No active profile set, falling back to 1 default profile: "default"
2022-10-14 13:39:25.684  INFO 22352 --- [    Test worker] EmbeddedDatabaseContextCustomizerFactory : Replacing 'dataSource' DataSource bean with embedded version
2022-10-14 13:39:25.757  INFO 22352 --- [    Test worker] trationDelegate$BeanPostProcessorChecker : Bean 'io.zonky.test.db.config.EmbeddedDatabaseAutoConfiguration' of type [io.zonky.test.db.config.EmbeddedDatabaseAutoConfiguration$$EnhancerBySpringCGLIB$$6fc806ea] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2022-10-14 13:39:25.867  INFO 22352 --- [    Test worker] e.g.s.a.p.repository.PodRepositoryTest   : Started PodRepositoryTest in 0.704 seconds (JVM running for 1.58)



============================
CONDITIONS EVALUATION REPORT
============================


Positive matches:
-----------------

   EmbeddedDatabaseContextCustomizerFactory.PrimaryDataSourceAutoConfiguration matched:
      - NoneNestedConditions 0 matched 1 did not; NestedCondition on EmbeddedDatabaseCondition.EmbeddedDatabaseDisabled @ConditionalOnProperty (zonky.test.database.replace=none) did not find property 'replace' (EmbeddedDatabaseCondition)

   EmbeddedDatabaseContextCustomizerFactory.PrimaryDataSourceAutoConfiguration#embeddedDataSource1 matched:
      - @ConditionalOnMissingBean (types: javax.sql.DataSource; SearchStrategy: all) did not find any beans (OnBeanCondition)


Negative matches:
-----------------

    None


Exclusions:
-----------

    None


Unconditional classes:
----------------------

    None



2022-10-14 13:39:25.891 ERROR 22352 --- [    Test worker] o.s.test.context.TestContextManager      : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@36b0fcd5] to prepare test instance [eu.X.X.api.pod.repository.PodRepositoryTest@fe7b6b0]

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'eu.X.X.api.pod.repository.PodRepositoryTest': Unsatisfied dependency expressed through field 'entityManager'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:659) ~[lib-spring-web-1.2.0.jar:na]

--- Rest is ommited


PodRepositoryTest > whenFindUrlByUnitNoAndUniqueContactId_thenReturnUrl() FAILED
    org.springframework.beans.factory.UnsatisfiedDependencyException at AutowiredAnnotationBeanPostProcessor.java:659
        Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException at DefaultListableBeanFactory.java:1799
PodRepositoryTest > whenFindUrlByTrackIdAndUniqueContactId_thenReturnUrl() FAILED
    org.springframework.beans.factory.UnsatisfiedDependencyException at AutowiredAnnotationBeanPostProcessor.java:659
        Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException at DefaultListableBeanFactory.java:1799
PodRepositoryTest > whenFindTrackIdByUnitNoAndUniqueContactId_thenReturnTrackId() FAILED
    org.springframework.beans.factory.UnsatisfiedDependencyException at AutowiredAnnotationBeanPostProcessor.java:659
        Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException at DefaultListableBeanFactory.java:1799
3 tests completed, 3 failed
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///C:/Users/X/Desktop/X-microservices-kafka/X-pod-service/build/reports/tests/test/index.html
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 2s
5 actionable tasks: 1 executed, 4 up-to-date

build.gradle:

plugins {
    id 'java'
    id 'org.springframework.boot' version '2.7.4'
    id 'io.spring.dependency-management' version '1.0.14.RELEASE'
}

group = 'eu.X.X.api'
version = System.getProperty("VERSION")
sourceCompatibility = JavaVersion.VERSION_17

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    maven {
        url 'http://X-nexus01.dc.X:8081/repository/maven-releases'
        allowInsecureProtocol = true
    }
    maven {
        url 'http://X-nexus01.dc.X:8081/repository/maven-public'
        allowInsecureProtocol = true
    }
    maven {
        url 'http://X-nexus01.dc.X:8081/repository/confluent.io-proxy'
        allowInsecureProtocol = true
    }
}

ext {
    jUnitVersion = '5.9.1'
    lombokVersion = '1.18.24'
    awsSdkVersion = '2.17.276'
}
dependencies {
    // X
    implementation group: 'eu.X.X.lib.spring', name: 'lib-spring-web', version: '1.2.0'
    // Spring Boot
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web'
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa'
    implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator'
    testImplementation group: 'org.springframework.boot', name: 'spring-boot-starter-test'
    annotationProcessor group: 'org.springframework.boot', name: 'spring-boot-configuration-processor'
    // Apache
    implementation group: 'org.apache.commons', name: 'commons-pool2', version: '2.11.1'
    implementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.13'
    implementation group: 'org.apache.pdfbox', name: 'pdfbox', version: '2.0.26'
    // Amazon
    implementation group: 'software.amazon.awssdk', name: 's3', version: "${awsSdkVersion}"
    implementation group: 'software.amazon.awssdk', name: 'apache-client', version: "${awsSdkVersion}"
    implementation group: 'com.amazonaws', name: 'aws-java-sdk-s3', version: '1.12.305'
    // JUnit
    testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: "${jUnitVersion}"
    testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: "${jUnitVersion}"
    testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: "${jUnitVersion}"
    // Lombok
    compileOnly group: 'org.projectlombok', name: 'lombok', version: "${lombokVersion}"
    annotationProcessor group:'org.projectlombok', name:'lombok', version: "${lombokVersion}"
    // Zonky
    implementation enforcedPlatform( group: 'io.zonky.test.postgres', name: 'embedded-postgres-binaries-bom', version:'14.5.0')
    testImplementation group: 'io.zonky.test', name: 'embedded-database-spring-test', version:'2.1.2'
    testRuntimeOnly group: 'io.zonky.test', name: 'embedded-postgres', version:'2.0.1'

    // Miscellaneous
    implementation group: 'org.zalando', name: 'logbook-spring-boot-starter', version: '2.14.0'
    implementation group: 'org.postgresql', name: 'postgresql', version: '42.5.0'
    implementation group: 'com.lowagie', name:'itext', version: '2.1.7'
    implementation group: 'net.sf.jasperreports', name:'jasperreports', version: '6.20.0'
    implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.11'
    implementation group: 'com.auth0', name: 'java-jwt', version: '4.0.0'
    testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.4.0'
}

test {
    useJUnitPlatform()
}

--- Rest is ommited


Solution

  • I fixed the issue myself after a lot of trial and error and research.

    The solution I found was to switch from @DataJpaTest to @SpringBootTest and to remove the @Autowired TestEntityManager. To persist test data I now use the repository.save() method of the @Autowired repositories.

    I created dummy repositories as sub-classes of the test class if I needed to save a test entity for which I do not have a real repository with queries in it.

    The usage of repository.save() has the side effect that the persisted (saved) entities now can't be asserted for equality anymore because they wont be the same object (memory address) when the repository returns them after a query-method is called in the test.

    My solution for that was to use AssertJ's assertThat(X).usingRecursiveComparison().isEqualTo(X) instead of JUnits assertEquals(X, X) for these cases.

    I hope this helps anyone. :)