Starting RabbitMq as GenerigContainer in Gitlab Ci - spring-boot

I have a Spring Boot 2.1 Application which has integration tests. For integration tests purposes i want start a RabbitMq container with testcontainers framework. When I start those on my local machine everything seems to work i can access my rabbitMQ during the IT tests. However once i execute under gitlab-ci i constantly get connection refused exceptions
Here is my application-it-properties
spring.rabbitmq.host=localhost
spring.rabbitmq.virtualHost=/
spring.rabbitmq.port=5673
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.dynamic=true
spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.listener.simple.acknowledgeMode=AUTO
spring.rabbitmq.listener.simple.concurrency=5
This is my verify step in gitlab-ci
verify:feature:
stage: verify
script:
- git config --global user.email gitlab#test.de
- git config --global user.name gitlab
- git fetch --all
- git checkout origin/develop
- git merge $CI_BUILD_REF --no-commit --no-ff
- mvn $MAVEN_CLI_OPTS verify sonar:sonar $SONAR_PREVIEW_CLI_OPTS
only:
- /feature.*/
And this is how i start my testcontainer RabbitMQ
#Slf4j
#RunWith(SpringRunner.class)
#TestPropertySource(locations = {"classpath:application-it.properties"})
#SpringBootTest
public class TransformerServiceApplicationIt {
private static final int EXPOSED_RABBITMQ_PORT = 5672;
private static final int EXPORTED_RABBITMQ_PORT = 5673;
/**
* Start the rabbitmq.
*/
static {
final Consumer<CreateContainerCmd> rabbitCmd = e -> e.withPortBindings(new PortBinding(Ports.Binding.bindPort(EXPORTED_RABBITMQ_PORT), new ExposedPort(EXPOSED_RABBITMQ_PORT)));
final GenericContainer rabbitMq = new GenericContainer("rabbitmq:3-management").withExposedPorts(EXPOSED_RABBITMQ_PORT)
.withCreateContainerCmdModifier(rabbitCmd);
rabbitMq.start();
}....
}
And this is my exception
[org.springframework.amqp.rabbit.core.RabbitTemplate]: Factory method 'rabbitTemplate' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'itRabbitMQConfig': Invocation of init method failed; nested exception is org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused (Connection refused)
My guess is that it has something to do with the hostname resolving for localhost on gitlab.

Try this:
static {
final GenericContainer rabbitMq = new GenericContainer("rabbitmq:3-management").withExposedPorts(EXPOSED_RABBITMQ_PORT);
rabbitMq.start();
// Pass the properties directly to the app. Do not use properties file.
System.setProperty("spring.rabbitmq.host", rabbitMq.getContainerIpAddress());
System.setProperty("spring.rabbitmq.port", rabbitMq.getMappedPort(5672).toString());
}

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

Kotlin integration Tests with Spring Boot

What I have: I'm developing a microservice, using Spring Boot with Web and MongoDB as storage. For CI integration tests I use test containers. I have two integrations tests with SpringBootTest annotations, which use TestConfig class. TestConfig class provides setup MongoDB test container with fixed exposed ports.
My problem: When I run my tests one at a time then they succeed. But when I run my tests at the same time then they failed.
MongoContainerConfig.kt
#TestConfiguration
class MongoContainerConfig {
var mongoContainer: GenericContainer<Nothing>
constructor() {
mongoContainer = FixedHostPortGenericContainer<Nothing>("mongo")
.withFixedExposedPort(27018,27017)
mongoContainer.start()
}
#PreDestroy
fun close() {
mongoContainer.stop()
}
}
First test
#SpringBootTest(
classes = arrayOf(MongoContainerConfig::class, AssertUtilsConfig::class),
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
class CardControllerTest {
#Test
fun test() {}
}
Second test
#SpringBootTest(classes = arrayOf(MongoContainerConfig::class, AssertUtilsConfig::class))
class PositiveTest {
#Test
fun test() {}
}
Error msg
Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mongoContainerConfig': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.lang.card.engcard.config.MongoContainerConfig$$EnhancerBySpringCGLIB$$e58ffeee]: Constructor threw exception; nested exception is org.testcontainers.containers.ContainerLaunchException: Container startup failed
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1320)
a
This project you can see on github with CI
https://github.com/GSkoba/eng-card/runs/576320155?check_suite_focus=true
It's very funny because tests work if rewrite them to java
Hah, it not easy)
https://docs.spring.io/spring/docs/5.2.5.RELEASE/spring-framework-reference/testing.html#testcontext-ctx-management-caching
Tests have different context and its why MongoContainerConfig call twice.
Fix - add webEnv as in CardControllerTest.kt
#SpringBootTest(classes = arrayOf(MongoContainerConfig::class, AssertUtilsConfig::class),
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class PositiveTest
Proof https://github.com/GSkoba/eng-card/actions/runs/78094215

Unable to call Mapper.xml file by using junit testing for the application developed using Mybatis+Springboot

I'm very new to junit testing. How to write junit test real database call from mybatis.xml file.
Please find the below code.
#RunWith(SpringRunner.class)
//#MybatisTest
#SpringBootTest
public class HotelMapperTest {
#Autowired
private HotelMapper hotelMapper;
#Test
public void selectByCityIdTest() {
Hotel hotel = hotelMapper.selectByCityId(1);
assertThat(hotel.getName()).isEqualTo("Conrad Treasury Place");
assertThat(hotel.getAddress()).isEqualTo("William & George Streets");
assertThat(hotel.getZip()).isEqualTo("4001");
}
when i run the junit testing i'm getting below exception:
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):
Herer my question is how we'll test the real database, When enable the #MybatisTest it's looking for datasource, already we specified all properties in applicaiton.properties. In this time i'm getting below exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource':
You can config mybatis mapper location in spring configuration file (such as application.yml).
mybatis configuration:
mybatis:
mapper-locations:
- classpath*:mapper/*.xml

spring boot stater parent 2.0.1 entityManagerFactory Bean creation exception

I am getting following error when I change my spring boot stater parent 1.5.9 RELEASE to 2.0.1 RELEASE
target/surefire-reports
-------------------------------------------------------------------------------
Test set: com.restapispringboot.RestApiSpringbootApplicationTests
-------------------------------------------------------------------------------
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 3.158 s <<< FAILURE! - in com.restapispringboot.RestApiSpringbootApplicationTests
contextLoads(com.restapispringboot.RestApiSpringbootApplicationTests) Time elapsed: 0.001 s <<< ERROR!
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: javassist/bytecode/ClassFile
Caused by: java.lang.NoClassDefFoundError: javassist/bytecode/ClassFile
Caused by: java.lang.ClassNotFoundException: javassist.bytecode.ClassFile
Could it be something in my config that I need to change to make it work?
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/boot_rest_api
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
#spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
# Hibernate ddl auto (create, create-drop, validate, update)
# spring.jpa.hibernate.ddl-auto = update
spring.jpa.hibernate.ddl-auto=create
logging.level.root=DEBUG
Application main
#SpringBootApplication
#EnableJpaRepositories("com.restapispringboot.repo")
#EntityScan("com.restapispringboot.model")
public class RestApiSpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(RestApiSpringbootApplication.class, args);
}
}
Entity
#Entity
#Table(name = "customer")
public class Customer implements Serializable {
// getters and setters
}
Repo
public interface CustomerRepository extends CrudRepository<Customer, Long> {
List<Customer> findByLastName(String lastName);
}
Update
I just notice when I mvn clean install I also get the following errors. But I checked my build path both JRE[JavaSE-1.8] and maven dependencies are build path...
ERROR] error reading /Users/erichuang/.m2/repository/org/aspectj/aspectjweaver/1.8.13/aspectjweaver-1.8.13.jar; invalid CEN header (bad signature)
[ERROR] error reading /Users/erichuang/.m2/repository/org/javassist/javassist/3.22.0-GA/javassist-3.22.0-GA.jar; invalid LOC header (bad signature)
As discussed in the comments, your error says, java.lang.NoClassDefFoundError: javassist/bytecode/ClassFile.
As you were migrating from spring boot version 1.5.9 RELEASE to 2.0.1 RELEASE, there might be some conflict in the javassist jar (3.20.0-GA vs 3.22.0-GA).
So you can clean your maven repo (delete the localRepository) and run your command once again.

Spring Boot with Embedded Mongo : Cannot assign requested address: JVM_Bind

I am trying to setup a JUnit test for a Spring Boot with embedded Mongo & Kafka :-
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
classes = {AccountingApplication.class})
#DataMongoTest
public class BaseEmbeddedTest {
#ClassRule
public static KafkaEmbedded embeddedKafka = new KafkaEmbedded(1, true);
#Autowired
private MongoTemplate mongoTemplate;
#Test
public void emptyTest(){
}
}
src/test/resources/application.yml :-
spring:
data:
mongodb:
port: 0
kafka:
bootstrap-servers: ${spring.embedded.kafka.brokers}
PROBLEM
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [de.flapdoodle.embed.mongo.config.IMongodConfig]: Factory method 'embeddedMongoConfiguration' threw exception; nested exception is java.net.BindException: Cannot assign requested address: JVM_Bind
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
... 140 more
Caused by: java.net.BindException: Cannot assign requested address: JVM_Bind
at java.net.DualStackPlainSocketImpl.bind0(Native Method)
at java.net.DualStackPlainSocketImpl.socketBind(DualStackPlainSocketImpl.java:106)
at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:387)
at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:190)
at java.net.ServerSocket.bind(ServerSocket.java:375)
at java.net.ServerSocket.<init>(ServerSocket.java:237)
at de.flapdoodle.embed.process.runtime.Network.getFreeServerPort(Network.java:80)
at org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration.embeddedMongoConfiguration(EmbeddedMongoAutoConfiguration.java:147)
What am I doing wrong here ?
Version:-
dependencyManagementPluginVersion = '1.0.3.RELEASE'
springBootVersion = '1.5.6.RELEASE'
springCloudVersion = 'Dalston.SR2'
projectVersion = '0.0.1-SNAPSHOT'
javaVersion = 1.8
kotlinVersion = '1.1.4'
This annotation: #DataMongoTest causes Spring Boot to create an embedded Mongo instance. The exception messages tells us that the embedded Mongo instance cannot start because there is already a process running on the port it is trying to run on.
The embedded Mongo instance is configured by EmbeddedMongoAutoConfiguration and the strategy applied by Spring Boot - for port allocation - is as follows:
if configured Mongo port > 0 then
use the configured port
else
assign a random port
end
So, I suspect that your test context is configured with a non zero value for spring.data.mongodb.port. I know you posted your application.yml which implies that you are - correctly - assigning a zero value to spring.data.mongodb.port but if you put a breakpoint inside the EmbeddedMongoAutoConfiguration constructor and peek inside the properties parameter I think you'll see that the actual value in use by that configuration class is not zero. If the port value passed to EmbeddedMongoAutoConfiguration is actually zero but you are still getting the JVM_Bind error then that implies that this call: Network.getFreeServerPort(this.getHost()) is not returning a free port and that seems unlikely.
In order to fix this issue: as long as you configure your test context with spring.data.mongodb.port=0 then the embedded Mongo instance will be assigned a random port and this random port will be made known to other aspects of your Spring context (such as your MongoTemplate) which need to talk to that Mongo instance.

Resources