Cal Perez

Senior Software Engineer. I also like to cook, paint, and play D&D and Warhammer RPG.

How to set up Guard in Docker

May 29, 2021

My new place uses the same stack as my last place (React + Rails on Docker). However, when I started we didn't have a configured testing environment (or a test suite) to isolate our tests in. I like to use Guard with RSpec to keep RSpec "warm". It's also super helpful when doing TDD so you can just keep running your specs over and over.

I'm assuming you already have a Guardfile and Dockerfile. In case you don't have a Dockerfile, here's a simple one that leverages MySQL.

This is my favorite best practice guide for creating a Dockerfile.

# Pick your version of Ruby.
FROM ruby:2.7.2

# Update the package lists before installing.
RUN apt-get update -qq

# This installs
# * build-essential because Nokogiri requires gcc
# * common CA certs
# * the mysql CLI and client library
RUN apt-get install -y \
  build-essential \
  ca-certificates \
  default-libmysqlclient-dev

# Install your version of Node.
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - \
  && apt-get install -y nodejs

# Create working directory
ENV APP_HOME /app
RUN mkdir ${APP_HOME}
WORKDIR ${APP_HOME}

# Copy the Gemfile
COPY Gemfile ${APP_HOME}/Gemfile
COPY Gemfile.lock ${APP_HOME}/Gemfile.lock

# Make sure we are running bundler version 2.0
RUN gem install bundler -v 2.1.4
RUN bundle install

# Copy the project over
COPY . ${APP_HOME}

# Run guard
CMD ["bundle", "exec", "guard", "--no-bundler-warning", "--no-interactions"]

Then create a new docker-compose.test.yml file.

version: '3.8'
services:
  db:
    image: mysql:5.7
    env_file: .env

  redis:
    image: 'redis:5-alpine'

  guard:
    tty: true
    stdin_open: true
    depends_on:
      - db
      - redis
    build:
      context: .
      dockerfile: Dockerfile.test
    env_file: .env
    volumes:
      - '.:/app'

So as is, we can run docker compose -f docker-compose.test.yml --build up and Guard will start up. You can then edit and save one of your specs to execute it.

But what if you want to use the pry gem and stop execution of your tests?

This part of the Docker Compose file is key:

tty: true
stdin_open: true

These keys map to the -t and -i flags, respectively, when running docker run exec -it CONTAINER_NAME. This command opens a pseudo terminal that connects your machine's terminal (iTerm, Terminal, CommandPrompt) to the stdin/stdout stream of the container. It lets you "login" to the container to run commands and view the output of said commands, basically.

Tying this back to spec execution, you could then attach yourself to the Docker container running guard using docker attach CONTAINER_ID and you'd see a REPL session from pry wherever you added binding.pry in your spec.

For example:

describe "validations" do
  it 'is valid with valid attributes' do
    binding.pry # <---- Here
    expect(build(:user)).to be_valid
  end
end

Running this spec will stop execution at the binding.pry where you can then attach to the container and see the REPL session so you can test what build(:user) yields and other variables or logic in your spec.

A quick way to attach to a container in one line is docker attach $(docker ps -aqf "ancestor=CONTAINER_NAME | head -n 1).

Happy testing!

Previous

GraphQL and Schema Stitching

Next

Electrical Components: Resistor