This assignment is targeted at developing an integration test that involves the management of a separate server process. The aspects under test should already be implemented in your assignment 3 solution. The focus of this assignment will be on testing a different way — one that is closer to the deployment option versus some of the more performant unit and unit integration options achieved earlier.
You have the option to implement the integration test in one of three ways
-
Testing against a Spring Boot Executable JAR.
-
This involves a fair amount of Maven plugin configuration.
-
-
Testing against a Docker Container using Docker/Maven plugins
-
This also involves a fair amount of Maven plugin configuration, adds extra steps, but comes closer to production deployment.
-
-
Testing against a Docker Container using Docker/Testcontainers.
-
This involves very little Maven plugin configuration. Most of the work can be performed within the JUnit JVM.
-
Turn in one solution option, but if you do turn in multiple — identify which one is your primary option.
Do Not Make Use of ejava-parent Maven "it" Profile
For this assignment being primarily expressed within your provided solution project poms, do not make use of the Maven In short, this means do not declare the |
1. Assignment 4a: Executable JAR Option
1.1. Purpose
In this portion of the assignment, you will demonstrate your knowledge of implementing a Maven Failsafe integration test with a Spring Boot executable JAR. You will:
-
dynamically generate test instance-specific property values in order to be able to run in a shared CI/CD environment
-
start server process(es) configured with test instance-specific property values
-
implement a Maven Failsafe integration test in order to test a Spring Boot executable JAR running as a stand-alone process
-
execute tests against server process(es)
-
stop server process(es) at the conclusion of a test
-
evaluate test results once server process(es) have been stopped
1.2. Overview
In this portion of the assignment, you will be again testing your assignment 3 solution. However, this time as a stand-alone Spring Boot executable JAR running separately from the JUnit test.
1.3. Requirements
-
Provide a Maven project pom that will dynamically generate a unique network server port prior to the Maven
pre-integration-test
phase.The following shows an example output of satisfying the requirement using the Build Helper Maven Plugin and assigning it to Maven property
my.server.port
.$ mvn verify ... [INFO] --- build-helper:3.6.0:reserve-network-port (generate-port-number) @ autorentals-it-springboot --- [INFO] Reserved port 58262 for my.server.port
-
Provide a Maven project pom that will
-
build the Spring Boot Executable JAR
The following shows an example output of satisfying the build requirement
[INFO] --- spring-boot-maven-plugin:3.3.2:repackage (build-app) @ autorentals-it-springboot --- [INFO] Creating repackaged archive .../target/autorentals-it-springboot-1.0-SNAPSHOT-bootexec.jar with classifier bootexec
-
start the application as a Spring Boot executable JAR during the Maven
pre-integration-test
phase.-
The application API must listen to HTTP requests on the dynamically generated network server port
The following shows an example output of satisfying the requirement for start and assigning the port number.
[INFO] --- spring-boot-maven-plugin:3.3.2:start (start-server) @ autorentals-it-springboot --- ... INFO 97896 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 58262 (http)
-
-
stop the application during the
post-integration-test
phaseThe following shows an example output of satisfying the requirement for stopping the application.
[INFO] --- spring-boot-maven-plugin:3.3.2:stop (stop-server) @ autorentals-it-springboot --- [INFO] Stopping application...
-
-
Define a JUnit test that will test a call to a server-side component via an HTTP endpoint. This only has to be a single call. It does not matter what the call is as long as it is to something you developed, and it returns a success status. Security activation is not required. The call can be anonymous. There is no requirement to use injection or a Spring context on the client-side even though the tips here show
@Bean
factories.-
Accept/use a dynamically generated server port at runtime
The following shows an example of how the class examples have mapped "it.server.*" properties to a @ConfigurationProperties class. This can be leveraged to identify the base URL to the external server.
@Bean @ConfigurationProperties("it.server") ServerConfig serverConfig() { return new ServerConfig(); }
The following shows an example URL construction based upon the @ConfigurationProperties bean that has had server address information mapped to "it.server.*" properties.
@Bean URI rentalsURL(ServerConfig serverConfig) { return UriComponentsBuilder.fromUri(serverConfig.getBaseUrl()) ...
The following shows an example output of configuring JUnit to use the same dynamically assigned port number.
16:16:45.771 main DEBUG i.e.e.c.web.RestTemplateLoggingFilter#intercept:37 GET http://localhost:58262/api/autorentals, returned 200 OK/200 ... {"contents":[]}
-
Not host or use any server-side components locally
To be fully compliant with this requirement, your JUnit test cannot use your @SpringBootApplication configuration of your application class. You should create a @SpringBootConfiguration as a substitute. Be sure to add @EnableAutoConfiguration if you need any automatically provided beans in the client (e.g., RestTemplateBuilder).
@SpringBootConfiguration @EnableAutoConfiguration public class IntegrationTestConfiguration {
-
-
Extend the Maven project pom to execute the JUnit test during the Maven
integration-test
phase and ignored during the Maventest
phase.Surefire and Failsafe look for unique class naming patterns. Name the test class according to the phase it needs to execute in. The Failsafe plugin must be explicitly configured to run the integration-test goal. The following shows example output of Failsafe running tests.
[INFO] T E S T S [INFO] ------------------------------------------------------- [INFO] Running info.ejava_student.starter.assignment4.it.MyIntegrationIT ... [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.975 s -- in info.ejava_student.starter.assignment4.it.MyIntegrationIT [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
-
Extend the Maven project pom to evaluate test results once server process(es) have been stopped
The Failsafe plugin must be explicitly configured to run the verify goal. The following shows example output of Failsafe verifying test results.
[INFO] --- maven-failsafe-plugin:3.3.1:verify (verify) @ autorentals-it-springboot ---
-
Turn in a source tree with complete Maven modules that will build and automatically test the application.
-
Try to configure DEBUG so that the URL exchange is easy and obvious to observe.
The following shows example output of DEBUG with the full URL requested and the response.
16:16:45.771 main DEBUG i.e.e.c.web.RestTemplateLoggingFilter#intercept:37 GET http://localhost:58262/api/autorentals, returned 200 OK/200 ... {"contents":[]}
-
1.3.1. Grading
Your solution will be evaluated on:
-
dynamically generate test instance-specific property values in order to be able to run in a shared CI/CD environment
-
whether the HTTP server port used between the JUnit test and the server-side components was dynamically generated such that each execution of the build results in a different server port number
-
-
start server process(es) configured with test instance-specific property values
-
whether the Spring Boot executable JAR was started with and uses a dynamically generated server port number
-
-
implement a Maven Failsafe integration test in order to test a Spring Boot executable JAR running as a stand-alone process
-
whether a JUnit test was provided that implements tests using HTTP requests
-
-
execute tests against server process(es)
-
whether the JUnit test is client-side-only, with server-side components deployed only to the remote application
-
-
stop server process(es) at the conclusion of a test
-
whether the started server process is terminated at the end of the overall tests
-
-
evaluate test results once server process(es) have been stopped
-
whether test results are actually evaluated and expressed in the overall Maven build result.
-
1.3.2. Additional Details
-
You are permitted to implement the integration test in the same module or a downstream module relative to the actual Spring Boot application. However, there will be some extra issues related to sharing the Java JAR and repackaging into a Spring Boot Executable JAR in a downstream module to address.
-
the following shows an example downstream Integration Test module with only the IT artifacts and no
src/main
artifacts.|-- pom.xml `-- src |-- main | `-- java (nothing) `-- test |-- java | `-- info ... | `-- it | |-- IntegrationTestConfiguration.java | `-- MyIntegrationIT.java `-- resources `-- application-test.properties
-
the downstream Integration Test module will have to declare a dependency on the upstream application module.
... <dependency> <groupId>info.ejava-student.starter.assignment3.autorentals</groupId> <artifactId>autorentals-security-svc</artifactId> <version>1.0-SNAPSHOT</version> </dependency> ...
-
the downstream module can use the
@SpringBootApplication
of an upstream module when present in the upstream module dependency. The example below shows an explicitmainClass
being identified in the upstream dependency.... <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>info.ejava_student.starter.assignment3.security.AutoRentalsSecurityApp</mainClass> ...
-
when running the IT test in the downstream module, the upstream application is what its being run. The following shows an example output from the upstream security application in the downstream IT module.
$ mvn spring-boot:run ... INFO 6781 --- [ main] i.e.s.a.security.AutoRentalsSecurityApp : Starting AutoRentalsSecurityApp using Java 17.0.12 with PID 6781 (.m2/repository/.../autorentals-security-svc-1.0-SNAPSHOT.jar started by jim in .../assignment4-autorentals-it/autorentals-it-springboot) ... INFO 6781 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/' INFO 6781 --- [ main] i.e.s.a.security.AutoRentalsSecurityApp : Started AutoRentalsSecurityApp in 1.782 seconds (process running for 2.064)
-
-
You may want to temporarily force a JUnit assertion error to verify the test results are being verified by Maven at the end.
2. Assignment 4b: Docker Plugin Option
There are two sections to this assignment option. You must complete them both.
2.1. Docker Image
2.1.1. Purpose
In this portion of the assignment, you will demonstrate your knowledge of building a Docker Image. You will:
-
build a layered Spring Boot Docker image using a Dockerfile and docker commands
2.1.2. Overview
In this portion of the assignment, you will be building a Docker image with your Spring Boot application organized in layers
2.1.3. Requirements
-
Provide a Maven project pom that will build the application as a Spring Boot executable JAR with the ability to extract layers.
Build Spring Boot Executable JARThe following shows an example output of satisfying the requirement to build the application.
$ mvn clean package ... [INFO] --- spring-boot-maven-plugin:3.3.2:repackage (build-app) @ autorentals-it-docker --- [INFO] Creating repackaged archive .../target/autorentals-it-docker-1.0-SNAPSHOT-bootexec.jar with classifier bootexec ...
Spring Boot Executable JAR is LayeredThe following shows an example output verifying the application packaging supports layers.
$ java -Djarmode=tools -jar target/autorentals-it-docker-*-bootexec.jar list-layers dependencies spring-boot-loader snapshot-dependencies application
-
Create a layered Docker image using a Dockerfile multi-stage build that will extend a JRE image and complete the image with your Spring Boot Application broken into separate layers.
Build Docker Image using LayersThe following shows an example output building a multi-stage Docker image, using layers.
$ docker build . -t test:test (1) [+] Building 5.3s (14/14) FINISHED ... => [builder 4/4] RUN java -Djarmode=tools -jar application.jar extract --layers --launcher --destination extracted ... => [stage-1 3/6] COPY --from=builder /builder/extracted/dependencies/ ./ => [stage-1 4/6] COPY --from=builder /builder/extracted/spring-boot-loader/ ./ => [stage-1 5/6] COPY --from=builder /builder/extracted/snapshot-dependencies/ ./ => [stage-1 6/6] COPY --from=builder /builder/extracted/application/ ./ => => naming to docker.io/test:test
1 the path provided will depend on the location you place your Dockerfile and files that go into the container -
The application API must listen to HTTP requests on a configured server port (with a default of 8080)
-
The application API must activate with a provided profile
-
The application should use HTTP and not HTTPS within the container.
Application Starts with Supplied Listen Port and Profile(s)The following shows an example output of satisfying that the application can use a provided HTTP port number and profile(s).
$ docker run --rm test:test --server.port=7777 --spring.profiles.active=nosecurity ... AutoRentalsSecurityApp : The following 1 profile is active: "nosecurity" ... TomcatWebServer : Tomcat started on port 7777 (http) with context path '/'
-
-
Turn in a source tree with complete Maven module containing the Dockerfile and source files. Use this exact same Dockerfile and application in the next section.
2.1.4. Grading
Your solution will be evaluated on:
-
build a layered Spring Boot Docker image using a Dockerfile and docker commands
-
whether you have a multi-stage Dockerfile
-
whether the Dockerfile successfully builds a layered version of your application using standard docker commands
-
2.1.5. Additional Details
-
You may optionally choose to build and manage Dockerfile/Docker Compose tasks using the io.fabric8:docker-maven-plugin, io.brachu:docker-compose-maven-plugin, or other plugins.
2.2. Docker Integration Test
2.2.1. Purpose
In this portion of the assignment, you will demonstrate your knowledge of implementing a Maven Failsafe integration test using Docker or Docker Compose. You will:
-
dynamically generate test instance-specific property values in order to be able to run in a shared CI/CD environment
-
start server process(es) configured with test instance-specific property values
-
implement a Maven Failsafe integration test in order to test a Spring Boot executable JAR running within a Docker container as a stand-alone process
-
execute tests against server process(es)
-
stop server process(es) at the conclusion of a test
-
evaluate test results once server process(es) have been stopped
2.2.2. Overview
In this portion of the assignment you will be using a Docker container as your server process and managing it with Maven plugins.
2.2.3. Requirements
-
Provide a Maven project pom that will build the Spring Boot executable JAR (from the previous section)
-
Provide a Maven project pom that will dynamically generate a unique network server port prior to the Maven
pre-integration-test
phase.Generate Unique Network Port NumberThe following shows an example output of satisfying the port allocation requirement using the Build Helper Maven Plugin and assigning it to Maven property
my.server.port
.$ mvn verify ... [INFO] --- build-helper-maven-plugin:3.6.0:reserve-network-port (generate-port-number) @ autorentals-it-docker --- [INFO] Reserved port 52368 for my.server.port
-
Provide a Maven project pom that will build the Docker image prior to the
pre-integration-test
phase.If your Dockerfile is below the
${project.basedir}
, you may have to provide acontextDir
setting to${project.basedir}
to indicate where all relatives paths start.<docker.contextDir>${project.basedir}</docker.contextDir>
Build Docker Image within Project BuildThe following shows an example output of satisfying the Docker build requirement using the
io.fabric8:docker-maven-plugin
Maven plugin.$ mvn package ... [INFO] --- docker-maven-plugin:0.44.0:build (build-image) @ autorentals-it-docker --- [INFO] Building tar: .../target/docker/autorentals-it-docker/1.0-SNAPSHOT/tmp/docker-build.tar [INFO] DOCKER> [autorentals-it-docker:1.0-SNAPSHOT] "autorentals-it-docker": Created docker-build.tar in 483 milliseconds [INFO] DOCKER> Step 1/14 : FROM eclipse-temurin:17-jre AS builder .... [INFO] DOCKER> Successfully built 04d5a32d0c34 [INFO] DOCKER> Successfully tagged autorentals-it-docker:1.0-SNAPSHOT [INFO] DOCKER> [autorentals-it-docker:1.0-SNAPSHOT] "autorentals-it-docker": Built image sha256:04d5a [INFO] DOCKER> autorentals-it-docker:1.0-SNAPSHOT: Removed dangling image sha256:fbcd3
-
Provide a Maven project pom that will start the application within a Docker image/container during the Maven
pre-integration-test
phase. You may use straight Docker or Docker Compose.Start Docker Container Prior to Integration TestThe following shows an example output of satisfying the Docker start requirement using the
io.fabric8:docker-maven-plugin
Maven plugin.$ mvn pre-integration-test [INFO] --- docker-maven-plugin:0.44.0:start (start-container) @ autorentals-it-docker --- [INFO] DOCKER> [autorentals-it-docker:1.0-SNAPSHOT] "autorentals-it-docker": Start container e95c730579be [INFO] DOCKER> [autorentals-it-docker:1.0-SNAPSHOT] "autorentals-it-docker": Waiting on url http://localhost:52368/api/autorentals with method HEAD for status 200..399. [INFO] DOCKER> [autorentals-it-docker:1.0-SNAPSHOT] "autorentals-it-docker": Waited on url http://localhost:52368/api/autorentals 3321 ms=
Verify Container is Running and Mapped to Dynamic PortThe following output demonstrates checking the containing is running and allocated port number assigned.
$ docker ps CONTAINER ID IMAGE PORTS 1214c08d9732 autorentals-it-docker:1.0-SNAPSHOT 0.0.0.0:52368->8080/tcp
Stop ContainerThe following demonstrates how to stop the container(s) associated with this module using the
io.fabric8:docker-maven-plugin
Maven plugin.$ mvn docker:stop
-
The application’s
server.port
must be mapped to the dynamically generated network server port for the running Docker container -
The application must use a provided Spring profile (e.g.,
nosecurity
) -
The application may use HTTP (not HTTPS)
-
-
Define a JUnit test that will test the server-side components via HTTP endpoints
-
Accept/use a dynamically generated server port at runtime
-
Not host or use any server-side components locally
-
-
Extend the Maven project pom to execute the JUnit test during the Maven
integration-test
phase and ignored during the Maventest
phase.Execute JUnit Integration Test with FailsafeThe following shows an example output of satisfying the JUnit execution requirement using the Failsafe Maven Plugin.
$ mvn verify ... [INFO] --- maven-failsafe-plugin:3.3.1:integration-test (integration-test) @ autorentals-it-docker --- ... [INFO] Running info.ejava_student.starter.assignment4.it.MyIntegrationIT ... 17:16:15.678 main INFO .e.s.assignment4.it.MyIntegrationIT#logStarting:50 Starting MyIntegrationIT using Java 17.0.12 with PID 18304 (started in .../autorentals-it-docker) ... 17:16:17.284 main DEBUG i.e.e.c.web.RestTemplateLoggingFilter#intercept:37 GET http://localhost:52429/api/autorentals, returned 200 OK/200 sent: [Accept:"text/plain, application/json, ... rcvd: [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", ... {"contents":[]} [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.366 s -- in info.ejava_student.starter.assignment4.it.MyIntegrationIT [INFO] [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
-
Extend the Maven project pom to stop the Docker container once the test(s) have completed
Stop Docker Container After Integration Tests CompleteThe following shows an example output of satisfying the Docker stop requirement using the
io.fabric8:docker-maven-plugin
Maven plugin.$ mvn verify ... [INFO] --- docker-maven-plugin:0.44.0:stop (stop-container) @ autorentals-it-docker --- [INFO] DOCKER> [autorentals-it-docker:1.0-SNAPSHOT] "autorentals-it-docker": Stop and removed container 1214c08d9732 after 0 ms
-
Extend the Maven project pom to evaluate test results once server process(es) have been stopped
Verify Integration Test Results After Resources have Stopped[INFO] --- maven-failsafe-plugin:3.3.1:verify (verify) @ autorentals-it-docker --- [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS
-
Turn in a source tree with complete Maven modules that will build and automatically test the application.
-
Try to configure DEBUG so that the URL exchange is easy and obvious to observe.
The following shows example output of DEBUG with the full URL requested and the response.
16:16:45.771 main DEBUG i.e.e.c.web.RestTemplateLoggingFilter#intercept:37 GET http://localhost:52429/api/autorentals, returned 200 OK/200 ... {"contents":[]}
-
2.2.4. Grading
Your solution will be evaluated on:
-
dynamically generate test instance-specific property values in order to be able to run in a shared CI/CD environment
-
whether the HTTP server port used between the JUnit test and the server-side components was dynamically generated such that each execution of the build results in a different server port number used on the Docker host
-
-
start server process(es) configured with test instance-specific property values
-
whether the Docker container hosting the Spring Boot executable JAR was started and had its server port mapped to the dynamically generated value
-
-
implement a Maven Failsafe integration test in order to test a Spring Boot executable JAR running within a Docker container as a stand-alone process
-
whether a JUnit test was provided that implements tests using HTTP requests
-
-
execute tests against server process(es)
-
whether the JUnit test is client-side-only, with server-side components deployed only to the remote application
-
-
stop server process(es) at the conclusion of a test
-
whether the started server process is terminated at the end of the overall tests
-
-
evaluate test results once server process(es) have been stopped
-
whether test results are actually evaluated and expressed in the overall Maven build result.
-
2.2.5. Additional Details
-
You are permitted to implement the integration test in the same module or a downstream module relative to the actual Spring Boot application. However, there will be some extra issues related to sharing the Java JAR and repackaging into a Spring Boot Executable JAR in a downstream module to address.
-
the following shows an example downstream Integration Test module with only the IT artifacts and no
src/main
artifacts.|-- pom.xml `-- src |-- main | |-- docker (1) | | `-- Dockerfile | `-- java (nothing) `-- test |-- java | `-- info ... | `-- it | |-- IntegrationTestConfiguration.java | `-- MyIntegrationIT.java `-- resources `-- application-test.properties
1 the Dockerfile can be anywhere in your tree. This directory would likely house any additional artifacts that are part of the image. -
the downstream Integration Test module will have to declare a dependency on the upstream application module.
... <dependency> <groupId>info.ejava-student.starter.assignment3.autorentals</groupId> <artifactId>autorentals-security-svc</artifactId> <version>1.0-SNAPSHOT</version> </dependency> ...
-
the downstream module can use the
@SpringBootApplication
of an upstream module when present in the upstream module dependency. The example below shows an explicitmainClass
being identified in the upstream dependency.... <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>info.ejava_student.starter.assignment3.security.AutoRentalsSecurityApp</mainClass> ...
-
when running the IT test in the downstream module, the upstream application is what its being run. The following shows an example output from the upstream security application in the downstream IT module.
$ mvn spring-boot:run ... INFO 6781 --- [ main] i.e.s.a.security.AutoRentalsSecurityApp : Starting AutoRentalsSecurityApp using Java 17.0.12 with PID 6781 (.m2/repository/.../autorentals-security-svc-1.0-SNAPSHOT.jar started by jim in .../assignment4-autorentals-it/autorentals-it-springboot) ... INFO 6781 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/' INFO 6781 --- [ main] i.e.s.a.security.AutoRentalsSecurityApp : Started AutoRentalsSecurityApp in 1.782 seconds (process running for 2.064)
-
-
You may want to temporarily force a JUnit assertion error to verify the test results are being verified by Maven at the end.
-
You may optionally choose to build and manage Dockerfile/Docker Compose tasks using the io.fabric8:docker-maven-plugin, io.brachu:docker-compose-maven-plugin, or other plugins.
-
localhost
is the Docker host during normal development. That means JUnit can locate the Docker container usinglocalhost
during development. However,localhost
is not the Docker host in most Docker CI/CD environments. To be portable, your JUnit test must obtain the hostname from at runtime. The value ends up commonlyhost.docker.internal
and is discussed towards the end of the Docker IT lecture.
3. Assignment 4c: Testcontainers Option
There are two sections to this assignment option. You must complete them both.
3.1. Docker Image
3.1.1. Purpose
In this portion of the assignment, you will demonstrate your knowledge of building a Docker Image. You will:
-
build a layered Spring Boot Docker image using a Dockerfile and docker commands
3.1.2. Overview
In this portion of the assignment, you will be building a Docker image with your Spring Boot application organized in layers
3.1.3. Requirements
-
Provide a Maven project pom that will build the application as a Spring Boot executable JAR with the ability to extract layers.
Build Spring Boot Executable JARThe following shows an example output of satisfying the requirement to build the application.
$ mvn clean package ... [INFO] --- spring-boot-maven-plugin:3.3.2:repackage (build-app) @ autorentals-it-docker --- [INFO] Creating repackaged archive .../target/autorentals-it-docker-1.0-SNAPSHOT-bootexec.jar with classifier bootexec ...
Spring Boot Executable JAR is LayeredThe following shows an example output verifying the application packaging supports layers.
$ java -Djarmode=tools -jar target/autorentals-it-docker-*-bootexec.jar list-layers dependencies spring-boot-loader snapshot-dependencies application
-
Create a layered Docker image using a Dockerfile multi-stage build that will extend a JRE image and complete the image with your Spring Boot Application broken into separate layers.
Build Docker Image using LayersThe following shows an example output building a multi-stage Docker image, using layers.
$ docker build . -t test:test (1) [+] Building 5.3s (14/14) FINISHED ... => [builder 4/4] RUN java -Djarmode=tools -jar application.jar extract --layers --launcher --destination extracted ... => [stage-1 3/6] COPY --from=builder /builder/extracted/dependencies/ ./ => [stage-1 4/6] COPY --from=builder /builder/extracted/spring-boot-loader/ ./ => [stage-1 5/6] COPY --from=builder /builder/extracted/snapshot-dependencies/ ./ => [stage-1 6/6] COPY --from=builder /builder/extracted/application/ ./ => => naming to docker.io/test:test
1 the path provided will depend on the location you place your Dockerfile and files that go into the container -
The application API must listen to HTTP requests on a configured server port (with a default of 8080)
-
The application API must activate with a provided profile
-
The application should use HTTP and not HTTPS within the container.
Application Starts with Supplied Listen Port and Profile(s)The following shows an example output of satisfying that the application can use a provided HTTP port number and profile(s).
$ docker run --rm test:test --server.port=7777 --spring.profiles.active=nosecurity ... AutoRentalsSecurityApp : The following 1 profile is active: "nosecurity" ... TomcatWebServer : Tomcat started on port 7777 (http) with context path '/'
-
-
Turn in a source tree with complete Maven module containing the Dockerfile and source files. Use this exact same Dockerfile and application in the next section.
3.1.4. Grading
Your solution will be evaluated on:
-
build a layered Spring Boot Docker image using a Dockerfile and docker commands
-
whether you have a multi-stage Dockerfile
-
whether the Dockerfile successfully builds a layered version of your application using standard docker commands
-
3.1.5. Additional Details
-
You may optionally choose to build and manage Dockerfile/Docker Compose tasks using the io.fabric8:docker-maven-plugin, io.brachu:docker-compose-maven-plugin, or other plugins.
3.2. Testcontainers Integration Test
3.2.1. Purpose
In this portion of the assignment, you will demonstrate your knowledge of implementing a Maven Failsafe integration test using Docker and Testcontainers. You will:
-
start server container(s) prior to the start of tests
-
implement a Maven Failsafe integration test in order to test a Spring Boot executable JAR running within a Docker container as a stand-alone process
-
execute tests against server container(s)
-
stop server container(s) at the conclusion of tests
-
evaluate test results once server process(es) have been stopped
3.2.2. Overview
In this portion of the assignment you will be using a Docker container as your server process and managing it with Testcontainers.
3.2.3. Requirements
-
Provide a Maven project pom that will build the Spring Boot executable JAR (from the previous section)
-
Augment the project pom with Testcontainers dependencies for base capability and integration with JUnit.
-
Provide a JUnit test that will get executed during the
integration-test
phase (after the Spring Boot Executable JAR has been created) by the Maven Failsafe plugin.Vanilla Failsafe TestThere are no inputs to the test. It just has an ordering requirement to come after the Spring Boot executable JAR has been built. -
The JUnit test shall:
-
create a Testcontainers-managed container for the Spring Boot executable JAR using a either:
This means that you only define the container. Testcontainers will start and stop and provide information about the container.
-
@Testcontainers
-
@Container
-
source Dockerfile (best and easiest)
-
new ImageFromDockerfile()
-
withFileFromPath()
- for both Dockerfile and contextPath
-
-
an existing Docker image pre-built by the project’s pom using Maven plugins
-
-
expose the default API 8080 port
-
run the application with the desired Spring profile
See Other Common Property Sources course notes on options you have to set the profile property.
-
Dynamically assign any JUnit test-required properties that are derived from the running container.
-
it.server.host
-
it.server.port
-
-
-
Turn in a source tree with complete Maven modules that will build and automatically test the application.
The following is example output for a Maven/Failsafe build executing a JUnit test using Testcontainers
$ mvn clean verify ... [INFO] --- spring-boot-maven-plugin:3.3.2:repackage (build-app) @ autorentals-it-testcontainers --- [INFO] Creating repackaged archive .../target/autorentals-it-testcontainers-1.0-SNAPSHOT-bootexec.jar with classifier bootexec ... [INFO] --- maven-failsafe-plugin:3.3.1:integration-test (integration-test) @ autorentals-it-testcontainers --- [INFO] Running info.ejava_student.starter.assignment4.it.MyTestContainersIT ... INFO tc.localhost/testcontainers/fcbqgu1j8yyesl3b:latest -- Creating container for image: localhost/testcontainers/fcbqgu1j8yyesl3b:latest ... DEBUG i.e.e.c.web.RestTemplateLoggingFilter#intercept:37 GET http://localhost:56185/api/autorentals, returned 200 OK/200 sent: [Accept:"text/plain, application/json, application/xml, text/xml, application/*+json, application/*+xml, */*", Content-Length:"0"] rcvd: [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", X-Content-Type-Options:"nosniff", X-XSS-Protection:"0", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY", Content-Type:"application/json", Transfer-Encoding:"chunked", Date:"Sat, 12 Oct 2024 15:14:07 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"] {"contents":[]} [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 15.86 s -- in info.ejava_student.starter.assignment4.it.MyTestContainersIT
3.2.4. Grading
Your solution will be evaluated on:
-
implement a Maven Failsafe integration test in order to test a Spring Boot executable JAR running within a Docker container as a stand-alone process
-
whether the container was automatically started/stopped by Testcontainers
-
whether the Docker image was built within the scope of this module (either by Testcontainers during the test or via Maven plugins prior to the test)
-
whether the host and port for the container is obtained from Testcontainers
-
whether a JUnit test was provided that implements tests using HTTP requests
-
-
execute tests against server process(es)
-
whether the JUnit test is client-side-only, with server-side components deployed only to the remote application
-
-
evaluate test results once server process(es) have been stopped
-
whether test results are actually evaluated and expressed in the overall Maven build result.
-
3.2.5. Additional Details
-
You are permitted to implement the integration test in the same module or a downstream module relative to the actual Spring Boot application. However, there will be some extra issues related to sharing the Java JAR and repackaging into a Spring Boot Executable JAR in a downstream module to address.
-
the following shows an example downstream Integration Test module with only the IT artifacts and no
src/main
artifacts.|-- pom.xml `-- src |-- main | |-- docker (1) | | `-- Dockerfile | `-- java (nothing) `-- test |-- java | `-- info ... | `-- it | |-- IntegrationTestConfiguration.java | `-- MyTestContainersIT.java `-- resources `-- application-test.properties
1 the Dockerfile can be anywhere in your tree. This directory would likely house any additional artifacts that are part of the image. -
the downstream Integration Test module will have to declare a dependency on the upstream application module.
... <dependency> <groupId>info.ejava-student.starter.assignment3.autorentals</groupId> <artifactId>autorentals-security-svc</artifactId> <version>1.0-SNAPSHOT</version> </dependency> ...
-
the downstream module can use the
@SpringBootApplication
of an upstream module when present in the upstream module dependency. The example below shows an explicitmainClass
being identified in the upstream dependency.... <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>info.ejava_student.starter.assignment3.security.AutoRentalsSecurityApp</mainClass> ...
-
when running the IT test in the downstream module, the upstream application is what its being run. The following shows an example output from the upstream security application in the downstream IT module.
$ mvn spring-boot:run -Dspring-boot.run.profiles=nosecurity ... INFO 6781 --- [ main] i.e.s.a.security.AutoRentalsSecurityApp : ... Starting AutoRentalsSecurityApp using Java 17.0.12 with PID 34247 (.m2/repository/.../autorentals-security-svc-1.0-SNAPSHOT.jar started by jim in .../assignment4-autorentals-it/autorentals-it-testcontainers) ... INFO 6781 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/' INFO 6781 --- [ main] i.e.s.a.security.AutoRentalsSecurityApp : The following 1 profile is active: "nosecurity" ... INFO 6781 --- [ main] i.e.s.a.security.AutoRentalsSecurityApp : Started AutoRentalsSecurityApp in 1.782 seconds (process running for 2.064)
-
-
You may want to temporarily force a JUnit assertion error to verify the test results are being verified by Maven at the end.
-
You may optionally choose to build the Docker image using Testcontainers and a Dockerfile. Alternately, you may chose to use io.fabric8:docker-maven-plugin, io.brachu:docker-compose-maven-plugin, or other Maven plugins outside of Testcontainers — but that would defeat much of the simplicity of this overall option.
-
localhost
is the Docker host during normal development. That means JUnit can locate the Docker container usinglocalhost
during development. However,localhost
is not the Docker host in most Docker CI/CD environments. To be portable, your JUnit test must obtain the hostname from at runtime. The value ends up commonlyhost.docker.internal
and is discussed towards the end of the Docker IT lecture.