docker-container-test-header

Selenium WebDriver – How To Run Automated Tests Inside A Docker Container – Part 1

Overview:

Testautomationguru already has few docker and selenium related articles which talks about setting up the dockerized selenium grid.

  1. Setting up Dockerized Selenium grid.
  2. Managing Dockerized Selenium Grid using Arquillian cube.
  3. Setting up Dockerized Selenium grid in AWS / Cloud using RancherOS

Even if we have a selenium grid, we still need a host to run our tests – ie.. a host is required to run your maven command/ant/some batch file to execute the tests – so that test could talk to remote grid to send the WebDriver related instruction. In this article, we will see how we could use a container (as a host) for running our tests.

Challenges In Test Execution:

We already have simplified our selenium grid set up using docker using the above articles. But we still face few challenges. Your tests still have few dependencies to run them!

  • Test depends on specific version of java (like java 8)
  • Host should have maven or Ant or Gradle or other build tools installed
  • Tests depend on number of 3rd party jar files and other files like csv etc.
  • Tests depend on few jar files which are not part of maven repo!!
  • Remote slaves need to be set up with the build tools, java and any dependent jar files to execute the tests.

Managing these dependencies & Setting up remote slaves are time-consuming for big applications which has thousands of test cases to run on a daily basis on a multiple slave machines.

container-test-prop

That too, if you are planning to move AWS /cloud in future, How can you set this up all programmatically?

We will see how docker could help us here!!

Considering the length of the article, I divided this into 2. I would suggest you to read the second part as well. I have attached a sample project in the Part 2.

Sample Test:

To show a demo, I am creating a simple maven project. I add the selenium-java & testNG dependencies.

<dependencies>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.11</version>
        </dependency>
</dependencies>

I add the below page object.

  • A Google Page Object which searches for a string and return search results
public class GooglePage {

    private final WebDriver driver;
    private final WebDriverWait wait;

    @FindBy(name = "q")
    private WebElement searchBox;

    @FindBy(css = "input.lsb")
    private WebElement searchButton;

    @FindBy(className = "rc")
    private List<WebElement> searchResults;

    @FindBy(id = "foot")
    private WebElement footer;

    public GooglePage(final WebDriver driver) {
        this.driver = driver;
        PageFactory.initElements(driver, this);
        this.wait = new WebDriverWait(driver, 30);
    }

    public void goTo() {
        this.driver.get("https://www.google.com");
    }

    public void searchFor(String text) throws InterruptedException {
        this.searchBox.sendKeys(text);
        wait.until(ExpectedConditions.elementToBeClickable(this.searchButton));
        this.searchButton.click();
        wait.until(ExpectedConditions.presenceOfElementLocated(By.className("rc")));
    }

    public List<WebElement> getResults() {
        return this.searchResults;
    }

}

A corresponding TestNG Test would be as shown here.

  • The test searches for ‘automation’ and checks if the search results count is at least 10.
public class GoogleTest {

    private WebDriver driver;
    private GooglePage google;

    @BeforeTest
    public void setUp() throws MalformedURLException {
        DesiredCapabilities dc = DesiredCapabilities.chrome();
        driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), dc);
        google = new GooglePage(driver);
    }

    @Test
    public void googleTest() throws InterruptedException {
        google.goTo();
        google.searchFor("automation");
        Assert.assertTrue(google.getResults().size() >= 10);
    }
    
    @AfterTest
    public void tearDown() throws InterruptedException {
        driver.quit();
    }    

}

Now You could simply execute the test,

  • Using maven
mvn clean test
  • Once I have packed them as a jar, I can also execute the test in the command line as shown here
java -cp mytest.jar org.testng.TestNG -testclass org.testautomationguru.test.GoogleTest

If I need to execute my tests using Jenkins in a remote slave, I need to ensure that my remote machine also has  java, maven etc. If the test depends on few jar files which are not part of maven repo, We need to ensure that it is copied to the appropriate remote local maven repo.  Basically – you need to set up entire dependencies in each machine for your tests to execute!  A lot of manual work!! This is where Docker is going to help.

Dockerfile Maven Plugin:

Maven has a dockerfile plugin which nicely integrates docker and maven. It automates docker image build process in maven. By issuing a maven command, maven

  • pulls all the dependencies
  • compiles
  • tests
  • packages all the classes into a single jar with all the dependencies
  • also creates a docker image (based on the docker file)

My project looks like this.

container-proj

To use the plugin, I add the below dependency in the maven pom file.

<plugin>
    <groupId>com.spotify</groupId>
    <artifactId>dockerfile-maven-plugin</artifactId>
    <version>1.3.5</version>
    <executions>
        <execution>
            <id>default</id>
            <goals>
                <goal>build</goal>
                <goal>push</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <!-- vinsdocker is org name. -->
        <!-- containertest is the name of the image /  application  -->
        <repository>vinsdocker/containertest</repository>
        <!-- version of your image: could be sprint11 or release5 etc -->
        <tag>googletest</tag>
    </configuration>
</plugin>

My Dockerfile is as shown below. My maven creates a single jar (container-test.jar) with all the dependencies by using maven-assemply-plugin.

Dockerfile:

FROM openjdk:8-jre-slim

# Add the jar with all the dependencies
# Maven creates container-test.jar in the target folder of my workspace.
# We need this in some location - say - /usr/share/tag folder of the container
ADD  target/container-test.jar /usr/share/tag/container-test.jar

# Command line to execute the test
ENTRYPOINT ["/usr/bin/java", "-cp", "/usr/share/tag/container-test.jar", "org.testng.TestNG", "-testclass", "com.testautomationguru.container.test.GoogleTest"]

When I issue the below command, maven takes care of building the project and also creates a docker image.

mvn clean package

dockerfile-maven-build

It builds a new docker image which could be accessible using vinsdocker/containertest.

 


Note:

By default the plugin will try to connect to docker on localhost:2375. If the docker is not running on your machine and you would like to use remote docker, set the DOCKER_HOST environment variable.

DOCKER_HOST=tcp://<host>:2375

Now the docker image has been built with all the dependencies. An entrypoint has also been added – it means whenever you create a container (an instance of the docker image), it starts executing the test.

In my remote slave, I need to issue only below command – docker run vinsdocker/containertest:googletest

sudo docker run vinsdocker/containertest:googletest
Aug 27, 2017 4:06:05 AM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: OSS
Browser launched and navigated to Google
automation - entered for search
Search button clicked
Results appeared
Results Count : 11

===============================================
Command line suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================

I explicitly added few comments in the page object for better understanding.

Now we do not need to worry about the dependencies to run the automated test. As everything to run the test is packaged as an image. We need only machines (any OS ) with docker installed!  Issuing below command is enough to run our test in the cloud!! Docker will automatically download the image, creates an instance/container, runs the test and exits!!

docker run vinsdocker/containertest:googletest

container-test-doc-02

I have uploaded this project in GitHub – Please continue reading the Part 2 of this article to get the Github link.

Summary:

Docker not only helps us in setting up the selenium grid – but also simplifies managing test dependencies. Maven creates a new docker image as part of maven build process by using the given Dockerfile. Once the image is built, any machine with docker can execute the test!  It saves a lot of manual work in setting up the remote slave for execution.

We are not yet done!! We can still improve the process. Please continue reading this article – Running Dockerized Selenium Suites – Part 2

 

Happy Testing & Subscribe 🙂

 

Share This:

Categories: Articles, CI / CD / DevOps, Docker, Selenium

5 comments

  • Mephisto

    I get an error:
    Error: Main method not found in class com.testautomationguru.container.test.GoogleTest, please define the main method as:
    public static void main(String[] args)
    or a JavaFX application class must extend javafx.application.Application

    dont know what im doing wrong, everything seems ok. Its weird because testng tests doesnt require java main methods

      • Mephisto

        I use now the exactly same POM but it fails:

        First the system gives a warning:
        [WARNING] Artifact: UltimateTest:UltimateTest:jar:0.0.1-SNAPSHOT references the same file as the assembly destination file. Moving it to a temporary location for inclusion.

        Then it crashes:

        Step 2/3 : COPY ./target/UltimateTest-0.0.1-SNAPSHOT.jar ./usr/share/tag/UltimateTest.jar
        [ERROR] COPY failed: stat /var/lib/docker/tmp/docker-builder025734886/target/UltimateTest-0.0.1-SNAPSHOT.jar: no such file or directory

  • Musaffir

    Hello Vinoth,

    I understood till you make a docker image. May I know how you transfer the image to remote slave for docker to execute the image ? I meant to say when u execute docker run command for an image, the docker must have access to the image…. how we handle this ( i am just beginning with docker and try to understand the concept )

Leave a Reply

Your email address will not be published. Required fields are marked *