Search:

Creating End-to-End Web Test Automation Project from Scratch — Part 5

A wonderful serenity has taken possession of my entire soul.

Creating End-to-End Web Test Automation Project from Scratch — Part 5

Let’s Integrate Our Dockerized Web Project with CI/CD Pipeline!

Welcome to the 5th part of the blog post series called “Creating an End-to-End Web Test Automation Project from Scratch.” So far, we have covered many topics, from the creation of a project to containerizing it with Docker. If you need to review the previous chapters, you can find the articles at the links below.

 

  • Let’s Integrate Our Dockerized Web Test Automation Project with CI/CD Pipeline!
  • Auto-Scaling and Kubernetes Integration with KEDA

 

In this chapter, you will integrate CI/CD into your project with Jenkins and execute your test in a Docker container on a scheduled basis. Also, you will create a job that rebuilds and pushes your Docker image to the hub! Let’s start!

Jenkins Installation

First, install Jenkins via homebrew:


brew install jenkins-lts

After the installation, start Jenkins:


brew services start jenkins-lts

The default port for Jenkins is 8080. Go to the Jenkins page at “http://localhost:8080

Jenkins will create a password for you in a file and state its location.

  1. Go to that location
  2. Copy the password
  3. Paste it into the input field on Jenkins page and continue.
  4. Then create a username/password pair for yourself.

 

For the official installation guide, please refer to Jenkins Documentation.

Configurations

Environment

Before creating jobs for Jenkins, you need to configure it first. Go to Dashboard -> Manage Jenkins -> Configure System. Under the Global properties, check Environment variables.

manage-jenkins

 

By stating your path here, you enable Jenkins to run Ruby, Docker and other applications on your machine.

You can get all your path variables via the command below:


echo $path 

Note that you need to replace blanks with colons. For example:

Your Path:


/Users/muhammettopcu/Library/Android/sdk/tools /Users/muhammettopcu/Library/Android/sdk/platform-tools /Users/muhammettopcu/.jbang/bin /opt/homebrew/bin /opt/homebrew/sbi

Your Path for Jenkins:


/Users/muhammettopcu/Library/Android/sdk/tools:/Users/muhammettopcu/Library/Android/sdk/platform-tools:/Users/muhammettopcu/.jbang/bin:/opt/homebrew/bin

Note: If you are on Windows, concatenate them with semicolons.

 

Plug-ins

Now go to Dashboard -> Manage Jenkins -> Manage Plugins.

manage-plugins

In this screen, you will install the plugins that you need:

  1. Cucumber Reports: This enables you to have test reports visually.
  2. Git Parameter: This lets you pick a branch on your project and run the code inside.
  3. GitHub (if not installed already): This one enables you to integrate with GitHub features such as Webhooks.
  4. CloudBees Docker Build and Publish: This is to build a Docker image and publish it.

Credentials

Now you will configure credentials for both GitHub and DockerHub.

You need to navigate to Dashboard -> Manage Jenkins -> Credentials.

credentials

Then click “(global)” domain.

Click +Add Credentials button to add your credentials. 

 

For DockerHub:

 

  1. Kind: Username with password.
  2. Username: Username of your DockerHub account.
  3. Password: Access Token generated on DockerHub.

    To create your access token, log in to your DockerHub account. Go to Account Settings:

    account-settings

    Then go to the Security panel.

     security


    Click on the New Access Token button. Give your access token a name and R-W-D rights. Then click on Generate.

    new-access-token

  4. ID: ID to be used in Jenkinsfile or for other configurations: dockerhub.
  5. Description: Description of the credential. Here, dockerhub again.

For GitHub:

  1. Kind: Username with password.
  2. Username: Username of your GitHub account.
  3. Password: Access Token generated on GitHub.

    To create your access token, log in to your GitHub account. Go to Settings -> Developer settings -> Personal Access Tokens and click the Generate new token button. Give a name to your token and generate it.
  4. ID: ID to be used in Jenkinsfile or for other configurations: github.
  5. Description: Description of the credential. Here, github again.

 

Ruby Web Project

Also, let’s make a small addition to your code to generate Cucumber reports:

  1. First, create a new folder named “reports” under your project directory.

    ruby-web-project
  2. Then in the cucumber.yml file (if you don’t have yet, create one under the project directory), add `--format json --out reports/reports.json` to your default profile. This will let you create test execution reports under the reports folder in JSON format, named reports.json


`default: "--format progress --format json --out reports/reports.json"`

Dockerfile

This time, you will not build your Docker image with the files residing in your local. Instead, you will get the files directly from the GitHub repository. So you need to alter your dockerfile!

The first couple of lines are the same:


FROM ruby:3.0

RUN apt update && apt install git
#RUN apk update && apk add --no-cache build-base && apk add git
RUN apt-get install -y ffmpeg && apt-get install bc
WORKDIR /usr/src/app

You will make changes starting from this line:


RUN curl -o /usr/src/app/Gemfile https://raw.githubusercontent.com/kloia/dockerize-ruby-web-project/master/Gemfile

With the `curl` command, you download your Gemfile from your remote directory to the workdir. Note that https://raw.githubusercontent.com enables you to download individual files as plain text.


RUN gem install bundler && bundle install

First, install your gems.


RUN cd /usr/src/app && git init && git remote add origin https://github.com/kloia/dockerize-ruby-web-project.git && git fetch && git checkout -f origin/master

Now let’s look at the above RUN command bit by bit:

- cd /usr/src/app` navigates to workdir.

- `git init` initializes git.

- `git remote add origin https://github.com/kloia/dockerize-ruby-web-project.git` adds your existing GitHub project as a remote repository to git.

- `git fetch` fetches everything from the remote repository.

- `git checkout -f origin/master` Forces checkout to origin/master. “Why force it?” Otherwise, you can’t checkout since the Gemfile you downloaded in the earlier step conflicts with this command.

The rest of the file is the same.


CMD parallel_cucumber -n 2
EXPOSE 5000:5000

So your final dockerfile looks like this below:


FROM ruby:3.0
RUN apt update && apt install git
#RUN apk update && apk add --no-cache build-base && apk add git
RUN apt-get install -y ffmpeg && apt-get install bc
WORKDIR /usr/src/app
RUN curl -o /usr/src/app/Gemfile https://raw.githubusercontent.com/kloia/dockerize-ruby-web-project/master/Gemfile
RUN gem install bundler && bundle install
RUN cd /usr/src/app && git init && git remote add origin https://github.com/kloia/dockerize-ruby-web-project.git && git fetch && git checkout -f origin/master
CMD parallel_cucumber -n 2
EXPOSE 5000:5000

Jenkins Job Creations

Test Run Job with Parameters

Let’s create a jenkinsfile with your favourite file editor. The first part of your file is:


pipeline {
    agent any
        parameters {      
        choice(name: 'Headless', choices: ['true', 'false'], description: '')
        choice(name: 'Browser', choices: ['remote-chrome', 'remote-firefox'], description: '')
        string(name: 'ThreadCount', defaultValue: '1', description: '')
        string(name: 'Retry', defaultValue: '1', description: '')
            gitParameter name: 'BRANCH_TAG',
                        type: 'PT_BRANCH',
                        defaultValue: 'master',
                        selectedValue: 'DEFAULT',
                        quickFilterEnabled: true,
                        sortMode: 'DESCENDING_SMART',
                        tagFilter: '*',
                        branchFilter: 'origin/(.*)',
                        useRepository: '.*.git',
                        description: 'Select your branch'
    }

- In the parameters section, you create your sections for the parameters, which you will use while running parallel_cucumber command in the command line.

- The gitParameter lets you choose different branches on which you run your code. Note that you also have a video_recording branch with the configurations of Chapter 2.1.

 

git-parameter

 

Now next, you are going to define your stages.


stages {
        stage('Checkout & Run Tests') {
            steps {
                sh "docker run muhammettopcu/dockerize-ruby-web:latest git checkout ${params.BRANCH_TAG} && parallel_cucumber -n ${params.ThreadCount.toInteger()} -o 'headless=${Headless} browser=${Browser} --retry ${params.Retry.toInteger()}'"
            }
        }
    }


    }

 

stages

 

You use the `docker run` command with two parts:

- `git checkout ${params.BRANCH_TAG}` checkouts to a specified branch.

- `parallel_cucumber -n ${params.ThreadCount.toInteger()} -o 'headless=${Headless} browser=${Browser} --retry ${params.Retry.toInteger()}'` runs cucumber with the specified arguments in Jenkins build.

And last, let’s define your post actions.


post {
       always {
           cucumber([
               buildStatus: 'null',
               customCssFiles: '',
               customJsFiles: '',
               failedFeaturesNumber: -1,
               failedScenariosNumber: -1,
               failedStepsNumber: -1,
               fileIncludePattern: '**/*.json',
               jsonReportDirectory: 'reports',
               pendingStepsNumber: -1,
               reportTitle: 'Cucumber Report',
               skippedStepsNumber: -1,
               sortingMethod: 'ALPHABETICAL',
               undefinedStepsNumber: -1
           ])
       }
       success{
           script{
               sh "echo successful"
          }
       }
       failure{
           script{
               sh "echo failed"
          }
       }
       aborted{
           script{
               sh "echo aborted"
           }
       }
   }
}

In the always action, you define your Cucumber plug-in. Note that `jsonReportDirectory: 'reports'` is the directory that you created for the report files in your project. You might ask “But I am running my tests in a Docker container. The reports would be generated inside the container. How will Jenkins reach these?” A valid point, indeed! It works because Docker Plugin automatically syncs Jenkins’ workspace and the container’s workdir. If it didn’t, you would have needed to do it manually with volumes when executing `docker run` command. 

For the success, failure and aborted events, there is only a simple echo command. You may want to implement a notification script for your favourite messaging applications such as Discord or Slack. If you are interested in doing this, me and my colleagues have covered this topic in a recent webinar, here is the link for the recording.

This is it. Let’s save and move it to your project directory. It is named jenkins_cucumber without any extensions. Do not forget to push it to your GitHub repository since you’ll make Jenkins pull it from there.

Now go to Jenkins and create your first job with the jenkinsfile that you created. First, you need to click the New Item button on the Dashboard.

dashboard

Now write your job name and choose Pipeline option.

pipeline

Now configure your Job.

  • If you want it to run every hour such as 07:00, 08:00, you need to use 
    `0 * * * *`

build-triggers

This is called “cron expression”. You can configure it according to your own needs. For detailed information, please refer to Jenkins Documentation.

Now in the Pipeline section, choose Pipeline script from SCM and as SCM choose Git.

pipeline-script-from-scm

Now state your GitHub repository and credentials.

repositories

And finally, you need to give the path of your jenkinsfile here:

script-path

Note that this path is of your `jenkinsfile` residing in your Github repository. If you did not put it in the root directory, then change the path accordingly.

Now you can see your job in the Dashboard!ruby-web-project-test

Let’s spin up your Selenium server with Docker Compose and run your job manually, just for this time.

docker-compose

Choose your parameters and click “Build” button. The tests are running:

build

Let’s see your results:

run-test-result

Some of them failed. Open the build on the left side by clicking on it:

build-history

On the opened panel, click Cucumber Reports:

cucumber-reports

Hey, your test results are visualized!

test-result

Automated Docker Image Build with GitHub Webhook

Now let’s create a Jenkins job that rebuilds your project image and pushes it to DockerHub. You will trigger this job with GitHub webhook, which pings Jenkins only when there is a push action in your repository.


pipeline {
  agent any
  options {
    buildDiscarder(logRotator(numToKeepStr: '5'))
  }
  environment {
    DOCKERHUB_CREDENTIALS = credentials('dockerhub')
  }
  stages {
    stage('Build') {
      steps {
        sh "docker build -t muhammettopcu/dockerize-ruby-web:latest https://github.com/kloia/dockerize-ruby-web-project.git"
      }
    }
    stage('Login') {
      steps {
        sh "echo $DOCKERHUB_CREDENTIALS_PSW | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin"
      }
    }
    stage('Push') {
      steps {
        sh "docker push muhammettopcu/dockerize-ruby-web:latest"
      }
    }
  }
  post {
    always {
      sh 'docker logout'
    }
  }
}

 

1. In the options block, you restrict your Job to keep only the latest five builds. The older ones are discarded.


2. In the environment block, you assign DockerHub credentials to a variable named DOCKERHUB_CREDENTIALS.

 

3. You have three stages this time:


- Build: It builds your project with a tag number. (In this instance, I just wanted to overwrite my builds, so I used `latest` as a tag number. You may want to implement an algorithm to track your version numbers.)

- Login: It logins to DockerHub account with the credentials you created in the previous steps.

- Push: It pushes your newly created image to your DockerHub repository.

 

4. And in the always block, you log out from docker.

 

Now let’s create your job. You know the drill this time.

The only thing different from the previous job is the Build Triggers section. Choose “GitHub hook trigger for GITScm polling” option.

github-build-triggers

 

Note: The Jenkins file name for this job should be different from the first one.

 

Now go to the directory of your project on GitHub. In there, navigate to Settings -> Webhooks.

kloia_dockerize-ruby-web-project


On this page, click `Add webhook` button:

webhooks-guide

 

The Payload URL should be in this form: https://your-jenkins-domain/github-webhook/

webhooks-add

Since I am testing Jenkins on my local machine, I need to expose my 8080 port to the internet. Either I use “port forwarding” or “reverse proxy” for this. 

My internet provider uses CGNAT, so the first one is not an option for me. Therefore I chose localhost.run service.

Why did I prefer this service? Ngrok is another option but the free version puts an intermediate layer that gives warning to the user who wants to reach the website. It is for phishing protection, but because of this layer Webhook can not reach Jenkins.

If you already own a premium Ngrok service or if you run your Jenkins in any other public server, you may not need to use this option.

So, you need to create an SSH key pair for this:

  1. Type `ssh-keygen -b 4096 -t rsa` and press enter.
  2. A file name prompt will be shown. Simply press enter to use the default value.
  3. You will enter the passphrase for the SSH key pair in the upcoming step.

Now expose your port with the command below:

 

`ssh -R 80:localhost:8080 nokey@localhost.run`

localhost-command

Type your passphrase and press enter.

It gave me the “https://a82b03292e9fbd.lhr.life” URL to reach my port. Write your own to WebHook’s Payload section:

 

payload-url

Then choose “Just the push event.” option and click `Add webhook` button. Note that you can specify your webhook’s triggers with the “Let me select individual events.” option.

webhook-trigger

Now you have your WebHook ready.

webhooks

Let’s make a change in your project file and push it to your repository! For demonstration purposes, I changed README.md file by adding just a whitespace character.

webhooks-readme

And Jenkins Job got triggered automatically!

jenkins-job

Now let’s check the DockerHub repository:

dockerize-ruby

The new build went through as well!

With this, we concluded CI/CD integration. So far, we covered how to install and configure Jenkins, create Jenkinsfiles, create and configure webhooks, create pipelines, and set up reverse proxying for your port. 

In the next and final part of this blog post series, you will integrate Kubernetes with your project! See you :)

Muhammet Topcu

Muhammet is currently working as QA Engineer at kloia. He is familiar with frameworks such as Selenium, Karate, Capybara, etc.