Вопрос или проблема
Я новичок в 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
. Давайте разберемся в причинах этой проблемы и в том, как ее можно решить.
Основные ошибки в конфигурации
-
Несоответствие зависимостей: В вашем
build.gradle
файле есть некоторые зависимости, которые могут конфликтовать или быть некорректными. В частности, вы указываете разные версии Spring Boot: в строке с плагином вы используете3.2.8
, а в зависимости для валидации3.3.2
. Эти версии могут быть несовместимыми. Рекомендуется использовать одну и ту же версию для всех зависимостей Spring Boot. -
Проблемы с встраиваемой базой данных: Судя по ошибке, причина может быть в том, что Spring пытается использовать встроенную базу данных для тестов, но не может ее найти. В вашем
build.gradle
файле есть зависимостьcom.h2database:h2
, которая подходит для использования в тестах. Однако возможно, что Spring не может найти эту зависимость.
Рекомендации по исправлению
-
Убедитесь, что версии зависимостей последовательны:
Замените строки в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'
-
Настройка тестов: Убедитесь, что ваш тестовый класс явно настраивает использование H2 базы данных. Добавьте следующую аннотацию в
UserRepositoryTest
:@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY)
Это гарантирует, что Spring будет использовать встроенную базу данных H2 вместо ожидаемого DataSource.
-
Проверка конфигурационных файлов: Убедитесь, что конфигурационные файлы находятся в правильных местах. Файл
application-test.properties
должен быть вsrc/test/resources
, и его настройки должны соответствовать используемой базе данных. -
Обновление кода: Возможно, вам потребуется заменить часть кода в методе
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. Убедитесь, что все зависимости и настройки соответствуют друг другу. Если возникнут дополнительные вопросы, я буду рад помочь!