Dockerをテスト駆動開発していくための最小限のTips


アプリケーション開発では テスト駆動開発(Test Driven Development: TDD) はもう一般的になってきましたね。テストコードがないと継続的に開発ができなくて怖いですよね・・・

TDDの基本的な考え方は以下のような流れです。

  1. アプリケーションがあるべき姿を満たすようにテストを書く
  2. テストを実行して 失敗(RED)になることを確認する
  3. アプリケーションを実装してテストを 成功(GREEN)にする
  4. リファクタリングを行う

つまり REDGREENリファクタリング のサイクルを繰り返すことがテスト駆動開発です。

テスト駆動開発を行う目的は多々あるかと思いますが、
「キレイな実装をし、メンテしやすいコードを維持しながら継続的に開発を行うため」
というのが主な目的かと私は考えています。

さて、今回話題にあげたいのがインフラをどのようにして、テスト駆動開発の流れに乗せて開発するかです。
インフラのテストというとまだまだ未開拓な領域も多く、手が出しづらいのが現状ではないでしょうか?
今回はDockerのテストに焦点を当てて、最小限これだけやればDockerのTDD開発ができるぞ!という方法を考えてみました。

Dockerfile

Dockerfileは純粋な centos イメージ からスタートしていきます。
今回は centosイメージ に git をインストールしていく開発フローをTDDで進めていきます。

はじめのDockerfile
FROM centos

使用するツール

  • serverspec
  • docker-api

serverspecを使用しますので ruby です。
まずはGemfileに必要なgemを列挙してbundle installしましょう。

Gemfile
source "https://rubygems.org"

gem 'serverspec'
gem 'docker-api'
bash
$ bundle install

ちなみにディレクトリ構成を先にご紹介しておくとこんな感じです。

bash
$ tree
.
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── README.md
└── spec
    ├── spec_helper.rb
    └── web
        └── git_spec.rb

spec_helper.rb

spec_helper.rb
require 'serverspec'
require 'docker'

set :backend, :docker
set :docker_image, 'web'

今回のミソはこの部分かもしれません。
spec_helper.rbにこのように書いておくと、 serverspec がテスト対象の docker image を特定してくれます。
今回は実行する対象の docker image の名前をwebとしておきました。
なので、まずは docker image を web という名前をつけて作っておきましょう。
まだこの状態では純粋な centos が入った docker image が web という名前でできているだけです。

bash
# docker imageをwebという名前をつけてビルドする
$ docker build -t web .
Sending build context to Docker daemon   59.9kB
Step 1/1 : FROM centos
 ---> 49f7960eb7e4
Successfully built 49f7960eb7e4
Successfully tagged web:latest

# docker imageの一覧を確認
$ docker images
REPOSITORY                                               TAG                 IMAGE ID            CREATED             SIZE
web                                                      latest              49f7960eb7e4        5 weeks ago         200MB

テストを失敗させる RED

さて、前置きは整いましたのでここからいよいよテストコードを書いていきましょう。
まずはテストを実行して RED にするところまでもっていきます。

git_spec.rb
require 'spec_helper'

describe package('git') do
  it { should be_installed }
end

単純に git がインストールされて入ればOKですのでこれで十分ですね。
テストを実行して RED になることを確認します。

bash
$ bundle exec rspec

Package "git"
  should be installed (FAILED - 1)

Failures:

  1) Package "git" should be installed
     Failure/Error: it { should be_installed }
       expected Package "git" to be installed

     # ./spec/web/git_spec.rb:4:in `block (2 levels) in <top (required)>'

Finished in 1.09 seconds (files took 0.42839 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/web/git_spec.rb:4 # Package "git" should be installed

Dockerfileを書いていく

テストが RED になっていることを確認しましたので、これを GREEN にしなければなりません。
ここからやっと、Dockerfileを書いていくフェーズになります。

bash
$ emacs Dockerfile
Dockerfile
  FROM centos
+ RUN yum install -y git

gitを入れるだけなのでとても簡単です。
yum installするだけです。

テストを成功させる GREEN

Dockerfileができあがりましたので、あとはテストを GREEN にしましょう。
まずは先程作ったDockerfileからdocker imageをビルドします。

bash
# docker imageをwebという名前をつけてビルドする
$ docker build -t web .
bash
$ rspec

Package "git"
  should be installed

Finished in 1.13 seconds (files took 0.89826 seconds to load)
1 example, 0 failures

テストがGREENになりました。完成です。
以上で、Dockerに対するTDDの流れが実現できました。
そこまで複雑なことはしなくてもよいので、とても簡単ですね。

Travis CI を使って自動テストできるようにする

実際にチームでプロダクトを開発する際にはCIツールに頼るのが一般的です。

今回はCIツールとして、Travice CIを使ってみます。
使用方法とかはググってもらえればたくさん出てくると思うので省略しますが
Settings → Integration & Service から Add Serviceのプルダウンを押すと
Travis CIが出てきますので、そこからポチポチ設定すればできるはずです。

.travis.yml

まずはビルドの設定をしましょう。
.travis.ymlというファイルを作ってGithubのリポジトリの直下に置いておけば自動的に読み込んでビルドを行ってくれます。

ローカルでTDDをやった時の流れに従って、rspecによるテストを実行する前にdocker imageをビルドしておきましょう。
before_installで指定すればscriptより先に実行してくれるので、ここにdocker buildと書いてしまえばいいです。

.travis.yml
sudo: required
language: ruby
cache: bundler
service: docker
before_install:
    - docker build -t web .
script:
    - bundle exec rspec

Githubで開発をしてみる

Githubで適当なプルリクエストを立ててみましょう。
TravisCIがコミットごとに自動でテストを実行し、その結果を返しているのがわかります。

Travis CIの画面

ちなみにテストで失敗した or 成功したという結果はTravis CIではこのようなログで確認することができます。

errorの場合

passの場合

まとめ

  1. テスト駆動開発(TDD)を行うことで、メンテしやすいコードを維持しながら継続的に開発を行える
  2. インフラ(Docker)でもTDDできるよ
  3. TravisCIとか使うとGithubでの開発が捗っていい感じ

今回はDockerを例にとりましたが
Vagrantとかを使えば仮想環境でももちろんこのテクニックは応用できそうです。

これからはインフラもあたりまえにテストを書いていく時代ですよ