Define Spring profile groups programatically / use then in #ContextConfiguration tests - spring

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

Spring Test Wants To Connect to Database

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})

DI in tests without using spring boot (#SpringBootTest)

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

Can Not Get #Component As A Bean In Unit Test For Spring Boot Application with Apache Camel

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")

Spring Boot ConfigurationProperties fail to initialize for integration testing

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).

#ActiveProfile("integration-test") in SpringBoot test not resolving

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?

Resources