Parallel tests on CircleCI

Speed up builds by running Rails tests in parallel and learn more about CircleCI 2.1 features.

Jan 12, 2020 3 min read

By running tests in parallel on CircleCI, almost any test suite can go from tens of minutes to single minutes.

Below is an example CircleCI config that demonstrates how to set up parallelism. See parallelism: 4 in the test job.

It also demonstrates how to use the most useful CircleCI 2.1 features: orbs, commands, executors, jobs, workflows, and context.

Tip: to save time, download the CircleCI CLI. Then validate your repository’s .circleci/config.yml config file locally by running: circleci config validate.

# .circleci/config.yml

version: 2.1

aliases:
  - &bundle_path vendor/bundle
  # Default path of gem 'minitest-ci' https://github.com/circleci/minitest-ci
  - &results_path test/reports
  # Default path for screenshots from Rails system tests
  - &screenshots_path tmp/screenshots
  - &working_directory ~/repo

#
# Orbs are packages of config that you can import by name or configure inline.
orbs:
  # https://circleci.com/orbs/registry/orb/codecov/codecov
  codecov: codecov/codecov@1.0.5

  # https://circleci.com/orbs/registry/orb/valimail/dependency-manager
  # Aliased as "dm" throughout
  dm: valimail/dependency-manager@0.4.1

#
# Commands are reusable sets of steps invokable with parameters inside a job.
commands:
  prepare_workspace:
    steps:
      - dm/install-yarn
      - dm/update-bundler
      - attach_workspace:
          at: *working_directory
      - dm/bundle-gems

#
# Executors define the environment in which the steps of a job will be run.
executors:
  ruby-postgres-redis:
    docker:
      - image: circleci/ruby:2.6.5-browsers
        environment:
          BUNDLE_PATH: *bundle_path
          RESULTS_PATH: *results_path
          PGHOST: 127.0.0.1
          PG_USER: ubuntu
          RAILS_ENV: test
          RACK_ENV: test
          NODE_ENV: test
      - image: circleci/postgres:11.4-alpine-postgis
        environment:
          POSTGRES_USER: ubuntu
      - image: redis:5.0
    working_directory: *working_directory

#
# Jobs have two parts: the execution environment and a set of steps.
jobs:
  setup:
    resource_class: small
    executor: ruby-postgres-redis
    steps:
      - checkout
      - dm/install-yarn
      - dm/install-gems:
          cache-version: v1
      - dm/install-packages:
          cache-version: v1
      - run:
          name: Set up database.yml
          command: |
            mv config/database.yml.example config/database.yml
            sed -i 's/\$USER/ubuntu/g' config/database.yml            
      - run:
          name: Compile webpacker packs
          command: bundle exec rails webpacker:compile --trace
      - run:
          name: Save test filenames, splitting out slower system tests
          command: |
            circleci tests glob "test/**/*_test.rb" > test_all.txt
            grep -v "test/system" test_all.txt > test_main.txt
            grep "test/system" test_all.txt > test_system.txt            
      - persist_to_workspace:
          root: .
          paths:
            - .

  lint:
    resource_class: small
    executor: ruby-postgres-redis
    steps:
      - prepare_workspace
      - run:
          name: Lint JS
          command: yarn lint:js
      - run:
          name: Lint Ruby
          command: bundle exec rubocop
      - run:
          name: Scan for Rails code vulnerabilities
          command: |
            gem install brakeman
            brakeman            
      - run:
          name: Scan for bundled Ruby gem vulnerabilties
          command: |
            gem install bundler-audit
            bundle audit check --update            
      - run:
          name: Scan for Ruby and RubyGems system vulnerabilities
          command: |
            bundle add ruby_audit --group "test"
            bundle exec ruby-audit check            
      - run:
          name: Analyze Ruby code quality
          command: |
            gem install rubycritic
            rubycritic --minimum-score 90 --format json --no-browser app            

  test:
    resource_class: small
    executor: ruby-postgres-redis
    parallelism: 4
    steps:
      - prepare_workspace
      - run:
          name: Create and load database
          command: |
            bundle exec rails db:create --trace
            bundle exec rails db:schema:load --trace            
      - run:
          name: Run Rails non-system tests
          command: |
            TEST_FILES="$(circleci tests split test_main.txt --split-by=timings)"
            bundle exec rails test $TEST_FILES            
      - store_test_results:
          path: *results_path
      - run:
          name: Run Rails system tests
          command: |
            TEST_FILES="$(circleci tests split test_system.txt --split-by=timings)"
            bundle exec rails test $TEST_FILES

            # Want to use RSpec instead of minitest? Try this:
            # bundle exec rspec --format progress --format RspecJunitFormatter --out $RESULTS_PATH/rspec/core_results.xml $TEST_FILES            
      - store_test_results:
          path: *results_path
      - store_artifacts:
          path: *screenshots_path

#
# Workflows are sequences of jobs.
workflows:
  build:
    jobs:
      - setup
      - lint:
          requires:
            - setup
      - test:
          context: saas-tokens
          requires:
            - setup
Visit homepage
comments powered by Disqus