アプリケーション開発では テスト駆動開発(Test Driven Development: TDD) はもう一般的になってきましたね。テストコードがないと継続的に開発ができなくて怖いですよね・・・
TDDの基本的な考え方は以下のような流れです。
- アプリケーションがあるべき姿を満たすようにテストを書く
- テストを実行して 失敗(RED)になることを確認する
- アプリケーションを実装してテストを 成功(GREEN)にする
- リファクタリングを行う
つまり RED → GREEN → リファクタリング のサイクルを繰り返すことがテスト駆動開発です。
テスト駆動開発を行う目的は多々あるかと思いますが、
「キレイな実装をし、メンテしやすいコードを維持しながら継続的に開発を行うため」
というのが主な目的かと私は考えています。
さて、今回話題にあげたいのがインフラをどのようにして、テスト駆動開発の流れに乗せて開発するかです。
インフラのテストというとまだまだ未開拓な領域も多く、手が出しづらいのが現状ではないでしょうか?
今回はDockerのテストに焦点を当てて、最小限これだけやればDockerのTDD開発ができるぞ!という方法を考えてみました。
Dockerfile
Dockerfileは純粋な centos イメージ からスタートしていきます。
今回は centosイメージ に git をインストールしていく開発フローをTDDで進めていきます。
FROM centos
使用するツール
- serverspec
- docker-api
serverspecを使用しますので ruby です。
まずはGemfileに必要なgemを列挙してbundle installしましょう。
source "https://rubygems.org"
gem 'serverspec'
gem 'docker-api'
$ bundle install
ちなみにディレクトリ構成を先にご紹介しておくとこんな感じです。
$ tree
.
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── README.md
└── spec
├── spec_helper.rb
└── web
└── git_spec.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 という名前でできているだけです。
# 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 にするところまでもっていきます。
require 'spec_helper'
describe package('git') do
it { should be_installed }
end
単純に git がインストールされて入ればOKですのでこれで十分ですね。
テストを実行して RED になることを確認します。
$ 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を書いていくフェーズになります。
$ emacs Dockerfile
FROM centos
+ RUN yum install -y git
gitを入れるだけなのでとても簡単です。
yum installするだけです。
テストを成功させる GREEN
Dockerfileができあがりましたので、あとはテストを GREEN にしましょう。
まずは先程作ったDockerfileからdocker imageをビルドします。
# docker imageをwebという名前をつけてビルドする
$ docker build -t web .
$ 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と書いてしまえばいいです。
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の場合
まとめ
- テスト駆動開発(TDD)を行うことで、メンテしやすいコードを維持しながら継続的に開発を行える
- インフラ(Docker)でもTDDできるよ
- TravisCIとか使うとGithubでの開発が捗っていい感じ
今回はDockerを例にとりましたが
Vagrantとかを使えば仮想環境でももちろんこのテクニックは応用できそうです。
これからはインフラもあたりまえにテストを書いていく時代ですよ