Thursday, November 18, 2021

Containerized Automated Tests in Azure DevOps

 


Summary: Very good article on end to end testing and using azure dev ops.

In this article, we will talk about how one can easily containerize and run automated test cases as part of CI — CD using Azure DevOps.


trigger:
- master
resources:
- repo: self
variables:
tag: '$(Build.BuildId)'
stages:
- stage: Build
displayName: Build and Push image
jobs:
- job: Build
displayName: Build and Push
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Docker@2
inputs:
containerRegistry: 'Dockerhub'
repository: 'ghoshasish99/cits-test'
command: 'buildAndPush'
Dockerfile: '**/Dockerfile'
- task: CopyFiles@2
inputs:
Contents: 'docker-compose.yml'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
publishLocation: 'Container'
- task: Docker@2
inputs:
containerRegistry: 'Dockerhub'
repository: 'ghoshasish99/cits-test'
command: 'buildAndPush'
Dockerfile: '**/Dockerfile'
FROM java:openjdk-8-jre
LABEL maintainer="Ashish Ghosh"
ENV PROJ=""
ENV RELEASE=""
ENV TESTSET=""
ENV GRIDURL=""
RUN mkdir /workspace
WORKDIR /workspace
COPY . .
RUN chmod -R 755 ./
CMD ./Run.command -run -project_location ${PROJ} -release ${RELEASE} -testset ${TESTSET} -setEnv "run.RemoteGridURL=${GRIDURL}"
FROM    maven:3.6.0-jdk-8
RUN mkdir /functional-test
WORKDIR /functional-test
COPY . .
CMD mvn clean test -Dcucumber.options="--tags '@Regression and @Smoke'" -DexecutionPlatform="GRID"
- task: CopyFiles@2
inputs:
Contents: 'docker-compose.yml'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
publishLocation: 'Container'
version: "3"
services:
selenium-hub:
image: selenium/hub
container_name: selenium-hub
ports:
- "4444:4444"
chrome:
image: selenium/node-chrome
depends_on:
- selenium-hub
environment:
- HUB_HOST=selenium-hub
- HUB_PORT=4444
automation:
image: ghoshasish99/cits-test:${TAG}
container_name: automation-test
depends_on:
- chrome
environment:
- PROJ=Projects/${PROJECT_NAME}
- RELEASE=${RELEASE_NAME}
- TESTSET=${TESTSET_NAME}
- GRIDURL=http://selenium-hub:4444/wd/hub
volumes:
- ./Results/:/workspace/Projects/${PROJECT_NAME}/Results/TestExecution/${RELEASE_NAME}/${TESTSET_NAME}
- ${TAG} : This is the image tag that needs to be pulled. If you remember this is the $(Build.BuildId) which was used to tag the image before pushing to the registry.- ${PROJECT_NAME} , ${RELEASE_NAME} , ${TESTSET_NAME} are certain variables defined specifically to pass as inputs to the CITS execution engine.
TAG=$(Release.Artifacts.DockerizedTests.BuildId) PROJECT_NAME=DemoProject RELEASE_NAME=Grid TESTSET_NAME=Test docker-compose run automation
volumes:
- ./Results/:/workspace/Projects/${PROJECT_NAME}/Results/TestExecution/${RELEASE_NAME}/${TESTSET_NAME}
docker-compose down 

Setting up Cypress tests for a Node.js app on Azure DevOps



Introduction

This shows how to set up Cypress(cypress.io) tests towards a Node.js application on an Azure DevOps pipeline, based on how the team did this for the first time, step by step.

The steps were done using shared team knowledge.

Overview of steps

  • Goal 1: Run Cypress test towards a Node.js app
  • Goal 2: Run Cypress tests on an Azure DevOps pipeline
  • Goal 3: Send test results to a Cypress Dashboard
  • Goal 4: Configure pipeline to fail if tests fail

Goal 1: Run Cypress test towards a Node.js app

Background: Selection of Cypress as the test framework

Cypress was the test automation framework chosen for this project due to it seeming like a good match for the project based on a previous trial, seeing this used successfully for another project, and experience with other test frameworks.

The project is a 20 week, 10 sprint delivery for revamping web application used for both back office administration and customer facing administration. The database will be migrated to the cloud, an API will be built and a new GUI will be created. The team consists of an architect, backend / middleware developers, a front end developer, a tester (myself) and project management.

Try a quick Cypress pilot locally

The front end developer created a Vue app and some Cypress tests.

The tester (myself) setup several different types of Cypress tests to test different elements in a page for a standard web application, based upon the Cypress getting started manual.

In this way we did not depend upon each other and could share different learnings.

We them demo’d this to each other, and were keen to continue with Cypress.

Commands for running the tests on the command line in the node application

Here are the commands for running the tests in UI and headless mode.

UI mode is how the tests will be run locally when building and testing new features:

npm run test:e2e

Headless mode is how the tests will be run on the pipeline:

npm run test:e2ehl

Add the shortcuts “test:e2e” and “test:e2ehl” to package.json

Snip:

{
 "name": "app-ui",
 "version": "0.1.0",
 "private": true,
 "scripts": {
 "serve": "vue-cli-service serve",
 "build": "vue-cli-service build",
 "test:unit": "vue-cli-service test:unit",
 "test:e2e": "vue-cli-service test:e2e",
 "test:e2ehl": "vue-cli-service test:e2e --headless",
 "lint": "vue-cli-service lint"
 }

Test the shortcut commands locally.

Run tests in UI mode locally, with shortcut:

npm run test:e2e

Run tests in headless mode locally, with shortcut:

npm run test:e2ehl

Goal 2: Run Cypress tests on an Azure DevOps pipeline

Pre-requisite: The Azure DevOps space was set up with the UI repository

In short, create and configure a pipeline to run the tests in headless mode

Create pipeline

In Azure DevOps

  • *Create pipeline > new pipeline > select Azure > select the repository > select “Node.js with Vue” > create branch and commit message with appropriate links to work item
  • The pipeline will start running, take a peek and follow along with the steps to see what it does out of the box

Configure pipeline to run the tests in headless mode

Update azure-pipelines.yml to run the tests in headless mode:

Snip azure-pipelines.yml :

# Node.js with Vue

# Build a Node.js project that uses Vue.

# Add steps that analyze code, save build artifacts, deploy, and more:

# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript

trigger:
 - master

pool:
 vmImage: 'ubuntu-latest'

steps:
 - task: NodeTool@0
   inputs:
     versionSpec: '10.x'
   displayName: 'Install Node.js'

 - script: |
    npm install
    npm run test:e2ehl
    npm run build
   displayName: 'npm install, test and build'

After pushing the update to the repository, verify the logs to be sure the test ran.

Snip:

My First Test
✓ Visits the app root url (982ms)

Goal 3: Send test results to a Cypress Dashboard

Cypress has a Dashboard feature which shows test run results in a simple and elegant way.

Pre-requisites

  • A Cypress dashboard was created and tested locally
  • It was then shared with some team members for testing
  • Note that Cypress Dashboard is a paid feature depending on usage

Update package.json with the new command to send test results to the dashboard

Snip:

{..…
 "build": "vue-cli-service build",
 "test:unit": "vue-cli-service test:unit",
 "test:e2e": "vue-cli-service test:e2e",
 "test:e2ehl": "vue-cli-service test:e2e --headless",
 "test:e2e-pipeline": "vue-cli-service test:e2e --headless --record --key the-key-goes-in-here",
 "lint": "vue-cli-service lint"
 }

Update azure-pipelines.yml to run this command instead

Snip:

- script: |
     npm install
     npm run test:e2e-pipeline
     npm run build

Now when the pipeline is run it is possible to click through to the Dashboard. For example, clicking on a result shows links to test test output, a video of the test run in the case of failure, and so on.

Goal 4: Configure pipeline to fail if tests fail

Currently the azure-pipeline.yml has install, test and build in one step which is a quick proof of concept of tests running, however this results in the pipeline showing success even if Cypress tests fail.

Therefore splitting up the steps is needed as follows.

Snip of azure-pipeline.yml:

 - task: Npm@1
   inputs:
     command: 'install'
 - task: Npm@1
   inputs:
     command: 'custom'
     customCommand: 'run test:e2e-pipeline'
   displayName: 'Test'
 - task: Npm@1
   inputs:
     command: 'custom'
     customCommand: 'run build'
   displayName: 'Build'

Trigger the pipeline where there is one known failing test. If one does not exist, write one for the purpose of testing the pipeline. The pipeline now shows this error message:Error message

The full azure-pipeline.yml is now

# Node.js with Vue
# Build a Node.js project that uses Vue.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
trigger:
    - dev
    - master
    
pool:
  vmImage: 'ubuntu-latest'
  
steps:
  - task: NodeTool@0
    inputs:
      versionSpec: '10.x'
    displayName: 'Install Node.js'
  
  - task: Npm@1
    inputs:
      command: 'install'
  - task: Npm@1
    inputs:
      command: 'custom'
      customCommand: 'run test:e2e-pipeline'
    displayName: 'Test'
  - task: Npm@1
    inputs:
      command: 'custom'
      customCommand: 'run build'
    displayName: 'Build'

A keen reader will note that the tests are also now triggered on both dev and master branches, instead of just master as in the initial steps, because the pipeline evolves along with the project.

Conclusion

So far Cypress, Node.js with Vue and the Azure DevOps pipeline are working well together.

In addition, the tools are a good match for the project.

Finally, the main success factor has been a great team working together to share knowledge and experience.

That’s it - credit goes to the team for this setup!

Configuring Cypress in CI with Azure DevOps Pipelines


Cypress is a front-end test automation framework built for the modern web. It is open source and written entirely in JavaScript. It addresses the key pain points developers and QA engineers face when testing modern applications:

  • A rich yet simple API for interactions with automatic waiting
  • Mocha, Chai, and Sinon bundled in
  • A sleek dashboard with automatic reloads for Test-Driven Development
  • Easy debugging
  • Network traffic control for validation and mocking
  • Automatic screenshots and videos

It is a very powerful tool that enables developers to write End-to-End tests entirely in JavaScript, directly accessing everything within the browser. It is also a versatile tool that can be used to test REST APIs.

At To-Do Studio, we use Cypress to test End-to-End scenarios, as well as REST APIs.

On developers’ computers, we install the Cypress Test Runner and write tests locally. Everything works perfectly. However, for CI/CD testing, we must configure Cypress with Azure DevOps Pipelines. The recipe for configuring Cypress is not as simple as it may seem at first. I’m writing this post to make sure that a configuration recipe is documented somewhere on the web to explain how to configure Cypress in Continuous Integration (CI) with Azure DevOps Pipelines. Thus, with the help of Google search, those who will have the same goal in the future will find the right answer easily.

Here is diagram presenting an overview of the major elements of the solution:

Deployment_overview

First, ensure during the build phase that the cypress tests files are zipped and published to the artifacts drop.

build

Second, during the release phase hosted on an Ubuntu agent, extract the zipped test files,run the tests using Cypress and publish the tests results. Here are the detailed steps:

1. Extract the zip file containing the tests

Extract the zip file that was published to the artifacts drop by the build pipeline.

extract

2. Run the tests

Start by creating the Cypress config file. Define the location of your tests with the integrationFolder configuration value. Do not use the testFiles configuration value or –spec command line option.

{
  "integrationFolder": "tests/e2e/specs",
  "baseUrl": "https://info-staging.to-do.studio",
  "projectId": "<insert your project Id>",
  "reporter": "junit",
  "reporterOptions": {
    "mochaFile": "tests/test-output-[hash].xml",
    "toConsole": true,
    "attachments": true
    },
  "video": false,
  "pluginsFile": "tests/e2e/plugins/index.js",
  "supportFile": "tests/e2e/support/index.js",
  "env": {
    "urlEnv": "staging"
  }
}

cypress_config

Follow by running the tests with Cypress using the command: npx cypress run –record –key <insert your record key>. npx install and run Cypress in a single step. npx is a npm package runner (x stands for eXecute). The typical use is to download and run a package temporarily. Please note that if you intent to record screenshots and videos with Cypresss Dashboard Service, you need to add the unique projectId into your cypress.json and pass the record key into the command. If you do not need visual results, simply omit the recording option and projectId.

run_tests

3. Publish the tests results

Here you will publish the junit mocha files created during the tests run. Make sure that your merge test results and that it fails if there are test failures.

publish_tests