Automated testing is a crucial component of the CI/CD pipeline, ensuring that code changes do not introduce new bugs and that the software remains stable. This section will guide you through the basics of automated testing, the types of tests, and how to implement them in your CI/CD pipeline.

Key Concepts

  1. Automated Testing: The use of software tools to run tests on code automatically, without human intervention.
  2. Types of Automated Tests:
    • Unit Tests: Test individual components or functions.
    • Integration Tests: Test the interaction between different components.
    • End-to-End (E2E) Tests: Test the entire application flow from start to finish.
    • Performance Tests: Measure the performance of the application under various conditions.

Setting Up Automated Tests

Step 1: Choose a Testing Framework

Depending on the programming language and the type of application, you can choose from various testing frameworks. Here are some popular ones:

Language Unit Testing Frameworks Integration Testing Frameworks E2E Testing Frameworks
JavaScript Jest, Mocha Jest, Mocha Cypress, Selenium
Python unittest, pytest pytest Selenium, Robot Framework
Java JUnit JUnit Selenium, Cucumber
Ruby RSpec RSpec Capybara, Selenium

Step 2: Write Unit Tests

Unit tests are the foundation of automated testing. They are designed to test individual functions or methods in isolation.

Example: JavaScript with Jest

// math.js
function add(a, b) {
  return a + b;
}

module.exports = add;

// math.test.js
const add = require('./math');

test('adds 1 + 2 to equal 3', () => {
  expect(add(1, 2)).toBe(3);
});

Explanation:

  • math.js contains a simple add function.
  • math.test.js contains a test case that checks if add(1, 2) equals 3.

Step 3: Write Integration Tests

Integration tests ensure that different parts of the application work together as expected.

Example: Python with pytest

# app.py
def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

# test_app.py
from app import add, subtract

def test_add():
    assert add(1, 2) == 3

def test_subtract():
    assert subtract(2, 1) == 1

Explanation:

  • app.py contains two functions: add and subtract.
  • test_app.py contains test cases for both functions, ensuring they work correctly together.

Step 4: Write End-to-End Tests

End-to-end tests simulate user interactions with the application to ensure the entire system works as expected.

Example: JavaScript with Cypress

// cypress/integration/sample_spec.js
describe('My First Test', () => {
  it('Visits the Kitchen Sink', () => {
    cy.visit('https://example.cypress.io')
    cy.contains('type').click()
    cy.url().should('include', '/commands/actions')
    cy.get('.action-email').type('[email protected]')
    cy.get('.action-email').should('have.value', '[email protected]')
  })
})

Explanation:

  • This Cypress test visits a URL, clicks a link, checks the URL, types into an input field, and verifies the input value.

Step 5: Integrate Tests into CI/CD Pipeline

To ensure tests run automatically on every code change, integrate them into your CI/CD pipeline.

Example: GitHub Actions

# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '14'
    - run: npm install
    - run: npm test

Explanation:

  • This GitHub Actions workflow runs on every push and pull request.
  • It sets up Node.js, installs dependencies, and runs tests using npm test.

Practical Exercise

Exercise: Implement Automated Tests

  1. Setup:

    • Choose a simple application (e.g., a calculator app).
    • Select a testing framework based on your programming language.
  2. Write Unit Tests:

    • Write unit tests for at least three functions in your application.
  3. Write Integration Tests:

    • Write integration tests to ensure different parts of your application work together.
  4. Write End-to-End Tests:

    • Write an end-to-end test that simulates a user interaction with your application.
  5. Integrate Tests into CI/CD Pipeline:

    • Use a CI/CD tool (e.g., GitHub Actions, Jenkins) to run your tests automatically on every code change.

Solution

  1. Setup:

    • Assume a JavaScript calculator app using Jest.
  2. Unit Tests:

// calculator.js
function add(a, b) {
  return a + b;
}

function subtract(a, b) {
  return a - b;
}

module.exports = { add, subtract };

// calculator.test.js
const { add, subtract } = require('./calculator');

test('adds 1 + 2 to equal 3', () => {
  expect(add(1, 2)).toBe(3);
});

test('subtracts 2 - 1 to equal 1', () => {
  expect(subtract(2, 1)).toBe(1);
});
  1. Integration Tests:
// calculator.js (add multiply function)
function multiply(a, b) {
  return a * b;
}

module.exports = { add, subtract, multiply };

// calculator.test.js (add integration test)
const { add, subtract, multiply } = require('./calculator');

test('adds 1 + 2 to equal 3', () => {
  expect(add(1, 2)).toBe(3);
});

test('subtracts 2 - 1 to equal 1', () => {
  expect(subtract(2, 1)).toBe(1);
});

test('multiplies 2 * 3 to equal 6', () => {
  expect(multiply(2, 3)).toBe(6);
});
  1. End-to-End Tests:
// cypress/integration/calculator_spec.js
describe('Calculator App', () => {
  it('Performs addition', () => {
    cy.visit('http://localhost:3000')
    cy.get('#num1').type('1')
    cy.get('#num2').type('2')
    cy.get('#add').click()
    cy.get('#result').should('have.text', '3')
  })
})
  1. Integrate Tests into CI/CD Pipeline:
# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '14'
    - run: npm install
    - run: npm test
    - name: Run Cypress tests
      run: npx cypress run

Conclusion

In this section, you learned how to implement automated tests, including unit, integration, and end-to-end tests. You also learned how to integrate these tests into a CI/CD pipeline to ensure they run automatically on every code change. By following these steps, you can significantly improve the stability and reliability of your software.

In the next section, you will work on a final project to implement a complete CI/CD pipeline, putting together all the concepts learned throughout the course.

© Copyright 2024. All rights reserved