I am trying to setup a simple, straight forward setup to run some UI automation tests on a Gitlab pipeline. I am using the following Dockerfile
to generate the image:
# Use a base image with Selenium and Chrome
FROM selenium/standalone-chrome:latest
# Set environment variables for Selenium, Chromium, and Chromedriver versions
ENV JDK_VERSION=17
ENV GRADLE_VERSION=8.7
ENV SELENIUM_VERSION=4.19.1
ENV CHROMEDRIVER_VERSION=114.0.5735.90
ENV LOG4J_VERSION=2.17.1
ENV JUNIT_VERSION=5.9.1
ENV MOCKITO_INLINE_VERSION=5.2.0
ENV MOCKITO_VERSION=5.11.0
ENV LOMBOK_VERSION=1.18.32
# Install OpenJDK 17
USER root
RUN apt-get update && apt-get install -y
openjdk-17-jdk
&& rm -rf /var/lib/apt/lists/*
# Install Git
RUN apt-get update && apt-get install -y git
# Create directory for JAR files
RUN mkdir -p /home/gradle/libs/
# Download and install Chromedriver
RUN wget -q "https://chromedriver.storage.googleapis.com/$CHROMEDRIVER_VERSION/chromedriver_linux64.zip" -O /tmp/chromedriver.zip
&& unzip -o /tmp/chromedriver.zip -d /usr/local/bin
&& rm /tmp/chromedriver.zip
# Download Gradle 8.7
RUN wget -q "https://services.gradle.org/distributions/gradle-$GRADLE_VERSION-bin.zip" -O /tmp/gradle.zip
&& unzip -o /tmp/gradle.zip -d /home/seluser
&& rm /tmp/gradle.zip
&& mv /home/seluser/gradle-$GRADLE_VERSION /home/seluser/gradle
# Set Gradle home environment variable
ENV GRADLE_HOME /home/seluser/gradle
# Update PATH to include Gradle bin directory
ENV PATH $PATH:$GRADLE_HOME/bin
# Download Selenium JAR
RUN wget -q "https://repo1.maven.org/maven2/org/seleniumhq/selenium/selenium-java/$SELENIUM_VERSION/selenium-java-$SELENIUM_VERSION.jar" -O /home/gradle/libs/selenium-java.jar
# Download Log4j JARs
RUN wget -q "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/$LOG4J_VERSION/log4j-api-$LOG4J_VERSION.jar" -O /home/gradle/libs/log4j-api.jar &&
wget -q "https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/$LOG4J_VERSION/log4j-core-$LOG4J_VERSION.jar" -O /home/gradle/libs/log4j-core.jar
# Download JUnit JARs
RUN wget -q "https://repo1.maven.org/maven2/org/junit/jupiter/junit-jupiter/$JUNIT_VERSION/junit-jupiter-$JUNIT_VERSION.jar" -O /home/gradle/libs/junit-jupiter.jar
# Download Mockito JARs
RUN wget -q "https://repo1.maven.org/maven2/org/mockito/mockito-inline/$MOCKITO_INLINE_VERSION/mockito-inline-$MOCKITO_INLINE_VERSION.jar" -O /home/gradle/libs/mockito-inline.jar &&
wget -q "https://repo1.maven.org/maven2/org/mockito/mockito-junit-jupiter/$MOCKITO_VERSION/mockito-junit-jupiter-$MOCKITO_VERSION.jar" -O /home/gradle/libs/mockito-junit-jupiter.jar
# Download Lombok JARs
RUN wget -q "https://repo1.maven.org/maven2/org/projectlombok/lombok/$LOMBOK_VERSION/lombok-$LOMBOK_VERSION.jar" -O /home/gradle/libs/lombok.jar
# Switch to the user seluser
USER seluser
# Command to run (optional)
CMD ["bash"]
I have built the above Dockerfile and all the dependencies and packages are installed correctly.
As part of my unit tests, I have some Mockito tests to verify Chrome is runnable (in headless mode) inside the image. Here is my ChromeDriverManager
:
package drivers.managers;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import drivers.DriverManager;
import drivers.Environment;
@Log4j2
public class ChromeDriverManager extends DriverManager {
@Setter
private Environment driverEnvironment;
@Getter
private WebDriver driver;
/**
* Generate a new Driver Manager on the supplied Environment.
*
* @param environment Environment to use (eg, LOCAL, DOCKER, etc.)
*/
public ChromeDriverManager(final Environment environment) {
driverEnvironment = environment;
}
/**
* Initialize the Chrome Selenium WebDriver. Should be called from within your Test setup.
*/
public void initDriver() {
final long startTime = System.currentTimeMillis();
final ChromeOptions chromeOptions = new ChromeOptions();
log.debug("Initializing driver...");
// Kill the previous WebDriver, if any
closeDriver();
switch (driverEnvironment) {
case LOCAL -> {
driver = new ChromeDriver(chromeOptions);
}
case DOCKER -> {
chromeOptions.addArguments("--headless");
chromeOptions.addArguments("--no-sandbox");
chromeOptions.addArguments("--window-size=1600,1200");
driver = new ChromeDriver(chromeOptions);
}
default -> throw new UnsupportedOperationException(
String.format("Environment "%s" is not yet implemented.", driverEnvironment));
}
log.debug(String.format("Generated new web driver object. driver=%s timeMs=%d", driver,
(System.currentTimeMillis() - startTime)));
}
/**
* Close the WebDriver instance.
*/
public void closeDriver() {
if (driver != null) {
log.debug("Quitting driver instance: {}", driver);
driver.quit();
}
}
}
Here is my unit tests:
package drivers.managers;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.UnreachableBrowserException;
import drivers.Environment;
public class ChromeDriverManagerTest {
@Mock
private WebDriverFactory webDriverFactory;
@InjectMocks
private ChromeDriverManager chromeDriverManager;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
private void testInitDriver(final Environment environment) {
final ChromeOptions chromeOptions = new ChromeOptions();
when(webDriverFactory.createChromeDriver(chromeOptions)).thenReturn(mock(ChromeDriver.class));
chromeDriverManager.setDriverEnvironment(environment);
chromeDriverManager.initDriver();
assertTrue(isBrowserAlive());
chromeDriverManager.closeDriver();
}
@Test
void testInitDriverDocker() {
testInitDriver(Environment.DOCKER);
}
@Test
void testInitDriverUnsupportedEnvironment() {
chromeDriverManager = new ChromeDriverManager(Environment.UNSUPPORTED);
// When trying to initialize driver with unsupported environment, UnsupportedOperationException should be thrown
assertThrows(UnsupportedOperationException.class, chromeDriverManager::initDriver);
}
/**
* Checks if the browser is alive by navigating to google.com.
* @return - True of False
*/
private boolean isBrowserAlive() {
try {
chromeDriverManager.getDriver().navigate().to("https://www.google.com");
return true;
}
catch (final UnreachableBrowserException e) {
return false;
}
}
interface WebDriverFactory {
WebDriver createChromeDriver(ChromeOptions options);
}
}
I build my repo using ./gradlew build
or gradle build (in the image)
.
My issue is everything works as expected on my laptop (Macbook) while I get the following error inside the docker image:
* What went wrong:
Execution failed for task ':compileJava'.
> Java compiler is not available. Please check that /usr/lib/jvm/temurin-17-jre-amd64 contains a valid JDK installation.
I feel I am missing something in the Docker installation or I am not using the right combination for my Selenium, Chrome, JDK17, Gradle, Mockito, etc. setup. Can someone help me (or guide) to the right combination of dependencies for setting this up?