-
Notifications
You must be signed in to change notification settings - Fork 4
Example: TestContainers
David Matějček edited this page Nov 6, 2024
·
3 revisions
This is probably the simplest possible test with GlassFish and TestContainers. It automatically starts the GlassFish Docker Container and then stops it after the test. The test here is quite trivial - downloads the welcome page and verifies if it contains expected phrases.
If you want to run more complicated tests, the good path is to
- Write a singleton class managing the GlassFish Docker Container or the whole test environment.
- Write your own Junit5 extension which would start the container before your test and ensure that everything stops after the test including failures.
- You can also implement direct access to the virtual network, containers, so you can change the environment configuration in between tests and simulate network failures, etc.
package org.omnifish.testcon.tc;
import java.io.IOException;
import java.io.InputStream;
import java.lang.System.Logger;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.Duration;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.exporter.ZipExporter;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.io.TempDir;
import org.omnifish.app.SimpleApplication;
import org.omnifish.app.SimpleResource;
import org.testcontainers.containers.Container.ExecResult;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.MountableFile;
import static java.lang.System.Logger.Level.INFO;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.stringContainsInOrder;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class WelcomePageIT {
private static final Logger LOG = System.getLogger(AsadminTest.class.getName());
private static final String APP_NAME = "application";
private static final String APP_FILENAME = APP_NAME + ".war";
@TempDir
private static Path tempDir;
private static Path applicationPath;
private static GenericContainer<?> server;
@BeforeAll
public static void init() {
// Create a war file using ShrinkWrap.
// You can use also a real war file instead.
WebArchive webArchive = ShrinkWrap.create(WebArchive.class).addClass(SimpleResource.class)
.addClass(SimpleApplication.class).addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
// Print the list of files in the war file for sure. You can skip this.
LOG.log(INFO, webArchive.toString(true));
applicationPath = tempDir.resolve(APP_FILENAME);
webArchive.as(ZipExporter.class).exportTo(applicationPath.toFile(), true);
// Instead of latest it is better to keep tests more consistent and use some concrete version.
server = new GenericContainer<>("ghcr.io/eclipse-ee4j/glassfish:latest")
.withCommand("startserv")
// List of internal ports mapped to ports of your host.
.withExposedPorts(8080)
// GlassFish produces log inside the container, here you define what to do with the output.
.withLogConsumer(o -> System.err.print("GF: " + o.getUtf8String()))
// Here you copy the file from your host to the container.
// Alternatively, you can copy it elsewhere and use
// the asadmin deploy command to deploy your application.
.withCopyFileToContainer(MountableFile.forHostPath(applicationPath),
"/opt/glassfish7/glassfish/domains/domain1/autodeploy/" + APP_FILENAME)
// Here you define how TestContainers find out that the server side is ready for testing.
.waitingFor(
Wait.forLogMessage(".*Successfully autodeployed.*", 1)
.withStartupTimeout(Duration.ofSeconds(10L)))
;
// Everything is set, lets start the server!
server.start();
}
/**
* Stop the server after all tests in this class.
* Alternatively you can move start+stop and even the war file deployment to a JUnit5 extension.
*/
@AfterAll
public static void stopContainer() throws Exception {
if (server == null) {
return;
}
LOG.log(INFO, "Stopping server");
ExecResult result = server.execInContainer("/opt/glassfish7/bin/asadmin", "stop-domain", "--kill");
LOG.log(INFO, "Result: {0}", result.getStdout());
server.close();
LOG.log(INFO, "Server stopped!");
}
/**
* Tests the welcome page of the glassfish which is always at the root context
* unless you deploy your app to the root context.
*/
@Test
void getRoot() throws Exception {
String content = get("");
assertThat(content, stringContainsInOrder("Eclipse GlassFish", "index.html", "production-quality"));
}
/*
* Tests the response from your application.
* The context is set by the war file name or by web.xml settings
* or by asadmin deploy command arguments.
* Here we used the autodeploy and no web.xml.
*/
@Test
void getApplication() throws Exception {
assertThat(get(APP_NAME), stringContainsInOrder("Hello TestCon!"));
}
/**
* HTTP GET implementation. You can use any client implementation suitable for your needs instead.
*/
private String get(String endpointRelativePath) throws IOException {
URL url = URI.create("http://localhost:" + server.getMappedPort(8080)
+ "/" + endpointRelativePath).toURL();
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try {
connection.setConnectTimeout(10);
connection.setReadTimeout(1000);
connection.setRequestMethod("GET");
assertEquals(200, connection.getResponseCode(), "Response code");
try (InputStream in = connection.getInputStream()) {
return new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
} finally {
connection.disconnect();
}
}
}
Well, there might be a question for the servlet class.
package org.omnifish.app;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.core.Response;
@SuppressWarnings("javadoc")
@Path("/")
public class SimpleResource {
@GET
public Response getResponse() {
return Response.ok("Hello TestCon!").build();
}
}
package org.omnifish.app;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
import java.util.Set;
@SuppressWarnings("javadoc")
@ApplicationPath("")
public class SimpleApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
return Set.of(SimpleResource.class);
}
}