Dockerで始めるRSpec

モチベーション

"Effective Testing with RSpec 3"というRSpecの本を読み、RSpecをインストールしようとしたら、以下のようなエラーが発生した。

diff-lcs's executable "htmldiff" conflicts with /Users/mine/.rbenv/versions/2.7.2/bin/htmldiff
Overwrite the executable? [yN]  n

既存の環境を汚すのは嫌なので、Dockerで環境を作ってしまおうというのが記事のモチベーションとなっている。

また本が書かれたのが2017年とずいぶん昔なので、昔の環境を再現する手段としてもDockerは良いと考えた。

環境

  • MacBook Pro(macOS Catalina)
  • Ruby 2.5
  • RSpec 3.6

書籍ではRuby 2.4となっていたが、docker hubのサポートバージョンで一番古いものが2.5だったので、とりあえず2.5にした。

ディレクトリ構成

% tree
.
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── README.md
└── apps

Dockerfile

FROM ruby:2.5.8

WORKDIR /apps

COPY Gemfile Gemfile.lock ./

COPY ./apps /apps

RUN bundle install

Gemfile

source 'https://rubygems.org'
ruby '2.5.8'

gem "rspec-rails", "3.6.0"

Gemfile.lockは中身は空で、ファイルだけ作成する。

使い方

Dockerfileからイメージをビルドする。

% docker build -t rubyapp . 

イメージができたことを確認する。

 % docker image ls             
REPOSITORY                   TAG           IMAGE ID       CREATED         SIZE
rubyapp                      latest        bb1bec6a36e0   2 hours ago     888MB

イメージを利用する。 上で作成したrubyappというイメージから、rubyappというコンテナを作成する。

% docker run -dit --name rubyapp  -v $PWD/apps:/apps rubyapp

docker runというコマンドは

  • docker pull
  • docker create
  • docker start

という3つの一連のコマンドをまとめて実行するコマンド。

Dockerのプロセスを確認する。

 % docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED         STATUS         PORTS     NAMES
ae232a4e65e4   rubyapp   "irb"     8 minutes ago   Up 8 minutes             rubyapp

コンテナに入る。

% docker exec -it rubyapp bash

execはコンテナ内でコマンドを実行するという意味。

コンテナ内でRSpecのバージョンを確認する。

/apps# ruby -v
ruby 2.5.8p224 (2020-03-31 revision 67882) [x86_64-linux]

# rspec -v
RSpec 3.6
  - rspec-core 3.6.0
  - rspec-expectations 3.6.0
  - rspec-mocks 3.6.0
  - rspec-rails 3.6.0
  - rspec-support 3.6.0

コンテナを停止する。

% docker stop rubyapp_docker
rubyapp_docker

最初のRSpecを書いてみる

以下のようなディレクトリ構成とする。

└── apps
    └── ch1
        └── spec
            └── sandwich_spec.rb

上記のRubyファイルはホストコンピュータ(コンテナの外)から編集可能で、自分はRubyMineで編集している。

コンテナとファイルを共有するためにdocker runするときに-vでバインドするボリュームを指定したためである。

sandwich_spec.rbは以下の通り。

Sandwich = Struct.new(:taste, :toppings)

RSpec.describe 'An ideal sandwich' do
  it 'is delicious' do
    sandwich = Sandwich.new('delicious', [])

    taste = sandwich.taste

    expect(taste).to eq('delicious')
  end
end

rspecを実行するとテストの結果を見ることができる。

/apps/ch1# rspec
.

Finished in 0.00516 seconds (files took 0.09017 seconds to load)
1 example, 0 failures