belgoros
belgoros

Reputation: 3928

Docker, Docker compose - how to run RSpec tests

Can anybody point it out, - what is the best approach to run RSpec tests on a Rails API app inside a Docker container (during the container build/run) ?

The aim is to be able to separate the development environment from other ones and to run tests only in development mode, start Puma server only in staging, production environments.

What is the right place to put bundle exec rspec command, - in a separate entrypoint.sh script, Directly in Dockerfile, docker-compose.yml file, or other solutions?

All the Googles results as well as Docker for Rails Developers book by PragProg have no examples and the only way to run the tests theys provide is to run them against an already running container.

Actually, my Dockerfile looks like that:

FROM ruby:2.6.1

RUN apt-get update -yqq
RUN apt-get install -yqq --no-install-recommends build-essential zip unzip libpq-dev libaio1 libaio-dev nodejs

ENV APP_HOME=/usr/src/app
ENV BUNDLE_PATH /gems

COPY . $APP_HOME

RUN echo "gem: --no-rdoc --no-ri" >> ~/.gemrc

WORKDIR $APP_HOME
RUN gem update --system
RUN gem install bundler
RUN bundle install

RUN ["chmod", "+x", "entrypoint.sh"]
CMD ["./entrypoint.sh"]

The entrypoint.sh looks like that:

#!/bin/bash

set -e

if [ -f tmp/pids/server.pid ]; then
  rm tmp/pids/server.pid
fi

./wait-for-it.sh ${DATABASE_HOST}:${DATABASE_PORT}

if [ -z "$RAILS_ENV" ]; then
  echo "RAILS_ENV variable is not set, will use development by default"
  bundle exec rails db:reset
  bundle exec rails db:migrate
  bundle exec rspec
else
  bundle exec rails s -e $RAILS_ENV -p 3000 -b 0.0.0.0
fi

And finally, docker-compose.yml:

version: '3.3'

services:
  api:
    build: ../..
    ports:
      - '3000:3000'
    volumes:
      - .:/usr/src/app
      - gem_cache:/gems
    env_file:
      - ./env/database.env
      - ./env/web.env
    depends_on:
      - database
    # Keeps the stdin open, so we can attach to our app container's process and
    # do stuff such as `byebug` or `binding.pry`:
    stdin_open: true
    # Allows us to send signals (CTRL+C, CTRL+P + CTRL+Q) into the container
    tty: true
  database:
    image: postgres:9.6
    env_file:
      - ./env/database.env
    volumes:
      - db-data:/var/lib/postgresql/data
    ports:
      - 5432:5432
volumes:
  db-data:
  gem_cache:

Upvotes: 13

Views: 15071

Answers (3)

Paul Danelli
Paul Danelli

Reputation: 1133

I found this worked for me:

# docker-compose.yml

# Shared config from my compose file
x-app_config: &app_config
  build:
    context: .
    dockerfile: ./docker/Dockerfile
  stdin_open: true
  tty: true
  environment: &env
    RAILS_ENV: ${RAILS_ENV:-development}
  volumes:
    - .:/app

 # ...

 test:
   <<: *app_config
   environment:
     <<: *env
     RAILS_ENV: test
   entrypoint: ["bundle", "exec", "rspec"]

Now run the test:

docker-compose run test ./specs/path/to/your/test_spec.rb

Hope that helps someone :)

Upvotes: 3

blueman
blueman

Reputation: 91

try

docker-compose run -e "RAILS_ENV=test" api bundle exec rspec spec/link/to/file.rb

Upvotes: 9

David Maze
David Maze

Reputation: 159790

You should run bundle exec rspec on your host, before you run any Docker commands.

# Install and test the application locally
bundle install
bundle exec rspec
bundle exec rails server

# Great, it works; now package and run it in Docker
docker build -t ... .
docker run -p ... --net ... --name ... ...

You might find it useful to set up a partial Docker Compose environment with some dependencies. You can start these up before you run your test.

docker-compose up -d mysql redis
# Change config/settings.yml and config/database.yml to point at localhost
bundle exec rails rspec

You'll hit two practical problems trying to run tests in Docker.

  1. If you try to run tests in the Dockerfile, that doesn't have access to any of the other services that might be defined in the same docker-compose.yml file. In the particular case of Active Record, the application is actually unable to start, and you can't even run unit-type tests, without the database available. So you can't really run tests in the Dockerfile.

  2. If you try to run tests at application startup, you need to include all of your test dependencies in the Docker image (which increases your image size and potential security attack surface). You need to decide whether you always want to run tests (which increases startup time, possibly significantly) or only run them sometimes (which is awkward in a plain Docker Compose setup, though pretty straightforward in more robust CI systems).

Upvotes: 2

Related Questions