I have a Spring Gateway application that depends on Eureka and Config Server. When I try to run the tests for this application, they fail because they require Eureka Server and Config Server to be enabled. Is it possible to run tests without first starting these services?
#SpringBootTest
class GatewayApplicationTests {
#Test
void contextLoads() {
}
}
Maybe there are some solutions for this based on mocks?
Related
I'm developing a service that has many dependencies like Redis, PubSub, S3, ServiceC and ServiceD. When I hit an endpoint in development, e.g. /data/4, a http request to ServiceC is performed.
So to that to work, something that mocks ServiceC must run. In my case it's wiremock (since I cant run ServiceC directly, as it also has many dependencies). All of these MockServices are in a docker-compose-dev file.
Now to my question: How can I run the docker-compose with testcontainers, get the assigned ports, and set the correct properties to the WebClient has the right mock url + port?
What lifecycle hook can I use to run before spring boot starts, and also can configure the properties?
One downside would be an increased boot time in the dev mode though, but I can't assign fixed ports in docker compose file, because they might be used on the developer's machine. So makes no sense to ship url defaults for the service urls on localhost.
Testcontainers supports starting Docker Compose out-of-the-box using the DockerComposeContainer class. While creating an instance of this class you have to expose all your containers:
#Testcontainers
public class DockerComposeTest {
#Container
public static DockerComposeContainer<?> environment =
new DockerComposeContainer<>(new File("docker-compose.yml"))
.withExposedService("database_1", 5432, Wait.forListeningPort())
.withExposedService("keycloak_1", 8080,
Wait.forHttp("/auth").forStatusCode(200)
.withStartupTimeout(Duration.ofSeconds(30)));
#Test
void dockerComposeTest() {
System.out.println(environment.getServicePort("database_1", 5432));
System.out.println(environment.getServicePort("keycloak_1", 8080));
}
}
The example above maps a PostgreSQL database and Keycloak to a random ephemeral port on your machine. Right after the wait checks pass, your test will run and you can access the actual port with .getServicePort().
A possible solution for the WebClient configuration is to use a ApplicationContextInitializer as you somehow have to define the Spring Boot property before the container starts:
public class PropertyInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
// static access to environment variable of the test
TestPropertyValues
.of("your_web_client_base_url=http://localhost:" + environment.getServicePort("serviceC", 8080) + "/api")
.applyTo(configurableApplicationContext);
}
}
You can then register the initializer for your integration tests with:
#Testcontainers
#ContextConfiguration(initializers = {PropertyInitializer.class})
class DockerComposeTest {
}
As an alternative, you could also try to solely mock all HTTP communication to external services with WireMock.
Am developing application using Maven it has EJB layer. I configured datasource in WebSphere Liberty server. All transaction can be handle by the server. Am using Jenkins to build a application. I would like to create CI/CD implementation. For that I tried add Junit test in application. but am unable to connect database while doing Jenkins build. Because there is no communication b/w server and Jenkins while doing build. How can I create Junit that handle database connection and EJB without Mock.?
One possible solution you could use here is an integration testing library called MicroShed Testing. It is created for integration tests with JavaEE/MicroProfile app servers such as Liberty, and can be used to test your application with external resources running such as DBs.
MicroShed Testing is ideal if you are running your Liberty application inside of a Docker container. If you are running in Docker, you can easily write an integration test that looks something like this:
#MicroShedTest
#SharedContainerConfig(AppContainerConfig.class)
public class DatabaseTest {
#Inject
public static MyJAXRSEndpoint personSvc;
#Test
public void testGetPerson() {
Long bobId = personSvc.createPerson("Bob", 24);
Person bob = personSvc.getPerson(bobId);
assertEquals("Bob", bob.name);
assertEquals(24, bob.age);
}
}
To get this working, you can define your application topology in the class referenced by the #SharedContainerConfig annotation like this:
public class AppContainerConfig implements SharedContainerConfiguration {
#Container
public static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>()
.withNetworkAliases("testpostgres")
.withDatabaseName("testdb");
#Container
public static MicroProfileApplication app = new MicroProfileApplication()
.withEnv("POSTGRES_HOSTNAME", "testpostgres")
.withEnv("POSTGRES_PORT", "5432")
.withAppContextRoot("/myservice");
#Override
public void startContainers() {
postgres.start();
app.start();
}
}
The above code will do the following steps:
1) Build your application into a Docker container, using the Dockerfile in your repo
2) Start up the docker container for your app AND the postgresql database (or any other DB container you may need)
3) Wait for containers to be ready, then run tests that invoke HTTP requests on the running containers
I find this approach nice because it works the same way anywhere Docker is installed -- either locally on your machine or in your CI/CD pipeline.
For more info on MicroShed Testing, I recommend checking out the website here:
https://microshed.org/microshed-testing/
and especially the examples, which include a Liberty+Database example:
https://microshed.org/microshed-testing/features/99_Examples.html
Disclaimer: I work on Liberty and MicroShed Testing
My Spring Boot application also starts a gRPC service along with its REST (HTTP) service. I've written specific tests for gRPC and REST. When I run a gradle test these tests are run sequentially, however; there is no reason they can't be run in parallel.
What I'm shooting for here is a single instance of my Spring Boot application running while the tests are executed in parallel.
I've tried setting the test section in my gradle file so it has 'forkCount', I also tried setting options such that parallel="classes", but this produces an error about the 'parallel' being an unknown property (maybe a junit 5 thing?)
test {
options {
parallel = "classes"
// forkCount = 2
}
}
The forkCount option is not what I'm looking for since it will start multiple instances of the spring application.
I've also tried removing the #RunWith from the test classes and making a separate test class (which has the #RuWith annotation) that has the following method in it
#Test
void testRunner() {
JUnitCore.runClasses(ParallelComputer.classes(), {GrpcTests.class, RestTests.class});
}
But the tests still appear to run sequentially.
I've tried several other things as well, sorry I don't have all of them handy.
Goal
Ideally what I'm hoping for is a single instance of my Spring Boot app running while the test classes run in parallel (bonus kudos if I can get the methods to run in parallel too)
Java Version: "1.8.0_171"
Spring Boot Version: 2.0.4.RELEASE
Per the recommendation I tried adding the
#Test
public void contextLoads() throws Exception {
}
And adding the 'maxParallelForks' entry in the gradle file, I had already been using the #SpringBootTest annotation but this behaved the same as when I used 'forkCount` in that at least 2 instances where started as can be seen by the test shutdown log
2019-04-25 10:24:17.245 LogLevel=INFO 53838 --- shutting down gRPC server since JVM is shutting down
...
2019-04-25 10:24:30.125 LogLevel=INFO 53839 --- shutting down gRPC server since JVM is shutting down
You can see I get two shutdown messages and the PIDs are shown (53838 & 53839).
You need to combine #SpringBootTest with maxParallelForks.
Annotate your unit tests with #SpringBootTest. #SpringBootTest will boot up a Spring Boot context that will be cached across all your tests.
"A nice feature of the Spring Test support is that the application context is cached in between tests, so if you have multiple methods in a test case, or multiple test cases with the same configuration, they only incur the cost of starting the application once"
See:
https://spring.io/guides/gs/testing-web/
Add the following to your build.gradle. To run multiple test at the same time.
tasks.withType(Test) {
maxParallelForks = 4 //your choice here
}
See https://guides.gradle.org/performance/#parallel_test_execution
I have application written in Spring Boot 2 and REST API. When I run this app on embeded Tomcat server via bootRun gradle task everything is fine.
The problem is that when this application is deployed on standalone Tomcat 8.5 server response is truncated to 8kB. Why is that?
My REST controller:
#RestController
public class ApiController {
public ResponseEntity<Mono<ResultData>> get(String param) {
// generating data
return ResponseEntity.ok(Mono.just(ResultData.builder()
.data(data)
.build()));
}
}
Solved. I have not extended SpringBootServletInitializer (https://docs.spring.io/spring-boot/docs/current/reference/html/howto-traditional-deployment.html) - when you would like to run Spring Boot app as deployable war you have to do this.
We were planning to use spring cloud Netflix oss components. So I was doing a small sample project.
I developed 2 spring microservices and those services runs well on
http://localhost:9000/microsvc-one http://localhost:9001/microsvc-two
And also wrote a sample spring cloud etflix eureka maven project which runs well on
http://localhost:8761
I used annotations #EurekaDiscoveryClient and #SpringBootApplication on both the spring boot microservices main class
I used annotation #EnableEurekaServer and #SpringBootApplication
Now I am facing a problem in registering those services in eureka server. I referred some samples. I am not understanding those.
I did some changes in application.yml files of microsvc-one and microsvc-two and also application.yml file of eureka server.
But still it shows empty.
What all changes are required or missing or correct configuration to be done so that my services are being registered on eureka.
I also have other question like do i need to create a separate project which has #EnableConfigServer and #SpringBootApplication Annotations other than the above 2 microservices and eureka server project module to register the services on eureka.
I see those in most of the examples.
If yes..how do we link between all these?
If you are using springBoot application you will need the annotaion #SpringBootApplication thats why that annotation is there on the project you are seeing. #EnableConfigServer is when you are using the spring-cloud config server it is used to externalize the configuration properties but since you have the application.yml inside the project so you donot need that either.
I am thinking you have a spring boot application for both Microservices and the Eureka server. You need to annotate the eureka main class with
#SpringBootApplication
#EnableEurekaServer
#EnableDiscoveryClient
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
Additionally you need annotate you microservice's main class with..
#SpringBootApplication
#EnableDiscoveryClient
public class MicroApplication {
public static void main(String[] args) {
SpringApplication.run(MicroApplication.class, args);
}
}
Since you donot have you application.yml file in the question here is what you need.
You need the below configuration in application.yml of the microservices.
eureka:
client:
serviceUrl:
defaultZone: ${eurekaurl:http://localhost:8761/eureka/}
In the Eureka Server application.yml file I have this in mine. you might need to tweak it based on what you want.
info:
component: Registry Server
server:
port: ${port:8761}
eureka:
client:
registerWithEureka: false
fetchRegistry: false
server:
enable-self-preservation: false
waitTimeInMsWhenSyncEmpty: 0
instance:
hostname: localhost
lease-expiration-duration-in-seconds: 15
lease-renewal-interval-in-seconds: 5
Suppose you have a microservice named "LoginServer" now, let's see how to register this service with discovery server (Eureka Server) at startup.
Here Spring Boot startup class of LoginServer.java:
#EnableAutoConfiguration
#EnableDiscoveryClient
public class LoginServer {
public static void main(String[] args) {
// Will configure using login-server.yml
System.setProperty("spring.config.name", "login-server");
SpringApplication.run(LoginServer.class, args);
}
}
The #EnableDiscoveryClient - enables service registration and discovery. In this case, this process registers itself with the discovery-server service using its application name, that is configured in YML configuration file.
let's see the complete setup:
First create a login-server.yml (any name but extension should be .yml) file into src/main/resources package folder. And write those configurations and save.
# Spring properties
spring:
application:
name: login-server # This name used as ID so ("spring.config.name",
#"login-server"); must be same.
# Discovery Server Access
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1111/eureka/
# HTTP Server
server:
port: 2222 # HTTP (Tomcat) port
Run the LoginServer and let it finish initializing. Open the dashboard by putting this URL http://localhost:1111 in your favorite browser and refresh. After few seconds later you should see the LOGIN-SERVER. Generally registration takes up to 30 seconds (by default) so wait or restart.
And this is the microservice complete registration process.