Неудовлетворительные тестовые случаи Spring Boot – java.lang.IllegalStateException: Не удалось загрузить ApplicationContext.

Вопрос или проблема

Я новичок в Spring Boot и сейчас тестирую метод findByUsername в своем репозитории. Я отлаживаю это уже несколько часов. Какие осложнения возникли в моих конфигурациях и почему?

build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.8'
    id 'io.spring.dependency-management' version '1.1.6'
}

version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-validation:3.3.2'
    implementation 'org.springframework.boot:spring-boot-devtools:3.2.0'
    implementation 'mysql:mysql-connector-java:8.0.28'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testRuntimeOnly 'com.h2database:h2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
    useJUnitPlatform()
}

src/test/resources/application-test.properties

spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=LEGACY
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=sa

spring.jpa.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true

src/test/java///*/repository/UserRepositoryTest.java**

package ***.*******.*******.repository;

import static org.assertj.core.api.Assertions.assertThat;

import java.time.LocalDateTime;
import java.util.Optional;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

import com.crawler.backend.model.User;

@DataJpaTest
public class UserRepositoryTest {
    @Autowired
    private UserRepository userRepository;
    User user;

    @BeforeEach
    void setUp() {
        user = new User("test", "test");
        user.setUserDateCreated(LocalDateTime.now());
        userRepository.save(user);
    }

    @AfterEach
    void tearDown() {
        user = null;
        userRepository.deleteAll();
    }

    @Test
    void testFindByUsername_Found() throws Exception {
        Optional<User> existingUser = userRepository.findByUsername("test");
        assertThat(existingUser.isPresent()).isTrue();
        assertThat(existingUser.get().getUserId()).isEqualTo(user.getUserId());
        assertThat(existingUser.get().getUsername()).isEqualTo(user.getUsername());
    }

    @Test
    void testFindByUsername_NotFound() throws Exception {
        Optional<User> existingUser = userRepository.findByUsername("test1");
        assertThat(existingUser.get()).isEqualTo(null);
    }
}

результат теста

java.lang.IllegalStateException: Не удалось загрузить ApplicationContext для [MergedContextConfiguration@4487c0c2 testClass = com.crawler.backend.repository.UserRepositoryTest, locations = [], classes = [com.crawler.backend.BackendApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceDescriptors = [], propertySourceProperties = ["org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory$DisableAutoConfigurationContextCustomizer@209da20d, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.filter.TypeExcludeFiltersContextCustomizer@34be3d80, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@dbbec68d, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizer@59505b48, [ImportsContextCustomizer@126f1ba8 key = [org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration, org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManagerAutoConfiguration, org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration, org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration, org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration, org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration, org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcClientAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@248e319b, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@3e8c3cb, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.reactor.netty.DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactory$DisableReactorResourceFactoryGlobalResourcesContextCustomizerCustomizer@4b29d1d2, org.springframework.boot.test.context.SpringBootTestAnnotation@14ad81ff], contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]
 at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:180)
 at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.getApplicationContext(DefaultTestContext.java:130)
 at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:142)
 at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:98)
 at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.prepareTestInstance(TestContextManager.prepareTestInstance(TestContextManager.prepareTestInstance(TestContextManager.prepareTestInstance(TestContextManager.prepareTestInstance(TestContextManager.prepareTestInstance(TestContextManager.prepareTestInstance(… 17 more
Caused by: org.springframework.beans.factory.BeanCreationException: Ошибка создания бина с именем 'entityManagerFactory', определенного в классе HibernateJpaConfiguration.class: Не удалось инициализировать зависимость 'dataSourceScriptDatabaseInitializer' бина 'entityManagerFactory': Ошибка создания бина с именем 'dataSourceScriptDatabaseInitializer', определенного в классе DataSourceInitializationConfiguration.class: Неудовлетворенная зависимость, выраженная через параметр метода 'dataSourceScriptDatabaseInitializer' 0: Ошибка создания бина с именем 'dataSource': Не удалось заменить DataSource на встроенную базу данных для тестов. Если вы хотите встроенную базу данных, пожалуйста, добавьте поддерживаемую в classpath или настройте атрибут replace аннотации @AutoConfigureTestDatabase.
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:326)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205)
 at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:954)
 at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:625)
 at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
 at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)
 at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)
 at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.loadContext(SpringBootContextLoader.loadContext(SpringBootContextLoader.loadContext(SpringBootContextLoader.loadContext(SpringBootContextLoader.loadContext… 17 more
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Ошибка создания бина с именем 'dataSourceScriptDatabaseInitializer' определенного в классе DataSourceInitializationConfiguration.class: Неудовлетворенная зависимость, выраженная через метод 'dataSourceScriptDatabaseInitializer' параметр 0: Ошибка создания бина с именем 'dataSource': Не удалось заменить DataSource на встроенную базу данных для тестов. Если вы хотите встроенную базу данных, пожалуйста, добавьте ее в classpath или настройте атрибут replace аннотации @AutoConfigureTestDatabase.
 at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:795)
 at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:542)

 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1355)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
 at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337)
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:313)
 … 32 more
Caused by: org.springframework.beans.factory.BeanCreationException: Ошибка создания бина с именем 'dataSource': Не удалось заменить DataSource на встроенную базу данных для тестов. Если вы хотите встроенную базу данных, пожалуйста, добавьте поддерживаемую в classpath или настройте атрибут replace аннотации @AutoConfigureTestDatabase.
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1806)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
 at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337)
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
 at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:254)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.doResolveDependency(… 42 more
Caused by: java.lang.IllegalStateException: Не удалось заменить DataSource на встроенную базу данных для тестов. Если вы хотите встроенную базу данных, пожалуйста, добавьте поддерживаемую в classpath или настройте атрибут replace аннотации @AutoConfigureTestDatabase.
 at org.springframework.util.Assert.state(Assert.java:76)
 at org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration$EmbeddedDataSourceFactory.getEmbeddedDatabase(TestDatabaseAutoConfiguration.java:188)
 at org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration$EmbeddedDataSourceFactoryBean.afterPropertiesSet(TestDatabaseAutoConfiguration.java:153)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1853)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1802)
 … 53 more

Ответ или решение

Ваш вопрос касается ошибки, возникающей при загрузке контекста приложения в тестах Spring Boot, которая сопровождается исключением java.lang.IllegalStateException: Failed to load ApplicationContext. Давайте разберемся в причинах этой проблемы и в том, как ее можно решить.

Основные ошибки в конфигурации

  1. Несоответствие зависимостей: В вашем build.gradle файле есть некоторые зависимости, которые могут конфликтовать или быть некорректными. В частности, вы указываете разные версии Spring Boot: в строке с плагином вы используете 3.2.8, а в зависимости для валидации 3.3.2. Эти версии могут быть несовместимыми. Рекомендуется использовать одну и ту же версию для всех зависимостей Spring Boot.

  2. Проблемы с встраиваемой базой данных: Судя по ошибке, причина может быть в том, что Spring пытается использовать встроенную базу данных для тестов, но не может ее найти. В вашем build.gradle файле есть зависимость com.h2database:h2, которая подходит для использования в тестах. Однако возможно, что Spring не может найти эту зависимость.

Рекомендации по исправлению

  1. Убедитесь, что версии зависимостей последовательны:
    Замените строки в build.gradle, чтобы использовать одну и ту же версию Spring Boot:

    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.springframework.boot:spring-boot-devtools'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'com.h2database:h2'
  2. Настройка тестов: Убедитесь, что ваш тестовый класс явно настраивает использование H2 базы данных. Добавьте следующую аннотацию в UserRepositoryTest:

    @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)

    Это гарантирует, что Spring будет использовать встроенную базу данных H2 вместо ожидаемого DataSource.

  3. Проверка конфигурационных файлов: Убедитесь, что конфигурационные файлы находятся в правильных местах. Файл application-test.properties должен быть в src/test/resources, и его настройки должны соответствовать используемой базе данных.

  4. Обновление кода: Возможно, вам потребуется заменить часть кода в методе testFindByUsername_NotFound(), так как вызов existingUser.get() может привести к исключению NoSuchElementException, если пользователь не найден. Вместо этого используйте:

    assertThat(existingUser).isEmpty();

Пример исправленного класса тестирования

После внесения всех изменений ваш тестовый класс должен выглядеть примерно так:

package com.crawler.backend.repository;

import static org.assertj.core.api.Assertions.assertThat;

import java.time.LocalDateTime;
import java.util.Optional;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
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.jdbc.AutoConfigureTestDatabase;

import com.crawler.backend.model.User;

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
public class UserRepositoryTest {
    @Autowired
    private UserRepository userRepository;
    User user;

    @BeforeEach
    void setUp() {
        user = new User("test", "test");
        user.setUserDateCreated(LocalDateTime.now());
        userRepository.save(user);
    }

    @AfterEach
    void tearDown() {
        userRepository.deleteAll();
    }

    @Test
    void testFindByUsername_Found() {
        Optional<User> existingUser = userRepository.findByUsername("test");
        assertThat(existingUser).isPresent();
        assertThat(existingUser.get().getUserId()).isEqualTo(user.getUserId());
        assertThat(existingUser.get().getUsername()).isEqualTo(user.getUsername());
    }

    @Test
    void testFindByUsername_NotFound() {
        Optional<User> existingUser = userRepository.findByUsername("test1");
        assertThat(existingUser).isEmpty();
    }
}

Заключение

Следуя вышеописанным рекомендациям, вы сможете устранить возникшую проблему и корректно запустить тесты в Spring Boot. Убедитесь, что все зависимости и настройки соответствуют друг другу. Если возникнут дополнительные вопросы, я буду рад помочь!

Оцените материал
Добавить комментарий

Капча загружается...