Building Automated Testing Pipeline With Semaphore CI And Selenium Grid
Tags: testing, tutorial, webdev, programming
Published: 2022-11-23 15:33:05

In today’s digital age, product teams must build products at a brisk pace while maintaining the best quality. Therefore, the methodology to follow and the tool selection to accomplish this can be paramount to get better results. Moreover, software applications don’t just support businesses now; rather, they have become an integral part of a business. Hence, it’s obvious that the product teams deliver a product with speed, reliability, scale, security, quality, and improved collaboration. So companies started following the DevOps model, which is a combination of Development (Dev) and Operations (Ops) teams.

DevOps include the practices such as [Continuous Integration and Continuous Delivery](https://www.lambdatest.com/blog/what-is-continuous-integration-and-continuous-delivery/?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog) (CI/CD), Developing Microservices, Adopting Infrastructure as code, Monitoring and Logging the application, and Communication & collaboration within teams. To rapidly develop a product with speed, teams often use SaaS (Software as a Service) solutions that are available to accomplish the required task without the overhead of setting up or maintaining any infrastructure. One such solution available in the market for CI/CD is Semaphore CI.

In this article, we’ll learn about Semaphore Continuous Integration and dig deeper into using it for building automated testing pipelines with [online Selenium Grid](https://www.lambdatest.com/selenium-grid-online?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog).

Let’s begin!

***A complete [webdriver](https://www.lambdatest.com/learning-hub/webdriver?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=learning_hub) tutorial that covers what WebDriver is, its features, architecture and best practices.***

## Overview Of Semaphore CI

Semaphore CI is one of the few CI/CD tools available to accomplish continuous integration and continuous delivery. Other [best CI/CD tools](https://www.lambdatest.com/blog/best-ci-cd-tools/?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog) in the same space are Jenkins, Azure DevOps, TravisCI, CircleCI, TeamCity Cloud, etc. Semaphore Continuous Integration tool identifies itself as the hosted CI/CD solution for teams that do not have bottlenecks! It is designed and built around increasing developer productivity and reducing the execution time for any pipeline. As per the official website, Semaphore Continuous Integration is the fastest CI/CD solution available in the market, with the ability to deliver the projects in record time.

Refer below to the benchmarking performance of Semaphore CI for a production-grade web application as compared to other CI/CD tools:

![](https://cdn-images-1.medium.com/max/2000/0*8ii2AgylaHpen4At.png)

From the above survey, it is clear that the Semaphore CI is the winner in terms of shorter execution time, as it has taken the least amount of time to complete operations in the pipeline.

Now you have understood what Semaphore Continuous Integration is, let’s see some of the best features available in Semaphore Continuous Integration.

## Features Of Semaphore CI

Below are some of the important features provided by the Semaphore Continuous Integration tool.

1. Hosted CI solution, no need to manage external servers

2. Flexibility to build from simple sequential build to multi-stage parallel programmable pipelines

3. Flexibility to create our environment with native Docker support

4. Optimize the monorepo workflows

5. Build, test and deploy on different programming languages and platforms.

6. Faster builds with smart caching

7. Support for containerization for pipelines using Docker and Kubernetes.

8. Ability to debug the failures in seconds by implementing SSH into the running jobs

9. Support for all deployment strategies — canary, blue-green, etc. including the option to rollback changes.

10. Built-in Docker registry to store the images.

Before we get into the details of Semaphore CI, let’s brush up on some important concepts around CI/CD. The below video will deep dive into the fundamentals of Continuous Integration, Continuous Deployment, and Continuous Delivery(CI/CD).

{% youtube https://youtu.be/MggFYkomfx0?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog  %}

However, you can also visit the [LambdaTest YouTube channel](https://www.youtube.com/c/LambdaTest?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog) for videos that will guide you through building an automated testing pipeline using popular CI/CD tools like Jenkins, GitLab, CircleCI, and more.

***Mobile [Mobile Emulator Online](https://www.lambdatest.com/mobile-emulator-online?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=webpage) from LambdaTest allows you to seamlessly test your mobile applications, websites, and web apps on mobile browsers and mobile devices.***

## Getting Started With Semaphore CI

Semaphore Continuous Integration is a commercial CI/CD service offering. It offers three pricing plans for the customers.

1. Free

2. Startup

3. Enterprise Cloud

Though it’s a commercial product, Semaphore CI has a free plan that provides $10 worth of credits (1300 minutes) for running a single job at a time on Linux, macOS, and Docker environments. We will use this option to explore Semaphore Continuous Integration and learn to build pipelines for our test automation examples using cloud Selenium Grid.

The basic prerequisite is that “You need an active GitHub account and a code repository”.

As of now, Semaphore supports creating pipelines for repositories that are hosted on GitHub. However, there is a plan to support repositories in Bitbucket. And supporting other options such as GitLab integration and any git repository is under consideration by the team.

We are using the Demo GitHub repository to discuss examples.

Below are the steps to get started using the Semaphore CI

1- Navigate to Semaphore CI official website and click on the Sign up with GitHub button.

2- Semaphore CI will open up in a new tab. Click on Continue with GitHub button.

3- It takes us to the GitHub authorization page. Authorize the app by clicking on Authorize renderedtext.

4- On the next screen, Semaphore asks you to create an organization. By default, it takes the GitHub username and creates a Semaphore URL for us.

![](https://cdn-images-1.medium.com/max/2000/0*94OkDM9j1Vmg_0o0.png)

5- Click on Save & Continue.

6- On the next screen, choose the plan for your organization.

![](https://cdn-images-1.medium.com/max/2000/0*AU6ZNr5eiBNWcHoe.png)

7- We can choose either of the options here. Even for the Pro option, we don’t need to enter any credit card details.

8- Once this is completed, we can see our newly configured organization as below.

![](https://cdn-images-1.medium.com/max/2000/0*4ngs-7jXbfwg26Ds.png)

9- Upon clicking on the organization tile, we could see our workspace as below. We could see all of the work that we have been doing in the organization. Since I have already created some pipelines earlier, we can see all these details. If you are creating it for the first time, you can see an option to set up a new project.

![](https://cdn-images-1.medium.com/max/2000/0*R2Obu-MsU3ePiuDy.png)

10- Click on the Create New button.

![](https://cdn-images-1.medium.com/max/2000/0*91ZTBrdWCm3DPPr2.png)

Choose the repository you want to work with. We can either choose the GitHub app or use a GitHub personal token. Let’s select the project and click on Choose.

![](https://cdn-images-1.medium.com/max/2000/0*Ghnulfk8dp6qsy9w.png)

11- Semaphore initializes the set-up required as below.

![](https://cdn-images-1.medium.com/max/2000/0*JgTaEnKBwmoGXSb9.png)

12- We can add collaborators required for the project in the Add People or go to the next page.

13- In the Setup workflow, choose the Single Job workflow and click on Looks good, let’s start button.

![](https://cdn-images-1.medium.com/max/2000/0*sfOcRXcyfYw6qDia.png)

14- Once done, Semaphore Continuous Integration starts the pipeline and executes the jobs inside the pipeline.

![](https://cdn-images-1.medium.com/max/2000/0*t218nurytIDFONLv.png)

That’s it! This is the entire process of setting up a new account with Semaphore Continuous Integration and creating a new sample pipeline.

In the next section, let’s understand some important concepts and terminology used in Semaphore Continuous Integration and then build new workflows and pipelines.

***In this article, we look at what [Regression testing](https://www.lambdatest.com/blog/regression-testing-what-is-and-how-to-do-it/?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog) is, its importance and types, and how to perform it.***

## Important Concepts In Semaphore CI

Similar to other CI/CD solutions, Semaphore Continuous Integration has its own set of terminology while working with pipelines:

* **Workflow**: Workflow in Semaphore CI is similar to a [Jenkins Pipeline](https://www.lambdatest.com/blog/jenkins-pipeline-tutorial/?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog). Semaphore uses pipelines, blocks, tasks, and promotions in the workflow to manage the application CI process. Each workflow for a project can have multiple pipelines.

* **Pipeline**: A pipeline is a collection of similar blocks within the same workflow.

* **Block**: Blocks are the building components of a pipeline. Each block in Semaphore CI consists of an optional name and task as a mandatory value. Blocks define steps in the pipeline on what to do as part of the pipeline. By default, all the blocks in the pipeline run sequentially by Semaphore CI. However, we can also define blocks to run in parallel mode by using block dependencies.

* **Task** (Mandatory): Task specifies what to do in the block. The task consists of jobs, epilogue, prologue, and secrets.

* **Job**: Jobs in the Semaphore specify the commands to execute in a pipeline. Each job is run in an isolated clean virtual environment created by the Semaphore. Examples include checking out the code, running tests using Maven, etc.

* **Promotion**: Promotions in Semaphore are the junction blocks used for deployment purposes and promoting builds to different environments.

* **Secret**: Secrets in Semaphore are used to store sensitive information for a workflow such as API keys, passwords, etc., which are not committed to the version control system.

* **Agent**: Virtual environment to run the workflows is created by Semaphore, post which it is ready for us to use. These machines are called agents. They perform the actual execution of the job. Each agent consists of a machine type and OS image used to build the agent. Semaphore supports agents on Linux and macOS environments.

The below picture from the official Semaphore documentation gives a high-level overview of these concepts.

![](https://cdn-images-1.medium.com/max/2000/0*pzfyeGUYYkfpP5t0.png)

***Test your website or web app online for iOS browser compatibility. Perform seamless cross browser testing on the latest [iPhone tester](https://www.lambdatest.com/test-on-iphone-simulator?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog). Try for free!***

## Building A Pipeline With Semaphore CI

If you have noticed in the getting started section, Semaphore has created a new branch called setup-semaphore and added a configuration file semaphore.yml in the .semaphore directory in our project. Basically, the idea here is to create a pull request to the master/main branch from the setup-semaphore branch once all the pipeline steps are defined.

Based on the pull request trigger, which is configured by default, Semaphore starts the workflow and executes the jobs within the pipeline.

We have created the pull request to master and we will be working in the master branch only for all our examples.

There are two options available to create a new workflow in Semaphore.

1. Using the Visual Builder

2. Using the declarative YAML configuration in semaphore.yml

Let us first start configuring our workflow using the Visual Builder. But, before that, let’s try to understand the test scenario that we are trying to configure in Semaphore Continuous Integration.

1. Open a specified browser.

2. Open the GitHub page and verify the title.

3. Open the Gmail page and verify the title.

4. Open the LambdaTest page and verify the title.

We have set up our [Selenium automation](https://www.lambdatest.com/selenium-automation?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog) project and TestNG to run the tests in any browser. We use the BrowserFactory and BaseTest to instantiate the browser based on the input and pass it on to our tests. Please refer to the Demo GitHub repository for complete project code.

We are using TestNG and adding appropriate annotations for our tests. We have also configured the testng.xml file to include what tests to run.

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
    <suite name="All Test Suite">
        <test verbose="2" name="firefox tests">
            <parameter name="browser" value="LOCAL_FIREFOX"/>
            <parameter name="gridHubURL" value="http://localhost:4444/wd/hub"/>
            <classes>
                <class name="org.lambdatest.FirstTest"/>
                <class name="org.lambdatest.SecondTest"/>
            </classes>
        </test>
    </suite>

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
    <suite name="All Test Suite">
        <test verbose="2" name="firefox tests">
            <parameter name="browser" value="LOCAL_FIREFOX"/>
            <parameter name="gridHubURL" value="http://localhost:4444/wd/hub"/>
            <classes>
                <class name="org.lambdatest.FirstTest"/>
                <class name="org.lambdatest.SecondTest"/>
            </classes>
        </test>
    </suite>

Finally, we will use the below maven command to trigger the tests.

mvn clean install -DsuiteXmlFile=testng.xml

Complete code for this project is available here on the GitHub repository.

## Adding A Pipeline With Visual Builder

With all the prerequisites in place, let’s start with our first pipeline using the Visual Builder in the Semaphore CI.

Go to the organization URL, and open the project that we have imported from GitHub. Select the branch as master and click on the edit workflow button.

We can see the values below for the pipeline section.

![](https://cdn-images-1.medium.com/max/2000/0*g_MioIgEV56j9Q0q.png)

As we see here, Semaphore has chosen default values for The Agent, OS Image, and the Machine types. If needed, we can change these values and proceed with creating the Job. If any other steps need to be performed before and after running the pipeline, we can configure the commands in the Prologue and Epilogue sections, respectively.

For now, let’s take all the default values and change the name to First Pipeline in Semaphore and click on the Job.

Add a name for the block, give the specified commands in the jobs section and keep other values at their defaults. Here the command checkout is mandatory to clone the repository and create the workspace. The next one is the maven command that we need to run our UI tests in [Selenium](https://www.lambdatest.com/selenium?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog).

![](https://cdn-images-1.medium.com/max/2000/0*M7PefZmZMXk1tWc5.png)

Click on Run the workflow. Semaphore asks to commit the changes to the code repository. Add a comment and click on Looks good, let’s go. Semaphore initiates the workflow and starts executing the job that is configured for our project. As we observe in the below screen, the workflow is in Running status.

![](https://cdn-images-1.medium.com/max/2000/0*GXSKjgFC-aAs5G6y.png)

Once the workflow is completed, we could check the logs as below. Next, the Semaphore starts creating the fresh virtual environment for our job, loads all the required variables, if any, checks out the code from the repository, and runs the build commands specified.

From the below screen, we could observe that our pipeline execution is successful, and all our tests are passed!

![](https://cdn-images-1.medium.com/max/2000/0*eI9cqWfBH9qf3Srs.png)

Here is the summary of the tests that are executed as part of our job.

![](https://cdn-images-1.medium.com/max/2000/0*lJzxYYggUVlhjXKD.png)

Finally, the status is updated as Passed at the workflow level.

![](https://cdn-images-1.medium.com/max/2000/0*riNakxkHZDSF9blH.png)

***A complete [Webdriver](https://www.lambdatest.com/learning-hub/webdriver?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=learning_hub) tutorial that covers what WebDriver is, its features, architecture and best practices.***

## Configuring A Pipeline With semaphore.yml

In our previous run, we have leveraged the Visual Builder option available in Semaphore CI to add the configuration for the pipeline. However, you can also add the configuration using a declarative YAML file — semaphore.yml

You have to click on the Edit workflow button and then click on the semaphore.yml file to open the configuration file on the web. We can see the below configuration is automatically available for us. It is because of the configuration we did using the visual builder option in the previous step. Therefore, the same instructions are replicated here.

![](https://cdn-images-1.medium.com/max/2000/0*6eM1S16BpnKvjOOF.png)

Let’s look at the configuration in detail.

* **version** — version refers to the pipeline YAML version used in the configuration

* **name** — the name of the pipeline

* **agent** — agent defines the virtual environment, i.e., software and hardware that needs to be created to run our job

* **machine** — machine property is added to the agent only, and it has two properties — type and os_image

* **type** — virtualized hardware that is available for us to use in jobs

* **os_image** — operating system to be used for the jobs

* **blocks** — blocks define steps in the pipeline on what to do as part of the pipeline

* **task** — the task is a mandatory property in blocks which refers to the steps needed inside a block

* **env_vars** — key-value pair that holds the environment variables as part of a job

* **jobs** — property where we mention the actual commands to be executed along with a name to it

* **commands** — an array of strings that holds the commands to execute for a job

Now that we have understood the semaphore.yml configuration let’s discuss the next example. We use the below semaphore.yml file for our next pipeline execution and see if we could run the pipeline in Semaphore Continuous Integration. As you observe, we are running our tests in the Chrome browser this time.

**Semaphore.yml file**

    version: v1.0
    name: First Pipeline with semaphore
    agent:
      machine:
        type: e1-standard-2
        os_image: ubuntu1804
    blocks:
      - name: Tests in Chrome
        task:
          prologue:
            commands:
              - checkout
          jobs:
            - name: Run tests
              commands:
                - mvn clean install -DsuiteXmlFile=testng.xml -Dbrowser=LOCAL_CHROME
          epilogue:
            always:
              commands:
                - test-results publish target/surefire-reports/junitreports/*.xml

Some of the new properties are mentioned below:

* **prologue** — This is executed before the commands of each job of a task item. In our example, we have added the checkout command to the prologue block to check the source code before our test execution.

* **epilogue** — This is executed after the commands of each job item of a task. Semaphore has a beta feature at the moment to publish the test results from the pipeline execution without any additional configuration. However, since we are using TestNG in our example, we get the XML reports post-execution. Hence we configured the XML files as part of the epilogue block to check the execution results in a more readable way.

Also, we are passing the browser property to the test suite using the below maven command.

mvn clean install -DsuiteXmlFile=testng.xml -Dbrowser=LOCAL_CHROME

Once the pipeline is executed with the new configuration specified in the semaphore.yml file, we can see the below log. Our execution is successful even in the Chrome browser also!

![](https://cdn-images-1.medium.com/max/2000/0*VfQsE7ckb27N8H3y.png)

From the logs, we could see that the Chrome browser instance is launched.

![](https://cdn-images-1.medium.com/max/2000/0*nhyeWqBagJRBCRHY.png)

And the tests are executed successfully.

![](https://cdn-images-1.medium.com/max/2000/0*0hInF6eGu6nxRFsF.png)

The workflow view is shown below:

![](https://cdn-images-1.medium.com/max/2000/0*5eDEg0570Y5pSHAk.png)

Please take a look at the expanded logs for the test results publishing command that we specified in the epilogue section. In addition, Semaphore is publishing the JUnit XML results to the summary page.

![](https://cdn-images-1.medium.com/max/2000/0*EWfaUJ2UWIvWqQmP.png)

Navigate to the Summary tab, and we can see the detailed test execution results there.

![](https://cdn-images-1.medium.com/max/2000/0*jXsd84_7nkZ-PT6r.png)

If there is a failure in our tests, the view displays below — with the details to easily debug the issue.

![](https://cdn-images-1.medium.com/max/2000/0*vAyF9QI_AoaUH4AM.png)

![](https://cdn-images-1.medium.com/max/2000/0*JPqKGtlThNo9jjWw.png)

***Perform browser [Automation testing](https://www.lambdatest.com/automation-testing?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=webpage) on the most powerful cloud infrastructure. Leverage LambdaTest automation testing for faster, reliable and scalable experience on cloud.***

## Building A Complex Workflow With semaphore.yml

With the knowledge that we have acquired till now, we could run the pipelines in the Semaphore Continuous Integration tool. We have discussed using Visual Builder and semaphore.yml to create and run a simple UI test pipeline. How about taking our experience to the next level and creating a complex workflow for our project? Let’s see how we can do it.

Add the below code in the semaphore.yml file and run the workflow.

**Semaphore.yml file for complex flow — sequential**

    version: v1.0
    name: Complex Pipeline with multiple browsers - sequential
    agent:
      machine:
        type: e1-standard-2
        os_image: ubuntu1804
    blocks:
      - name: Build
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          jobs:
            - name: Build
              commands:
                - checkout
                - mvn -q package -DskipTests=true
                - cache store
      - name: API Smoke tests
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          prologue:
            commands:
              - checkout
              - cache restore
          jobs:
            - name: Run Smoke API tests
              commands:
                - mvn clean install -DsuiteXmlFile=testng-api.xml
          epilogue:
            always:
              commands:
                - test-results publish target/surefire-reports/junitreports/*.xml
      - name: Firefox tests
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          prologue:
            commands:
              - checkout
              - cache restore
          jobs:
            - name: Run tests in Firefox
              commands:
                - mvn clean install -DsuiteXmlFile=testng.xml -Dbrowser=LOCAL_FIREFOX
          epilogue:
            always:
              commands:
                - test-results publish target/surefire-reports/junitreports/*.xml
      - name: Chrome tests
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          prologue:
            commands:
              - checkout
              - cache restore
          jobs:
            - name: Run tests in Chrome
              commands:
                - mvn clean install -DsuiteXmlFile=testng.xml -Dbrowser=LOCAL_CHROME
          epilogue:
            always:
              commands:
                - test-results publish target/surefire-reports/junitreports/*.xml

From the workflow overview page, we could see the below execution summary. First, the project is built, then the API [smoke tests](https://www.lambdatest.com/learning-hub/smoke-testing?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog) are executed, then the UI tests are executed in Firefox browser followed by UI test execution in Chrome browser.

![](https://cdn-images-1.medium.com/max/2000/0*qD-ViWlu1XpVTolY.png)

We are using the env_vars property for storing the maven local repository path. The env_vars property is a key-value pair with the name and value for the variable. Next, we are using the Semaphore CI caching feature to cache and reuse the project dependencies.

We have used the cache store command to store the cache and cache restore to restore the saved cache. As you have noted, along with our previous UI tests using Selenium, we have added a simple API test as part of our
suite using REST Assured library. And we configured to run the API tests using a separate TestNG XML file — testng-api.xml

Since we haven’t declared any dependencies between the jobs inside the block, the jobs are executed in sequential order by default one after the other. So let’s tweak our semaphore.yml as below and see if we can run the same in parallel mode.

Add the below code in the semaphore.yml file and run the workflow.

**Semaphore.yml file for complex flow — Parallel**

    version: v1.0
    name: Complex Pipeline with multiple browsers - parallel
    agent:
      machine:
        type: e1-standard-2
        os_image: ubuntu1804
    blocks:
      - name: Build
        dependencies: []
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          jobs:
            - name: Build
              commands:
                - checkout
                - mvn -q package -DskipTests=true
                - cache store
      - name: API Smoke tests
        dependencies:
          - Build
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          prologue:
            commands:
              - checkout
              - cache restore
          jobs:
            - name: Run Smoke API tests
              commands:
                - mvn clean install -DsuiteXmlFile=testng-api.xml
          epilogue:
            always:
              commands:
                - test-results publish target/surefire-reports/junitreports/*.xml
      - name: Firefox tests
        dependencies:
          - API Smoke tests
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          prologue:
            commands:
              - checkout
              - cache restore
          jobs:
            - name: Run tests in Firefox
              commands:
                - mvn clean install -DsuiteXmlFile=testng.xml -Dbrowser=LOCAL_FIREFOX
          epilogue:
            always:
              commands:
                - test-results publish target/surefire-reports/junitreports/*.xml
      - name: Chrome tests
        dependencies:
          - API Smoke tests
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          prologue:
            commands:
              - checkout
              - cache restore
          jobs:
            - name: Run tests in Chrome
              commands:
                - mvn clean install -DsuiteXmlFile=testng.xml -Dbrowser=LOCAL_CHROME
          epilogue:
            always:
              commands:
                - test-results publish target/surefire-reports/junitreports/*.xml



![](https://cdn-images-1.medium.com/max/2000/0*0IqA7az0xN6nHmit.png)

From the workflow overview page, we could see the above test execution summary. First, the project is built, then the API smoke tests are executed. As per the semaphore.yml configuration, we have added API Smoke tests as the dependency for jobs to run Firefox tests and Chrome tests.

As soon as the API Smoke tests job is completed, Semaphore triggers the other jobs simultaneously (i.e. the UI tests are executed in Firefox browser and Chrome browser simultaneously). This small configuration change enables the execution of the builds in less time and improves the overall efficiency of the pipelines. Therefore, we could achieve parallel execution without any code changes on our project, along with zero changes in testng.xml.

## Semaphore CI Pipeline With Docker

Till now, we have been running our tests on the browsers launched in the virtual infrastructure created by the Semaphore CI. Semaphore Continuous Integration also has native support to create and run the pipelines using docker. Docker CLI is already installed in all Semaphore virtual machines; hence we can use it without additional configuration. Let’s modify our configuration to run the same pipeline in browser containers launched by docker. Docker is one of the most widely used containerization technologies. To learn more about Docker and execute UI Selenium tests using Docker, please refer to our detailed article on how to [run Selenium tests in Docker](https://www.lambdatest.com/blog/run-selenium-tests-in-docker/?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog).

Add the below code in the semaphore.yml file and run the workflow in docker.

Semaphore.yml file for complex flow — Docker Containers

    version: v1.0
    name: Complex Pipeline with multiple browsers - docker
    agent:
      machine:
        type: e1-standard-2
        os_image: ubuntu1804
    blocks:
      - name: Build
        dependencies: []
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          jobs:
            - name: Build
              commands:
                - checkout
                - mvn -q package -DskipTests=true
                - cache store
      - name: API Smoke tests
        dependencies:
          - Build
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          prologue:
            commands:
              - checkout
              - cache restore
          jobs:
            - name: Run Smoke API tests
              commands:
                - mvn clean install -DsuiteXmlFile=testng-api.xml
          epilogue:
            always:
              commands:
                - test-results publish target/surefire-reports/junitreports/*.xml
      - name: Firefox tests
        dependencies:
          - API Smoke tests
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          prologue:
            commands:
              - checkout
              - cache restore
          jobs:
            - name: Run tests in Firefox
              commands:
                - docker run -d -p 4444:4444 -v /dev/shm:/dev/shm selenium/standalone-firefox:4.0.0-rc-1-prerelease-20210618
                - docker ps
                - mvn clean install -DsuiteXmlFile=testng.xml -Dbrowser=GRID_FIREFOX -DgridHubURL=http://localhost:4444/wd/hub
          epilogue:
            always:
              commands:
                - test-results publish target/surefire-reports/junitreports/*.xml
      - name: Chrome tests
        dependencies:
          - API Smoke tests
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          prologue:
            commands:
              - checkout
              - cache restore
          jobs:
            - name: Run tests in Chrome
              commands:
                - docker run -d -p 4444:4444 -v /dev/shm:/dev/shm selenium/standalone-chrome:4.0.0-rc-1-prerelease-20210618
                - docker ps
                - mvn clean install -DsuiteXmlFile=testng.xml -Dbrowser=GRID_CHROME -DgridHubURL=http://localhost:4444/wd/hub
          epilogue:
            always:
              commands:
                - test-results publish target/surefire-reports/junitreports/*.xml

As you take a look at the semaphore.yml configuration, there are some minor changes that we have made to enable the execution in docker containers. We will use the below docker run commands to spin specific browser containers on port 4444 and point our tests to run on the relevant grid hub URL with maven arguments.

    docker run -d -p 4444:4444 -v /dev/shm:/dev/shm selenium/standalone-firefox:4.0.0-rc-1-prerelease-20210618

    mvn clean install -DsuiteXmlFile=testng.xml -Dbrowser=GRID_FIREFOX -DgridHubURL=http://localhost:4444/wd/hub

    docker run -d -p 4444:4444 -v /dev/shm:/dev/shm selenium/standalone-chrome:4.0.0-rc-1-prerelease-20210618

    mvn clean install -DsuiteXmlFile=testng.xml -Dbrowser=GRID_CHROME -DgridHubURL=http://localhost:4444/wd/hub

Once the execution is completed, we can see the workflow overview as below.

![](https://cdn-images-1.medium.com/max/2000/0*_mzoSSBww1N7cBXI.png)

If we take a look at each of the jobs for our UI tests that are using docker containers to execute the tests, we can see the below log information.

Docker starts the stand-alone firefox container on port 4444 as we see in the output of the docker ps command. Then, using the gridHubURL we have sent the test requests to the container on the same port. Hence the tests are executed successfully on Firefox containers and the result is passed.

Similarly, the Chrome container is started on port 4444, tests are executed and the execution is passed.

![](https://cdn-images-1.medium.com/max/2000/0*VS9Wp-QxwbtWKhs-.png)

## Semaphore CI Pipeline With docker-compose

We have seen multiple examples of building test automation pipelines till now. We would be able to use Docker for spinning up the containers and executing our tests in them. Let us take this forward and explore building the automation pipelines with docker-compose in Semaphore CI.

Docker-compose is a utility provided by Docker for defining and running the multi-container docker applications. We have specified all the instructions required to start the services in a declarative YAML file and trigger it with a simple docker compose up command.

Let’s modify our configuration to run the same pipeline in browser containers launched by the docker-compose. First, we need to add the below code in the semaphore.yml file and run the workflow.

**Semaphore.yml file for complex flow — Docker-Compose**

    # To execute this docker-compose yml file use `docker-compose -f docker-compose-v3.yml up`
    # Add the `-d` flag at the end for detached execution
    # To stop the execution, hit Ctrl+C, and then `docker-compose -f docker-compose-v3.yml down`
    version: "3"
    services:
      chrome:
        image: selenium/node-chrome:4.0.0-rc-1-prerelease-20210618
        volumes:
          - /dev/shm:/dev/shm
        depends_on:
          - selenium-hub
        environment:
          - SE_EVENT_BUS_HOST=selenium-hub
          - SE_EVENT_BUS_PUBLISH_PORT=4442
          - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
        ports:
          - "6900:5900"
    
      edge:
        image: selenium/node-edge:4.0.0-rc-1-prerelease-20210618
        volumes:
          - /dev/shm:/dev/shm
        depends_on:
          - selenium-hub
        environment:
          - SE_EVENT_BUS_HOST=selenium-hub
          - SE_EVENT_BUS_PUBLISH_PORT=4442
          - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
        ports:
          - "6901:5900"
    
      firefox:
        image: selenium/node-firefox:4.0.0-rc-1-prerelease-20210618
        volumes:
          - /dev/shm:/dev/shm
        depends_on:
          - selenium-hub
        environment:
          - SE_EVENT_BUS_HOST=selenium-hub
          - SE_EVENT_BUS_PUBLISH_PORT=4442
          - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
        ports:
          - "6902:5900"
    
      selenium-hub:
        image: selenium/hub:4.0.0-rc-1-prerelease-20210618
        container_name: selenium-hub
        ports:
          - "4442:4442"
          - "4443:4443"
          - "4444:4444"

Once the execution is completed, we could see the below details on the workflow overview page.

![](https://cdn-images-1.medium.com/max/2000/0*FFZILfIbZOfIJ_xh.png)

If you have noticed the semaphore.yml configuration file, there is a change in the way we set up the browser containers. We are using the below commands with docker compose and starting the required services.

    $ curl https://gist.GitHubusercontent.com/rakesh-vardan/c1dcf6531b826fad91f18c285d566a71/raw/ad90a18fe5e70f2d6aea06621b76d6e0329a4aab/docker-compose-sel.yml > docker-compose.yaml
    $ docker-compose up -d
    $ docker-compose ps

We have added the services in the docker-compose.yml file and downloaded the content for our execution. Once it is available in the build path, we have invoked the docker-compose up command to start the services using docker.

From the log, we could observe that there are 4 containers created for Chrome, Edge, and Firefox browsers and Selenium hub. Since the hub is running on port 4444, we have redirected our test requests to the same hub URL.

![](https://cdn-images-1.medium.com/max/2000/0*Pu3WxO_O8oyHWP2o.png)

As per the below screen, the tests have been executed successfully.

![](https://cdn-images-1.medium.com/max/2000/0*7J2A4kVPYwr8pn_O.png)

There is one difference in the configuration in this example. If you have observed, we have added the docker-compose commands in the prologue section of the task only, and instead of creating a separate block for each UI test, we have added multiple jobs in the same block itself. This is because all jobs in Semaphore run in an isolated environment in virtual machines. So the services that start in one job are not automatically available to other jobs in the workflow. So to share the services across, we have added the commands in the prologue section.

## Building Semaphore CI Pipeline With LambdaTest

We are good with our previous examples and can execute the tests in Semaphore pipelines. However, suppose we have requirements for viewing the execution live, checking the execution logs, parallel execution, and recording execution sessions. In that case, this functionality is not readily available for us to include in our example. So, either we need to change the code or set up additional tools to enable this feature for our pipelines. So do we have a solution here? Yes, we can use a [cloud Selenium Grid](https://www.lambdatest.com/selenium-automation?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog) like LambdaTest.

LambdaTest is a [cross browser testing](https://www.lambdatest.com/?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog) cloud platform to perform automation testing on 2000+ combinations of real browsers and operating systems. It is very easy to configure a project using LambdaTest and start leveraging the benefits out of it. So, let’s integrate our pipeline with the LambdaTest cloud grid solution and see how it’s working.

To start using the LambdaTest cloud, navigate to the LambdaTest Registration Page and sign-up for a new account (which is free to start with!). Once the LambdaTest account is activated, go to the [LambdaTest Profile](https://accounts.lambdatest.com/login?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog) Section and note the username and access key. We would need this LambdaTest username and accesskey to execute our tests on the LambdaTest platform.

Let’s modify our configuration to run the same pipeline in browsers launched by the LambdaTest cloud grid. Add the below code in the semaphore.yml file and run the workflow.

**Semaphore.yml file for complex flow — LambdaTest**

    version: v1.0
    name: Complex Pipeline with multiple browsers - parallel with LambdaTest
    agent:
      machine:
        type: e1-standard-2
        os_image: ubuntu1804
    blocks:
      - name: Build
        dependencies: []
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          jobs:
            - name: Build
              commands:
                - checkout
                - mvn -q package -DskipTests=true
                - cache store
      - name: API Smoke tests
        dependencies:
          - Build
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          prologue:
            commands:
              - checkout
              - cache restore
          jobs:
            - name: Run Smoke API tests
              commands:
                - mvn clean install -DsuiteXmlFile=testng-api.xml
          epilogue:
            always:
              commands:
                - test-results publish target/surefire-reports/junitreports/*.xml
      - name: Firefox tests
        dependencies:
          - API Smoke tests
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          prologue:
            commands:
              - checkout
              - cache restore
          jobs:
            - name: Run tests in Firefox
              commands:
                - mvn clean install -DsuiteXmlFile=testng.xml -Dbrowser=GRID_LAMBDATEST_FIREFOX
          epilogue:
            always:
              commands:
                - test-results publish target/surefire-reports/junitreports/*.xml
          secrets:
            - name: LT
      - name: Chrome tests
        dependencies:
          - API Smoke tests
        task:
          env_vars:
            - name: MAVEN_OPTS
              value: '-Dmaven.repo.local=.m2'
          prologue:
            commands:
              - checkout
              - cache restore
          jobs:
            - name: Run tests in Chrome
              commands:
                - mvn clean install -DsuiteXmlFile=testng.xml -Dbrowser=GRID_LAMBDATEST_CHROME
          epilogue:
            always:
              commands:
                - test-results publish target/surefire-reports/junitreports/*.xml
          secrets:
            - name: LT

Before triggering the build, we need to make sure that the below environment variables are set as secrets in the project settings in Semaphore CI. Secrets in Semaphore Continuous Integration are the objects that have global scope within an organization. These secrets can be accessed by the jobs or pipelines to which they have been connected.

![](https://cdn-images-1.medium.com/max/2000/0*RyYqfdLUzxTQESMS.png)

    LT_USERNAME - Username from the LambdaTest profile page.
    LT_ACCESS_KEY - Access key obtained from the LambdaTest profile page.

The grid hub URL is formed based on the values that we set in the environment variables, as shown below:

    https://<username>:<accessKey>@hub.lambdatest.com/wd/hub

Where username is the Username, accessKey is the access key from the LambdaTest account.

Once the execution is completed, we could see the below details on the workflow overview page in the Semaphore CI.

![](https://cdn-images-1.medium.com/max/2000/0*3eL1BWwBFV0j1MQ5.png)

And from the detailed logs, we could see that the execution is successful in Firefox and Chrome browsers. Furthermore, we can also see the command used to execute the tests.

![](https://cdn-images-1.medium.com/max/2000/0*F2l0ntYPqA4qBVzF.png)

![](https://cdn-images-1.medium.com/max/2000/0*VcxZQ0-zSrzSBC1Z.png)

This looks good, but how about the execution statistics and information. Once the Semaphore CI completes the test execution, we can navigate to the [LambdaTest Automation Logs](https://accounts.lambdatest.com/register?utm_source=devto&utm_medium=organic&utm_campaign=nov23_pk&utm_term=pk&utm_content=blog) to see our test execution in the Timeline view. To navigate to the Automation logs view, click on the test case to see other details such as execution recording, logs, etc.

As we observe, we can see that 2 out of 5 parallel sessions are running in LambdaTest — one is on Chrome browser, and another is on Firefox browser.

![](https://cdn-images-1.medium.com/max/2000/0*LIVb8UZC2EqXPto9.png)

We could observe that 6 tests are executed in browsers available on the LambdaTest cloud grid from the timeline view.

![](https://cdn-images-1.medium.com/max/2000/0*a9EY_Vz93DwlY9aQ.png)

Here is the detailed view of automation logs, along with a recording of the test execution. Of course, we could also build very useful analytics like coverage, pass-failed ratio, and minutes consumed, etc., from the test execution results and build ready-to-use metrics.

![](https://cdn-images-1.medium.com/max/2000/0*RwG6m3RVyHKY5aj2.png)

Isn’t it interesting? Without any code changes on our part, we can easily integrate our automation suite with LambdaTest’s cloud grid solution and benefit from the features that it provides to us.

## Semaphore CI Additional Capabilities

Let’s throw some light into some of the additional capabilities provided by the Semaphore CI. First, we will discuss the command-line options and the REST API provided by the Semaphore Continuous Integration tool.

## Command Line Interface(CLI)

Semaphore command line interface(CLI) is called sem. It enables us to create and manage the Semaphore projects and resources directly from the command line terminal. Sem is a very powerful CLI utility that comes with the power of managing all the resources of Semaphore — workflows, pipelines, jobs, blocks, organizations, etc.

To get started using the sem cli, open a terminal either in a Linux machine or a macOS machine. Currently, the sem cli is not available to Windows OS. Issue the below command to download and run the script to have the cli available on your machine.

```
curl https://storage.googleapis.com/sem-cli-releases/get.sh | bash
```

![](https://cdn-images-1.medium.com/max/2000/0*om4P1miN6B5K7Frz.png)

Once done, use the sem connect command along with the organization token to connect to the organization.

sem connect ORGANIZATION.semaphoreci.com ACCESS_TOKEN

Get your access token from the below page in Semaphore CI. If there is no token available for your account, create one for yourself using the Generate API token button.

![](https://cdn-images-1.medium.com/max/2000/0*78FDuAYB_KxaRnRB.png)

Once you are connected to the required organization, we can issue the sem commands to manage our resources in Semaphore CI. Check the available option using the sem –help command.

![](https://cdn-images-1.medium.com/max/2000/0*cGqCKUDbOnAOVYdB.png)

Let us see if we can get all the pipelines available in our project. So issue the sem command as below along with the project name. So we could see all the pipelines that we have used till now along with their details.

sem get pipelines -p semaphoreci-selenium-lambdatest

![](https://cdn-images-1.medium.com/max/2000/0*ALF2mmaPFRleg0iX.png)

![](https://cdn-images-1.medium.com/max/2000/0*TRdByNzYsscoMsJB.png)

As we see below, we can check all the logs generated as part of the specific job.

![](https://cdn-images-1.medium.com/max/2800/0*AjhlIixN1VpDazRo.png)

Most of the actions we perform on the Semaphore web interface can be performed using the command line option sem. Please refer to the official Semaphore CLI documentation to get other available options in sem CLI.

## Conclusion

Thank you for reading our detailed article on building test automation pipelines using Semaphore CI. We discussed different approaches for creating the workflows in Semaphore, starting with a simple one. Then we discussed the complex pipelines of both sequential and parallel execution. We have also discussed leveraging docker in our pipelines, including the docker-compose utility. Finally, we discussed integrating the workflows with the cloud grid solution — LambdaTest. As part of our examples, we have covered some of the essential concepts from Semaphore CI like workflows, blocks, jobs, pipelines, and secrets, etc. We also touched upon using the Semaphore Continuous Integration using the CLI and REST API. We hope this article can help you get started with building pipelines with Semaphore CI.

**Happy Testing!**