Skip to content

Knapsack Pro parallel testing

Run 1 hour test suite in 2 minutes with optimal parallelization on your CI

Source: https://knapsackpro.com

User Guide

For more information on how to install it and use it, please see the Knapsack Pro integration tutorial.

Supported Knapsack test runners

Knapsack Pro supports a few test runners out of the box:

As of now, Ruby has the most test runners supported by Knapsack:

  • RSpec
  • Cucumber
  • Minitest
  • test-unit
  • Spinach

We will limit this tutorial to Cucumber, as the setup for each test runner is generally pretty much the same.

Less Javascript runners are supported:

  • Cypress.io
  • Jest

We won't handle these in this tutorial, but the general setup is also the same here. For more information, you can read up on the offical Github repositories for Knapsack in Cypress.io and Jest:

We will now provide you with a Calliope pipeline example for Cucumber. We'll limit these to Gitlab and Github Actions for now.

# We setup the services required to perform the tests.
services:
  - selenium/standalone-chrome:3.141.59

# We setup the stages in order to properly use the nodes.
stages:
  - test
  - upload

# We set some variables required for both Knapsack and Calliope.
variables:
  KNAPSACK_PRO_TEST_SUITE_TOKEN_CUCUMBER: YourKnapsackApiKey
  CALLIOPE_API_KEY: YourCalliopeApiKey
  PROFILE_ID: YourCalliopeProfileID

# A simple before script to prepare the project for testing.
before_script:
  - gem install bundler
  - bundle install
  - export SELENIUM_ENV=remote

# Here we run the TA suite on 2 nodes to keep things simple, you can increase the amount of nodes if your environment can handle it.
# The more nodes, the quicker your TA suite will be.
cucumber:
  parallel: 2
  stage: test
  script:
    - bundle exec knapsack_pro cucumber "--format json --out artifacts/cucumber_${CI_NODE_INDEX}.json --format pretty" || true
  artifacts:
  paths:
    - artifacts/cucumber_${CI_NODE_INDEX}.json
  allow_failure: true

# To avoid having to change the upload script when deciding to use more nodes, we zip all the result files together to 1 zipfile,
# which we then finally upload to Calliope.
upload:
  dependencies: [cucumber]
  stage: upload
  script:
  - apt-get update
  - apt-get install zip unzip -y
  - cd artifacts; zip -r ../combined-results.zip . -i *.json; cd ..
  - curl -X POST
    -H "Content-Type:multipart/form-data"
    -H "x-api-key:${CALLIOPE_API_KEY}"
    -F "file[]=@combined-results.zip"
    "https://app.calliope.pro/api/v2/profile/${PROFILE_ID}/import/cucumber?os=YOUR_OS&platform=YOUR_PLATFORM&build=YOUR_BUILD"
# Here we give a name to the whole pipeline. You can call this whatever you want.
name: CI

# Here we decide when we want to run the pipeline.
# In this example we run it whenever a commit is pushed or a pull request is made to the master branch.
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    # The default environment of a GitHub job is Ubuntu, which we will leave for what it is.
    # We also define a Ruby version, in this example we use Ruby 2.7.
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        ruby-version: ['2.7']
        ci_node_total: [2]
        # Indexes for parallel jobs (starting from zero).
        # E.g. use [0, 1] for 2 parallel jobs, [0, 1, 2] for 3 parallel jobs, etc.
        ci_node_index: [0, 1]

    steps:
    - name: Checkout repository
      uses: actions/checkout@v2

    # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
    # change this to (see https://github.com/ruby/setup-ruby#versioning):
    # uses: ruby/setup-ruby@v1
    - name: Setup Ruby
      uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
      with:
        # We use the Ruby version that we just set. 
        # This step will also automatically install all the gems with Bundler, so we don't have to explicitly run it.
        ruby-version: ${{ matrix.ruby-version }}
        bundler-cache: true 

    # Now we run the Cucumber tests and export their results to a JSON file.
    - name: Run tests
      env: 
        KNAPSACK_PRO_CI_NODE_TOTAL: ${{ matrix.ci_node_total }}
        KNAPSACK_PRO_CI_NODE_INDEX: ${{ matrix.ci_node_index }}
        KNAPSACK_PRO_TEST_SUITE_TOKEN_CUCUMBER: ${{ secrets.KNAPSACK_PRO_TEST_SUITE_TOKEN_CUCUMBER }}
      run: bundle exec knapsack_pro cucumber "--profile test --format json --tag @fta --out cucumber_${{ matrix.ci_node_index }}.json --format pretty"

    # We save all the test reports as artifacts.
    - name: Save artifacts for upload
      if: always()
      uses: actions/upload-artifact@v2
      with:
        path: cucumber_${{ matrix.ci_node_index }}.json

  # After the test stage, we arrive at the upload stage.
  upload:
    if: always()
    runs-on: ubuntu-latest
    needs: [test]

    steps:
    # We have to manually download the artifacts first.
    - name: Retrieve test result artifacts
      uses: actions/download-artifact@v2
      with:
        path: results

    # We display the artifacts to the screen to make sure they were exported and imported correctly.
    - name: Display downloaded artifacts
      run: ls -R

    # To avoid having to change the upload script when deciding to use more nodes, we zip all the result files together to upload as 1 file.
    - name: Zip artifacts together
      uses: papeloto/action-zip@v1
      with:
        files: results/
        dest: results.zip

    # Finally we upload the zipped files to Calliope.
    - name: Upload results to Calliope
      run: curl -X POST
           -H "x-api-key:${{ secrets.API_KEY }}"
           -H "multipart/form-data"
           -F "file[]=@./results.zip"
           "https://app.calliope.pro/api/v2/profile/${{ secrets.PROFILE_ID }}/import/cucumber?os=myos&platform=myplatform&build=mybuild"

Knapsack supports more than just these two CI/CD tools. You can read up on them in the aforementioned installation tutorial.

Please note that the only CI/CD tools other than these two that we fully support are Jenkins and Travis CI. This does not mean Calliope cannot be used with these other tools, but you will have to implement those yourself.