I'd like to use Spring profile groups in fast ContextConfiguration unit tests.
Given the following application.yaml definition in test scope and profile configuration classes:
spring:
profiles:
default: test-default
group:
h2:
- test-default
- database
- h2
and configuration classes like
#Configuration
#EnableJpaRepositories(basePackageClasses = [OrderRepository::class] )
#EnableTransactionManagement
#EntityScan( basePackageClasses = [ OrderEntity::class])
#Profile("database")
class DatabaseConfiguration {}
I'd like to write a fast test with (#ContextConfiguration) e.g.
#ActiveProfiles("h2", "database", "test-default")
#ContextConfiguration( classes = [TestDefaultConfiguration::class])
internal class DbIngestionEventHandlerIntegrationTest()
I have to define both, h2 and database profiles, because ContextConfiguration does not load the application.yaml.
Is it possible to define spring profile groups programmatically? - so that they can be reused in tests?
Related
I am trying to write some tests for my application and encountered following problem:
I defined a application-test.yml with folling content:
server:
port: 8085
spring:
security:
oauth2:
resourceserver:
jwt:
# change localhost:8081 with container name
issuer-uri: http://localhost:8081/auth/realms/drivingschool
jwk-set-uri: http://localhost:8081/auth/realms/drivingschool/protocol/openid-connect/certs
keycloak:
realm: drivingschool
auth-server-url: http://localhost:8081/auth
ssl-required: external
resource: client-interface
use-resource-role-mappings: true
credentials:
secret: xxx
bearer-only: true
My test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#ActiveProfiles("test")
public class StudentControllerTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private StudentService service;
#MockBean
private StudentRepository repository;
#Test
public void contextLoads(){}
//more tests
}
the test all pass green BUT in the log i can see, that my app tries to connect to a database configured in my (basic) application.yml.
ava.sql.SQLNonTransientConnectionException: Could not connect to address=(host=localhost)(port=9005)(type=master) : Socket fail to connect to host:localhost, port:9005. Verbindungsaufbau abgelehnt (Connection refused)
at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:73) ~[mariadb-java-client-2.6.1.jar:na]
at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.create(ExceptionFactory.java:192) ~[mariadb-java-client-2.6.1.jar:na]
jpa:
database-platform: org.hibernate.dialect.MariaDBDialect
hibernate:
use-new-id-generator-mappings: false
ddl-auto: create
datasource:
url: jdbc:mariadb://localhost:9005/waterloo
username: waterloo
password: xxx
driver-class-name: org.mariadb.jdbc.Driver
when creating a application-prod.yml and moving all content from application.yml to application-prod.yml it tells me I have to configure a datasource URL
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class
I have the following questions:
Do the application.yml files get layered (*-test.yml settings on top of application.yml)?
Why does Spring try to build a connection to my database when I am not setting a datasource on my application-test.yml AND mocking the repository on the test?
Is it normal that Spring trys to establish a connection at this part?
3.1) If not: How to i prevent it from doing so?
Thanks and kind regards!
Failed to determine a suitable driver class
You need to add mariadb driver dependency to your gradle or maven file.
https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client/2.6.2
Make sure that dependency scope is suitable for test
If you already have and its still not working try to clean and rebuild your project.
Your Questions:
Do the application.yml files get layered (*-test.yml settings on top
of application.yml)?
If you add #ActiveProfiles("test") to you TestClass Spring will try to find an application-test.yml and overrrides application.yml properties with the given properties
Why does Spring try to build a connection to my database when I am not
setting a datasource on my application-test.yml AND mocking the
repository on the test?
Thats the magic of spring boot - it has default configurations for everything. You just need to set the Datasource properties and it will create the bean by itself.
Is it normal that Spring trys to establish a connection at this part? 3.1) If not: How to i prevent it from doing so?
You are starting the whole spring context with #SpringBootTest Annotation.
So it will startup all Repositories and try to establish connection to your database. If you don't want spring to startup the database layer you can just use #WebMvcTest
eg:
#RunWith(SpringRunner.class)
#WebMvcTest
#ActiveProfiles("test")
public class StudentControllerTests {
#Autowired
private MockMvc mockMvc;
#Test
public void contextLoads(){}
//more tests
}
Check this out: https://spring.io/guides/gs/testing-web/
If you need to startup the whole SpringContext you can also disable Spring Data AutoConfiguration with:
#SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
Check this out: https://www.baeldung.com/spring-data-disable-auto-config
missed following annotation:
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
Switching from spring boot back to "normal" spring because the app only uses some jdbc code to "upsert" into a postgresql database.
1)
tried annotating the test class with:
#RunWith(SpringJUnit4ClassRunner.class)
public class DBIntegration {
results in:
java.lang.IllegalStateException: Failed to load ApplicationContext
2)
tried annotating the class with:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {})
public class DBIntegration {
[main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [de.mydomain.myproject.DBIntegration]: no resource found for suffixes {-context.xml}.
No exceptions, but java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=insertDataFrom_sometest],
3) tried annotating the class with:
#Component
public class DBIntegration {
dependency injection does not work in this case, the expected service
(to be injected) throws a nullpointerexception
I am doing unit test for my Spring Boot application with Camel. When the application runs, it can get bean which is configured as a #Component
#Component("agencyExporterProcessor")
public class AgencyExporterProcessor {}
and I get the bean like this :
from(getTriggerExportEndpoint())
.routeId(getTriggerExportId())
// When shutting down, Camel will wait until the batch completed
.shutdownRunningTask(ShutdownRunningTask.CompleteAllTasks)
.log("[SamCustomExporter] - RouteId:${routeId} - Begin at ${date:now:MM/dd/yyyy HH:mm:ss.SSS}")
.setHeader(Messaging.Names.SAM_DATA_AGENCY_CONFIGURATION_HEADER_KEY.toString(), constant(getConfiguration()))
// Initialize a list to store exported CSV file names
.bean(agencyExporterProcessor, "prepareExportedFileList")
But when I test, the route cannot get the bean"
org.apache.camel.FailedToCreateRouteException: Failed to create route agencyExporterRoute_triggerExport at: >>> Bean[ref:agencyExporterProcessor method:prepareExportedFileList] <<< in route: Route(agencyExporterRoute_triggerExport)[[From[direct:agency... because of No bean could be found in the registry for: agencyExporterProcessor
This is how I configured my unit test class:
#DirtiesContext
#RunWith(SpringRunner.class)
#EnableAutoConfiguration
#SpringBootApplication
#SpringBootTest(classes = SamCustomExporterSpringApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class AgencyExporterRouteTest extends BaseRouteTestSupport {}
Please give advice!!!! Many thanks
You should refer to the bean name as a String value in the Camel route:
.bean("agencyExporterProcessor", "prepareExportedFileList")
Using gradle (3.4.1) with an integrationTest configuration, the tests using the ConfigurationProperties of Spring Boot (1.5.1.RELEASE) is failing to initialize even though the application initializes correctly (./gradlew bootRun). The class annotated with ConfigurationProperties is similar to the following
#Component
#ConfigurationProperties(prefix = "foo")
#Validated
public class AppConfiguration {
#NonNull
private URL serviceUrl;
...
The configuration file does have getters and setters. The error that is generated is similar to the following
java.lang.IllegalStateException: Failed to load ApplicationContext
....
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'AppConfiguration': Could not bind properties to AppConfiguration
....
Caused by: org.springframework.validation.BindException: org.springframework.boot.bind.RelaxedDataBinder$RelaxedBeanPropertyBindingResult
Field error in object 'foo' on field 'serviceUrl': rejected value [null]; codes ...
The configuration class of the integration test is annotated as follows
#Configuration
#ComponentScan(...)
#EnableConfigurationProperties
#EnableIntegration
public static class ContextConfiguration {}
The test class had the following annotations
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class ReleaseTest {
...
After looking at the Spring Boot code for the ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization() it suggested that the property source was not being discovered. Adding the org.springframework.boot:spring-boot-starter-test artifact as a compile-time dependency and modifying the context configuration of the test class to
#ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
the AppConfiguration class was initialized properly using a YAML-based properties file.
An alternative is to add
#TestPropertySource("classpath:/application.properties")
This approach doesn't require the spring-boot-starter-test dependency and requires that a "traditional" properties file is used (a YAML file will not work with this approach).
I have an integration test with the following configuration:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("integration-test")
#ContextConfiguration(classes = { PersistenceJpaConfig.class, ContextConfig.class, ServiceConfig.class, WebConfig.class})
#WebAppConfiguration
public class LeadsIntegrationTest {
...
}
The PersistenceJpaConfig class is the following:
#Configuration
#EnableTransactionManagement
#ComponentScan({ "org.persistence", "org.common.persistence" })
#PropertySource({ "persistence-${spring.profiles.active}.properties" })
#EnableJpaRepositories(basePackages = org.persistence.dao")
public class PersistenceJpaConfig {
...
}
The ${spring.profiles.active} resolves fine when the active profile is "dev", but when set to "integration-test" in the #ActiveProfiles("integration-test), it fails to resolve. Both the persistence-dev.properties and persistence-integration-test.properties are located in src/main/resources . The properties for dev has mysql configuration, and the integration-test has h2 embedded configuraiton for running integration tests that are isolated from dev data. When the test runs, I get the following:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'spring.profiles.active' in string value "persistence-${spring.profiles.active}.properties"
What is going on here?
You haven't told how you set the dev profile but my best guess is that you set it with a system property. When you use #ActiveProfiles the resolution of the profile comes too late for the #PropertySource annotation to pick up the value.
Regardless, do you know that 100% of that custom code there is supported out of the box with spring boot? Why can't you put that stuff in application-integration-test.properties and application-dev.properties?