Heroku上のDjangoアプリで静的ファイルをS3から配信する(前編)
HerokuにDjangoアプリケーションを置いて、静的ファイルもHerokuから配信した場合、USまでのレイテンシがあるので1ファイルごとに結構な時間がかかります。
試しに同じアプリケーションをさくらVPS(大阪)と、Heroku(US)に置いた場合、さくらでは 30ms〜150ms程度でロードできるのに対し、Herokuでは200ms以上かかりました。
HTML1ファイルだけならともかく、CSSやJS、画像のロードにも時間がかかるため、体感時間は結構変わります。
そもそも静的ファイルをアプリケーション・サーバーから配信するのはCPU、ディスク、ネットワークなどのリソースの面でも無駄が多く、推奨されていません。
そこで今回は、静的ファイルをAmazon S3のTokyoリージョンから配信してみました。いわゆる Web Storageパターン です。
長くなったので前後編に分けています。前編はHerokuに限らない一般的なDjangoアプリケーションの話をし、後編 ではHeroku特有の話をします。
ライブラリのインストール
$ pip install django-storages boto
django-storages はDjango用のカスタム・ストレージ・バックエンドを集めたライブラリです。
boto はAWSのPythonインターフェイスを提供するライブラリです。django-storagesのs3-botoバックエンドがbotoを使用する形になります。
requirements.txt
にも忘れずに書いておきます。(バージョンはご利用のものに合わせてください)
boto==2.29.1 django-storages==1.1.8
S3バケットの作成
S3に適当な名前でバケットを作成して、Static Website Hostingを有効にしておきます。
今回は static.bus.capybala.com
という名前にしました。
日本に置きたかったのでTokyoリージョンを選択しています。
Access Keyの作成
また、このバケットにファイルを追加できる権限を持つAccess KeyとSecret Access Keyを作っておきます。
以下の様なポリシーで良いでしょう。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:*", "Resource": [ "arn:aws:s3:::static.bus.capybala.com", "arn:aws:s3:::static.bus.capybala.com/*" ] } ] }
Djangoの設定
settings.py
に以下の設定を追加します。
INSTALLED_APPS = ( ... 'storages', # 行を追加 ... ) # S3バケットのURL(サブドメインのURLを使ってもどちらでもいいです。) STATIC_URL = 'https://s3-ap-northeast-1.amazonaws.com/static.bus.capybala.com/' # collectstaic時にS3を使う STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage' # 作成したS3バケットの名前 AWS_STORAGE_BUCKET_NAME = 'static.bus.capybala.com' # これをTrueにしたほうがファイル変更のチェックが速くなる AWS_PRELOAD_METADATA = True
以下のようにDEBUG
が有効かどうかによってSTATIC_URL
をローカル配信と切り替えても良いでしょう。
if DEBUG: STATIC_URL = '/static/' else: STATIC_URL = 'https://s3-ap-northeast-1.amazonaws.com/static.bus.capybala.com/'
AWSのクレデンシャル
AWSのAccess KeyとSecret Access Keyは秘密の値なので、settings.py
には書きません。Twelve Factor に従って、環境変数AWS_ACCESS_KEY_ID
とAWS_SECRET_ACCESS_KEY
で渡します。
.env
ファイルに書いておきます。
AWS_ACCESS_KEY_ID=*** AWS_SECRET_ACCESS_KEY=***
アップロードのテスト
ここまでの設定をした上で、以下のようにcollectstaticを実行すると、静的ファイルがS3バケットにアップロードされるはずです。
$ foreman run python manage.py collectstatic --noinput
細かい話(アップロード時のエラー)
S3への大きなファイルのアップロード時にBroken pipeというエラーが発生する問題に遭遇したので、コメントにあるように~/.boto
というファイルを作って対処しました。
$ cat ~/.boto [s3] host = s3-ap-northeast-1.amazonaws.com
なお、後編のHerokuでは特に必要ありませんでした。
前編はここまでです。後編ではHerokuを使います。
参考
Rubyによるデザインパターンを読んだ
恥ずかしながら10年以上プログラミングをやっていて、今までちゃんとデザインパターンを勉強したことがありませんでした。
それぞれのパターンはなんとなく知っているけど、ちゃんと理解できてはいないという状態でした。
デザインパターンを勉強しようとしたことは何度かありました。しかしJavaやC++で解説した書籍・Webサイトでは、いちいち本質的ではない基底クラスやインターフェイスを作っているように見えて、理解が進みませんでした。
- 作者: Russ Olsen,ラス・オルセン,小林健一,菅野裕,吉野雅人,山岸夢人,小島努
- 出版社/メーカー: ピアソン桐原
- 発売日: 2009/04/01
- メディア: 単行本
- 購入: 13人 クリック: 220回
- この商品を含むブログ (64件) を見る
この本の良いところ
この本が良いと思ったのは以下の点です。
- Rubyの動的な性質を活かしている
- パターンが厳選されており、説明の順序もわかりやすい
- 解決したい問題とそれを解決するコードがある
1. Rubyの動的な性質を活かしている
本書では、Rubyの動的な性質を活かすことで、無駄な基底クラスやインターフェイスなしにパターンを実現しています。
最初は Java に近いクラス設計をした上で、Rubyのパワーでリファクタリングしていき、簡潔なコードでデザインパターンを表現していきます。
ちなみに第2章にRuby自体の解説もあるので、Rubyに詳しくない人でも読めると思います。
2. パターンが厳選されており、説明の順序もわかりやすい
本書では、GoFの23パターンのうちの14パターン+Rubyオリジナルの3パターンを解説しています。
一度に多くのパターンを学んでも理解が追いつかなくなるので、よく使うものに絞られていて良かったです。
説明する順番も、パターン同士の結びつきが考慮されており、理解しやすいです。最初に Template Method というわかりやすいものを取り上げ、次に継承ではなく委譲で実現するために Strategy を使うといった具合です。
ABC順にAbstract Factoryから解説されると辛いです。
3. 解決したい問題とそれを解決するコードがある
それぞれのパターンについてまず問題があり、それを解決するよう設計を改善していくという流れで説明されているのでわかりやすいです。
なぜ他のやり方ではダメで、そのやり方を選んだのかがわかるので、理解が深まります。
Webサイトによっては、UMLやコードがほとんどで、背景が簡単にしか書かれていないことがありますが、そういうサイトは一度ちゃんと理解した人が見返すためのものなんだと気づきました。
さらに知るために
本書で、解説しているのはGoFの23パターンのうちの14パターン+Rubyオリジナルの3パターン(DSL、メタプログラミング、Convention over Configuration)です。
GoFのパターンのうち、解説されていないのは以下の9パターンです。
- Prototype
- Bridge
- Facade
- Flyweight
- Chain of Responsibility
- Mediator
- Memento
- State
- Visitor
これらを含めて、今後改めてGoF本を読んで勉強したいと思います。
- 作者: エリックガンマ,ラルフジョンソン,リチャードヘルム,ジョンブリシディース,Erich Gamma,Ralph Johnson,Richard Helm,John Vlissides,本位田真一,吉田和樹
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 1999/10
- メディア: 単行本
- 購入: 21人 クリック: 711回
- この商品を含むブログ (206件) を見る
まとめ
パターンによって、あるあるだなーとか、こんなやり方があるんだとか、感想はいろいろでした。ただ、全体を通して名前がついていることの重要性、そして「パターン」という言葉の意味を理解できました。
名前をつけて他人と共有できる「パターン」は、デザインパターンだけではないので今後も勉強していきたいと思います。
ここまで書いておいて非常にアレですが、本書は現時点で入手が困難です。実は、何年も前に良さげだなーと思って買った後、そのまま積んであったのです。
新品の紙の本は売ってないし、電子書籍にもなっていないのが残念です。良書なので、ぜひ電子書籍で発売してほしいです。
ちなみに原著は電子書籍でも販売されていますが、読んだことないのでなんとも言えません。Amazon.co.jpのレビューには平易で読みやすいと書かれていますが、Amazon.comにはKindle版のフォーマットが非常に読みにくいというレビューもあります。
Design Patterns in Ruby (Addison-Wesley Professional Ruby Series)
- 作者: Russ Olsen
- 出版社/メーカー: Addison-Wesley Professional
- 発売日: 2007/12/10
- メディア: ハードカバー
- クリック: 12回
- この商品を含むブログ (5件) を見る
Mac OS X 10.9.2でGitHubにアクセスするとサーバー証明書のエラーが発生する場合の解決策
問題
Mavericks の最新のアップデート(10.9.2)をインストールした後、Google ChromeでGitHubにアクセスした時に、SSLサーバー証明書のエラーが表示されるようになりました。
このように「実際の github.com に接続できない」と表示されます。
アドレスバーのアイコンをクリックして証明書を表示させると、以下のようにDigiCertによって署名されていることがわかります。
Bitbucketでも同じくエラーが表示されます。Bitbucketの証明書もDigiCertによるものです。
ターミナルで、git cloneしたときにも以下のようにエラーが表示されます。
$ git clone https://github.com/rails/rails.git Cloning into 'rails'... error: SSL certificate problem: Invalid certificate chain while accessing https://github.com/rails/rails.git/info/refs?service=git-upload-pack fatal: HTTP request failed
解決策
調べたところ、以下のページが見つかりました。 キーチェーンアクセスのログインの欄にある、DigiCertの証明書を削除すると直ったと書かれています。
osx - Can not clone any git repository from github - Super User
fixed my problem by checking my Keychain (Application -> Utilities -> Keychain Access.app)
I figured out that I had a own Digicert Certificate in my loging Keychain. It looks like this was broken. I have removed it and after this everything works fine.
Now https://github.com has a green "button" in the url bar instead of an grey one.
キーチェーンアクセスを確認してみると、確かに「ログイン」の欄にDigiCertの証明書があります。
この「DigiCert High Assurance EV Root CA」を削除したところ、正常に表示されるようになりました。
注意:証明書の削除は間違って行うと、正常なWebサイトにアクセスした時にも警告が出るようになり、元に戻せません。自己責任で行ってください。
証明書のツリーを見ると、一番上がオレンジ色のルート証明書になっていることがわかります。
原因
原因はよくわかっていませんが、思い返すと2011年ごろにGitHubの証明書がエラーになるとかで、証明書を自分で追加したような気がします。
goto fail; goto fail;のバグが修正されたことで、以前自分で追加した証明書が不正だと判定されるようになったのかもしれません。
しかし、2011年の時点ではMavericksではなかったので、その時点では問題なかったはず。もう少し勉強しないと。。
Python/FlaskアプリからOAuthでGitHub APIを使う
このようなサンプルが意外と見つからなかったので作りました。
GitHub API
GitHubはWeb API を提供しており、リポジトリやユーザーなどをAPI経由で取得・操作することができます。
執筆時の2014年2月現在では、v3
とbeta
の2つのバージョンがあり、デフォルトはbeta
です。ややわかりにくい名前付けですが、beta
のほうが古いようで、2014年4月15日からv3
がデフォルトになる予定です。
利用するバージョンはHTTPリクエストのAccept
ヘッダーで指定できます。v3
とbeta
の違いはあまり大きくないみたいなので、現時点では特に指定せずにデフォルトのバージョンを使っても問題なさそうです。
認証方法 としては、Basic認証、OAuth2 Token、OAuth2 Key/Secretの3種類が利用できます。 今回は、Flaskを利用したWebアプリなので、OAuth2 Tokenを利用します。
PythonでOAuth2
oauth2というStarがたくさんついたライブラリがありますが、2年以上メンテナンスされておらず、名前の割にOAuth2には対応していないようです。
フォークされたものもありますが、メンテナンスされておりStarも多いRauthが良さそうだったので利用します。
Rauthは内部でいい感じに Requests を使っており、使いやすいです。
サンプルアプリケーション
以下のようなアプリを作りました。GitHubで公開しています。
# coding: utf-8 import os import binascii from flask import Flask, request, session, abort, redirect from rauth import OAuth2Service # Read secret keys from env vars # See: http://12factor.net/config GITHUB_CLIENT_ID = os.environ['GITHUB_CLIENT_ID'] GITHUB_CLIENT_SECRET = os.environ['GITHUB_CLIENT_SECRET'] app = Flask(__name__) app.secret_key = os.environ['SESSION_SECRET_KEY'] # necessary for session # Set up service wrapper for GitHub # - `client_id` and `client_secret`: available in an application page of GitHub # - `name`: name for human # - `authorize_url` and `access_token_url`: available in the GitHub's developer guide # http://developer.github.com/v3/oauth/#web-application-flow # - `base_url`: base url for calling API github = OAuth2Service( client_id=GITHUB_CLIENT_ID, client_secret=GITHUB_CLIENT_SECRET, name='github', authorize_url='https://github.com/login/oauth/authorize', access_token_url='https://github.com/login/oauth/access_token', base_url='https://api.github.com/') @app.route('/') def top(): """ Top page """ # For authorized users, show welcome message and links if 'username' in session: return 'Welcome @{0}! <a href="/repos">Repos</a> <a href="/logout">Logout</a>'.format( session['username']) # Generte and store a state in session before calling authorize_url if 'oauth_state' not in session: session['oauth_state'] = binascii.hexlify(os.urandom(24)) # For unauthorized users, show link to sign in authorize_url = github.get_authorize_url(scope='', state=session['oauth_state']) return '<a href="{0}">Sign in with GitHub</a>'.format(authorize_url) @app.route('/callback/github') def callback(): """ OAuth callback from GitHub """ code = request.args['code'] state = request.args['state'].encode('utf-8') # Validate state param to prevent CSRF if state != session['oauth_state']: abort(400) # Request access token auth_session = github.get_auth_session(data={'code': code}) session['access_token'] = auth_session.access_token # Call API to retrieve username. # `auth_session` is a wrapper object of requests with oauth access token r = auth_session.get('/user') session['username'] = r.json()['login'] return redirect('/') @app.route('/logout') def logout(): """ Logout """ # Delete session data session.pop('username') session.pop('access_token') return redirect('/') @app.route('/repos') def repos(): """ List recently updated repositories """ # Restore auth_session from the access_token stored in session auth_session = github.get_session(session['access_token']) r = auth_session.get('/user/repos', params={'sort': 'updated'}) repos = r.json() list_items = [] for repo in repos: list_items.append('<li>{0}</li>'.format(repo['full_name'])) return '<ul>{0}</ul>'.format('\n'.join(list_items)) if __name__ == '__main__': app.run(debug=True)
前提
Foremanですが、Rubyの環境を作るのが面倒な場合は、バイナリ1つで実行できるforegoがお勧めです。Go言語++
動かすまでの手順
1. GitHubでアプリケーションを登録
https://github.com/settings/applications/new でアプリケーションを新規登録します。
Authorization callback URL
は http://localhost:5000/callback/github
とします。
リダイレクトされるだけなので、ローカルアドレスで大丈夫です。
2. クローンして依存関係をインストール
$ git clone https://github.com/orangain/example-github-oauth-flask.git $ cd example-github-oauth-flask $ virtualenv --python=python3 venv # virtualenvを使う場合 $ . venv/bin/activate # virtualenvを使う場合 (venv)$ pip install -r requirements.txt
執筆時点で最新のRauth 0.6.2が依存するRequests 1.3.2では、インストール時にSyntax Errorが表示されますが、特に問題なく使えます。
3. 設定ファイルを配置
example-github-oauth-flask/.env
を以下の内容で作成します。リポジトリに入れるとマズイ設定項目は環境変数から読み込むのが 12factor のやり方です。
GITHUB_CLIENT_ID=(1で作成したアプリのClient ID) GITHUB_CLIENT_SECRET=(1で作成したアプリのClient ID Secret) SESSION_SECRET_KEY=(ランダムな文字列)
4. サーバーを起動
(venv)$ foreman run python github_oauth.py
これで、 http://localhost:5000/ にアクセスすれば以下のようなページが表示され、GitHubのAPIを利用できます。
参考サイト
unattended-upgradesはインストールしただけでは動かない
2018-06-17追記: パッケージの自動削除について追記しました。検証環境はUbuntu 16.04 LTS x86_64です。
ということを知らなかったので、Debian/Ubuntuでの自動アップデートについて改めて調べてみました。
動作を確認した環境は、Ubuntu Server 12.04 LTS x86_64です。
自動アップデートを有効にする
結論から言うと、以下のようにインストールすれば有効になります。既にインストールされている環境では3行目だけを実行すればよいです。
$ sudo apt-get update $ sudo apt-get install unattended-upgrades $ sudo dpkg-reconfigure -plow unattended-upgrades # Choose "Yes"
3行目のコマンドは、以下のファイルを生成してくれます。*1
$ cat /etc/apt/apt.conf.d/20auto-upgrades APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Unattended-Upgrade "1";
不要パッケージの自動削除
自動アップデートを有効にしているとカーネル関連のファイルが溜まってディスクフルになることがあります。これを防ぐために、不要なパッケージを自動削除します。
$ sudo vim /etc/apt/apt.conf.d/50unattended-upgrades
以下の設定をtrue
にします。
Unattended-Upgrade::Remove-Unused-Dependencies "true";
ログ
自動アップデートのログは、 /var/log/unattended-upgrades/
以下に出力されます。
デフォルトでは毎日1回実行されます。
ドキュメント
自動アップデートを有効にする方法は、 /usr/share/doc/unattended-upgrades/README
の冒頭に書かれています。
Unattended upgrades ------------------- This script can upgrade packages automatically and unattended. However, it is not enabled by default. Most users enable it via the Software Sources program (available in System/Administration). If you would prefer to enable it from the command line, run "sudo dpkg-reconfigure -plow unattended-upgrades".
ちなみに、 Ubuntu Server Guide には /etc/apt/apt.conf.d/10periodic
を以下のように書き換えるという方法が載っています。これでも設定内容は同じことなので動くと思いますが、古い方法らしいです。 *2
APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Download-Upgradeable-Packages "1"; APT::Periodic::AutocleanInterval "7"; APT::Periodic::Unattended-Upgrade "1";
参考サイト
iTerm2でSolarizedを使うと一部の色が灰色になってしまう問題の対処法
iTerm2でSolarizedの配色を使う場合、以下の記事が参考になります。
- のびーの食っちゃね~だらだらな日々。食っちゃ寝生活してても意外と平気だったりする。 : ターミナル (iTerm2) な Vim を Solarized カラースキーマで!
- ターミナルをiTerm2に換えた: 黄昏てなんかいられない
問題
しかし、単純にダウンロードしたColor Schemeを設定しただけど、それまで色がついていたはずの文字列が灰色になってしまうことがあります。
特にSSHで他のサーバーにログインした時に色がなくなって困ることがありました。
こんな感じで色あせたように見えます。
解決策
以下のページに書いてありました。iTerm2の設定で、Profiles → Text → "Draw bold text in bright colors" のチェックを外すと、綺麗に表示されるようになります。
iterm text color highlighting · Issue #71 · altercation/solarized
以下のように世界が色を取り戻します。
原因
Solrizedのカラーパレットをよく見ると、Brightの列は一部の色が灰色になっています。 "Draw bold text in bright colors"のチェックが付いていると、iTerm2は太字をこの灰色でレンダリングしてしまうのです。
まとめ
Issueを眺めていると、この問題は上で挙げたIssueの他にもいろんな人が2〜3年前から指摘していますが、直る気配はないようです。
とりあえず、iTerm2でSolrizedを使う場合は、"Draw bold text in bright colors"のチェックを外すか、色を自分でカスタマイズすると綺麗に表示できます。
Ruby製アプリじゃなくてもTurnipで自動受け入れテストがしたい
Python製のWebアプリケーションの自動受け入れテストをしたくて、調べてみました。
Ruby界隈だと、最近ではCucumberに代わってTurnipというツールが流行っているみたいなので試してみました。
自動受け入れテスト用のライブラリ
Rubyで自動受け入れテストをしようとすると、以下の3つの選択肢があるようです。
- Cucumber
- テストケースを自然文で書けるため、非プログラマでも読みやすい。
- Turnip
- RSpecのfeature spec
Cucumberは以前にも使ったことがあるので、今回はTurnipを選択してみました。詳しい説明は以下のページに譲ります。
- Rubyist Magazine - エンドツーエンドテストの自動化は Cucumber から Turnip へ
- Rspec/Capybara/Turnipの入門記事を全力でまとめてみた - 酒と泪とRubyとRailsと
Webページを自動操作するライブラリ
Webページの自動操作にはCapybaraを使います。デフォルトではRackTestというドライバが使われますが、これはHTTP通信を行わないドライバでありRackアプリケーションでしか使えません。
このためHTTP通信を行う他のドライバを使います。ヘッドレスでJavaScriptを利用できるドライバとして、以下の2種類があるようです。
- capybara-webkit
- Qtを利用したWebKitドライバー
- Poltergeist
- PhantomJSを利用したWebKitドライバー
最近ではPoltergeistのほうがインストールが簡単かつ高速で人気らしいので、今回はPoltergeistを使いました。
前提
前提として、Ruby 2.1*1 およびBundlerがインストールされているものとします。
PoltergeistのインストールにはPhantomJSが必要なので、 PoltergeistのREADMEに従ってインストールします。
Macならば brew install phantomjs
で簡単にインストール出来ました。
準備
適当なフォルダを作ります。
$ mkdir bus-spec $ cd bus-spec
以下のコマンドでGemfile
を作ります。
$ bundle init
Gemfile
を以下のように編集します。
source "https://rubygems.org" gem 'turnip' gem 'capybara' gem 'poltergeist'
以下のコマンドでGemをインストールします。
$ bundle install --path=vendor/bundle
TurnipのREADMEを参考に、テストに必要なファイル・フォルダを作成します。
$ mkdir -p spec/{features,steps}
.rspec
を以下の内容で作成します。
-r turnip/rspec
spec/spec_helper.rb
を以下の内容で作成します。
Dir.glob('spec/steps/*steps.rb') { |f| load f, true } require 'capybara/rspec' require 'capybara/poltergeist' Capybara.default_driver = :poltergeist # 以下の関数はGeolocation APIをシミュレートするヘルパー関数です。 def simulate_location(latitude, longitude, accuracy=0) Capybara.current_session.execute_script <<-EOS window.navigator.geolocation = { getCurrentPosition: function(success) { var position = {coords: {latitude: #{latitude}, longitude: #{longitude}, accuracy: #{accuracy}}}; success(position); } }; EOS end
rspec
を実行して以下のように表示されれば準備完了です。
$ bundle exec rspec No examples found. Finished in 0.00005 seconds 0 examples, 0 failures
Turnipによる受け入れテスト
それではTurnipによる自動受け入れテストを書いていきます。
spec/features/search.feature
には、プログラムに詳しくない人が読んでもわかる書式(Gherkin形式)で機能を記述します。
# encoding: utf-8 # language: ja 機能:PCまたはスマートフォンでバス停を検索 シナリオ:トップページにアクセスして検索する 前提ポケドスにアクセスする 前提"京都市役所"に居る もしトップページを表示する ならば画面に"近くのバス停を探す"ボタンが表示されていること もし"近くのバス停を探す"ボタンをクリックする ならば画面に"この近くのバス停"と表示されていること かつ画面に"京都市役所前: 91m"と表示されていること
プレースホルダーに日本語を使う場合は、""
で囲うなど少し工夫が必要です。参考:Ruby - Turnip で placeholder のマッチングを柔軟にする - Qiita
spec/steps/bus_steps.rb
には、上で書いたGherkin形式のステップ(「前提」「もし」「ならば」など)に対応するプログラムを書きます。
# encoding: utf-8 step 'ポケドスにアクセスする' do Capybara.app_host = 'http://bus.capybala.com/' end step ':placeに居る' do |place| case place when '京都市役所' @place = {lat: 35.01129870069897, lng: 135.76807713296512} else raise "Unknown place" end end step 'トップページを表示する' do visit '/' simulate_location(@place[:lat], @place[:lng]) if @place end step '画面に:labelボタンが表示されていること' do |label| expect(page).to have_link(label) end step ':labelボタンをクリックする' do |label| click_link label end step '画面に:textと表示されていること' do |text| expect(page).to have_content(text) end
再度rspec
を実行して以下のように表示されれば成功です。
$ bundle exec rspec . Finished in 2.92 seconds 1 example, 0 failures
まとめ
Turnipはこのように、テストする機能を自然文で書けるテストライブラリであり、Capybaraと組み合わせることでWebサービスの自動受け入れテストを行えます。
2012年に書いた記事と比べると、Cucumber → Turnip、capybara-mechanize/capybara-webkit → Poltergeistと変化しました。
正直Rubyをゴリゴリ書いていない身としては、TurnipのCucumberに対する利点として挙げられる、RSpecが使える点とプレースホルダーが簡単に書ける点が、それほど嬉しいことなのかよくわかりませんでした。
また、CucumberやTurnipのGherkin書式は、普通のテストを書くのに比べてコストがかかる割に、どの程度受け入れられるの?という考えもあります。 参考:RSpecで、Cucumberのような結合試験のspecを記述する - 高尾宏治日記 on はてな
うまく使えば強力なツールですが、使いドコロをよく考えて使う必要があるでしょう。
ここで説明したものをもう少し複雑にしたコードをGitHubで公開しています。
参考サイト・書籍
- Rubyist Magazine - エンドツーエンドテストの自動化は Cucumber から Turnip へ
- Turnip について (1) / まずは動かす - Thanks Driven Life
- Everyday Rails… Aaron Sumnerによる et al. [Leanpub PDF/iPad/Kindle]
*1:今回は2.1を使いましたが、1.9.3以上ぐらいで動くと思います。