Home > Tags > Rails

Rails

Cucumber+email_specでActionMailerのテストをする

今までメール(ActionMailer)のテストはどうにも面倒で、自分でブラウザから動かしてログを見てってやっていたんですが、Cucumberでメールのテストもできるっぽいので試してみました。

確認環境はRails2.3.2、Cucumber0.2.3、email_spec0.0.10。

email_specはgithubをsourceに指定してインストールすることができます(bmabey’s email-spec at master – GitHub)。config/environments/test.rbあたりに書いておくといいと思います。

email_specをインストールするとgenerateにコマンドが追加されています。

$ ruby script/generate email_spec

ここで生成されるfeatures/step_definitions/email_steps.rbは英語なので日本語に直したものを使います。

これは僕がこないだ出向していたときのプロジェクトに使われていたものなので、多分その出向先の誰かが作ってくれたものだと思います。ありがとうございます。

最後にemail_specを使えるようにfeatures/support/env.rbに

require 'email_spec/cucumber'

を追記してやります。あとはシナリオにテストケースを書いてテストすればOKです。

 ならば "example@hoge.com" がメールを1通受信していること

ちなみにメールの文章中にあるURLに遷移することもできるので、例えば仮登録→メールで認証URL→本登録という流れもちゃんとテストできます。

Cucumberの登場でRailsのテスティング環境が変わった

ちょっと大げさなタイトルかもしれないですが、個人的にはそれぐらいの感動。「これで勝つる!」な気分。何に勝つのか知らないけれど。

今までのRailsのテストはもっぱらRSpecで書いてて、確かにこれはこれで素晴らしい。採用当初はバグが減ってその時も「これで勝つる!」な気分でした。でもやっぱり受け入れテストがネックになるんですよね。Seleniumとかも使ったりしてましたけど、ブラウザががちゃがちゃ動くし、なんとなく面倒で結局手動で確認という感じになってしまいました。

そこでCucumberの登場ですよ。個人的に素晴しいと思うのは

  • テストケースが自然文(っぽい)
  • そんなに邪魔じゃない

あたりかなぁ。テストケースが自然文っぽいというのは実はかなり大事で例えば

お客さん: まずトップページにアクセスするとログインフォームがあって、そこにログインすると「ようこそ!ほげほげさん」と出すようにして欲しい

っていう要望、つまりユーザーストーリがあった場合に割とそのままシナリオとしてテストに変換できる。

もし トップページ にアクセス
かつ メールアドレス に hogehoge@hoge.com と入力
かつ パスワード に hogehoge と入力
かつ ログイン ボタンを押す

ならば ようこそ!ほげほげさん と表示されること

これは疑似言語とかじゃなくて、これがそのままテストとして実行できる。つまり今までこっちがブラウザをポチポチしてお客さんに「できましたー(多分)」と報告していて、お客さんからすると何をどうテストしていたのかが曖昧という問題があったのだけれど、 Cucumberだとお客さんと一緒に受け入れテストを作ることができて、そのテストケースもお客さんが読むことができるから曖昧さが消えて安心が生まれる。

もちろん開発者にもメリットはあって、Cucumberはユーザ視点レベルのテストだからテストとしては一番外側なテストでここのテストがちゃんと通っていれば、アプリケーションの動作がユーザ視点での正しく動くという保証ができるわけです。

その保証ができるとどうなるかと言うと、極端な話、ユーザ視点での動作が正しければ内部の動作なんて割とどうでもいいわけで、内部に自由がでてきます。内部に自由がでてくると、コードの修正や設計の変更が怖くなくなり、リファクタリングがしやすくなります。結果、メンテのしやすいアプリが出来上がります。

Cucumberは残念なことに、Ajaxのテストができないのでその辺りはSeleniumや手動での確認、あとはjsonやxmlを吐き出すアクションをRspecでテストするなど臨機応変に対応する必要がありそうです。

最後にオマケですが、僕が実際にCucumberを使ったときの開発の流れをば。

  • Cucumberのシナリオを書く
  • Rspecでモデルのテストを書く
  • モデルにロジックを書く
  • Rspecでコントローラとビューのテストを書く
  • コントーラとビューを書く
  • Cucumberのテストが通ったのを確認して、実際に手動でも確認する
  • それぞれのテストをautotestとかでまわしながらリファクタリング

まず最初にCucumberのシナリオを書きます。仕様のブレを防ぐためです。この時点では当然Cucumberのテストは通りませんが、とりあえずモデルからビューにかけてボトムアップでコードを書きます。基本的にテストファーストです。

ビューまで書いたらこの時点でCucumberのテストが通っていれば手動で確認、通っていなければ同じ要領でテストファーストでコードを書きます。

最後に今まで書いたテストが落ちないようにリファクタリングを行います。ここまでで1つのユーザストーリ(Cucumberでいうフィーチャ)の実装が完了します。

CucumberはRspecの時もそうでしたが、新しく導入するときにどうしてもコストがかかります。でも慣れてくればそのコストは大分減りますし、stepもある程度使いまわせるものが多いので長期的に見れば逆にコストが減ると思っています。個人的にはCucumberを採用しない理由はないと思うので、この記事を読んでいる人も是非試してみてください。

  • Comments (Close): 0
  • Trackbacks (Close): 0

accepts_nested_attributes_forしたモデルの日本語化

名前、長いよね。accepts_nested_attributes_for。以下、ネストしたって言います。長いので。

最近はもっぱら日本語化はi18nにしてるんですが、今回ちょこっとこのネスト関係ではまったのでメモ。

とりあえずモデルの作成までガッとやります。Railsのバージョンは2.3.2です。最下行はgemのインストールが必要です(http://github.com/amatsuda/i18n_generators/tree/master)


それぞれアソシエーションとバリデーションのコードを追記しておきます。当然、accepts_nested_attributes_forの記述も必要です。その辺の詳細は省くので適当にググってみてください。

で、日本語化はモデルを作成した後に行なっているのでとりあえずカラムは日本語化されてる筈です。その辺の詳細も省きます。

されてるはずなのですが、一部日本語化されてない箇所があるのでこれを日本語化させます。

userモデルのところにentries_xxxが追記されています。ちなみにhas_oneの場合はentry_xxxになります。xxxはカラム名。

おわり。

Rails2.0.2から2.2.2に上げたらFileColumnがNameErrorだした。

自分の担当している案件のRailsのバージョンを2.0.2から2.2.2に上げたら以下のようなエラーが発生。

uninitialized constant FileColumn::ClassMethods::Inflector (NameError)

これの対応は/vender/plugin/file_column/lib/file_column.rbの以下の部分を

my_options = FileColumn::init_options(options,
                                                 Inflector.underscore(self.name).to_s,
                                                 attr.to_s)

以下のように書き換えればとりあえず対応できる。

my_options = FileColumn::init_options(options,
                                                 self.name.to_s.underscore,
                                                 attr.to_s)

ちゃんちゃん。

Rails2.2のi18n(国際化)を簡単に試してみた

11月21日にRails2.2が正式リリースされた。 RCなどでチェックしてた人は既に知っているかもしれないけど、2.2からデフォルトでi18n(国際化)対応がされている。

ちなみにi18nというのはinternationalizationの頭と後ろのiとn、文字数が18文字というところから来てるらしい。

とりあえず、まずはアプリの作成。

$ rails i18n_demo

作成されたファイル群の中にconfig/locales/en.ymlというファイルが出来ていると思うけど、これが翻訳ファイルになる。今回は英語と日本語を用意しようと思うので、en.ymlをコピーしてja.ymlを作成する。

$ cp config/locales/en.yml config/locales/ja.yml

翻訳ファイルを修正しようにも、そもそもアプリケーションが出来ていないので、scaffoldで簡単に作ってしまおう。

$ ruby script/generate scaffold entry title:string body:text
$ rake db:migrate

とりあえずは適当にサイトタイトル的なものでも作ってみよう。翻訳ファイルに関しては元々あった内容は削除してしまっていい。

# config/locales/ja.yml
ja:
  site_title: サイトタイトル

一応英語も。

# config/locales/en.yml
en:
  site_title: SiteTitle

Viewの修正。

# app/views/entries/index.html.erb

<%= I18n.t :site_title %>

この状態でサーバーを起動し、http://localhost:3000/entriesにアクセスしてみる。「SiteTitle」という文字が表示されているはずだ。この状態だとまだ言語がデフォルトの英語の設定になっている。多分、ブラウザの言語設定とかは見てなさげ?。ちょっとソースを見てないのでまだ未確認。もしかしたら、リクエストのヘッダーを見て言語を決める部分は自分で実装しなきゃいけないのかもしれない。

なんにせよ、日本語で表示したい場合は以下のようにする。

# config/environment.rb
# 最下行に追加
I18n.default_locale = "ja"

これで再度アクセスすれば「サイトタイトル」と表示されているはずだ。

次にGETパラメータで言語が切り替わるようにしてみよう。

# app/controllers/application.rb

before_filter :set_locale
def set_locale
  I18n.locale = params[:locale]
end

これでhttp://localhost:3000/entries?locale=enで「SiteTitle」と表示されているはずだ。

次にモデルの日本語化をしてみよう。まずはエラーメッセージを出すためにバリデーションを追加する。

# app/model/entry.rb
class Entry < ActiveRecord::Base
  validates_presence_of :title, :body
end

http://localhost:3000/entry/newでフォームに何も入力せず送信すればエラーがでるので英語で表示されていることを確認する。

普通のバリデーションのエラーとは違うエラーがでているけど、これは翻訳ファイルをちゃんと書いてないからでているエラー。英語の方はちょっと面倒なので無視するが、とりあえず日本語化をすすめる。

#config/locales/ja.yml
ja:
  site_title: サイトタイトル
    activerecord:
      errors:
        template:
          header: "{{model}}でエラーが発生しました。"
          body:   以下のエラー内容を確認してください。
        messages:
          blank:  が空白です。
      models:
        entry: エントリー
      attributes:
        entry:
          title: タイトル
          body: 本文

以上の修正をして、もう一度エラーを出してみるとエラーが日本語に変わっているはずだ。翻訳ファイルのどの部分がどの部分に対応してるかは雰囲気でわかると思う。

今回は必要な部分だけ翻訳したけど、もうちょっとちゃんとした翻訳ファイルが必要ならRailsをローカライズするも参考にするといいと思う。

諸々めんどくせー!って人は松田さんのRails 2.2の I18nによる日本語化を最も簡単に行う方法へどうぞ。gemで一発です。

まだ2.2はリリースされたばかりで、i18nまわりの日本語の情報はあまり多くはないと思うけど、そこまで複雑な仕組みでもないし、翻訳ファイルに関して言えば基本的な部分(ActiveRecordとか)は使い回しができるので手間もそこまで大きくはないと思う。ブラウザの言語設定での判断もそのうちプラグインがでてくるんじゃないかと思っている。

「国際化とか必要ないよー」の人も、バリデーションのエラーを日本語化するのにgettextを入れる必要がなくなったりとこれでかなりラクになるんじゃなかろうか。

RailsでMySQLとPostgreSQLを同時に扱う方法(+問題点)

プラグインとか無しで、複数のDB(MySQLとPostgreSQL)を扱う方法。Rails2.1でしか試してないけど、多分1.2とかでも大丈夫。多分。MySQLとPostgreSQLの接続ライブラリはgemからインストールしておくこと。

まずは、database.ymlに扱うデータベースの定義を書く。

development:
  adapter: mysql
  encoding: utf8
  database: db_development
  username: root
  password:

test:
(面倒だから略)

production:
(面倒だから略)

psql_development:
  adapter: postgres
  encoding: utf8
  database: ma2_mcd
  username: root
  password:

psql_test:
(面倒だから略)

psql_production:
(面倒だから略)

次にPostgreSQLに接続させたいモデルを編集する。例えば、ProfileモデルはPostgreSQLの方に接続させたい場合は以下のようになる。

class Profile < ActiveRecord::Base
  establish_connection("psql_#{RAILS_ENV}".to_sym)
end

establish_connectionにシンボルでdatabase.ymlの定義を渡せば、そのモデルはその定義のデータベースに接続する。本番、テスト、開発でそれぞれ定義が違うだろうから、RAILS_ENVで今起動している環境の定義を呼び出すようにしてやる。

追記

上記の設定で一応アプリは動作するけど、いくつか問題点があるのでそれをメモしておく。

マイグレーションの使い分けはどうする

例えば、db/migrateにcreate_hoge_for_psql.rb(数値は略)とcreate_foo_for_mysql.rbと合った場合、それをどう実行するかが問題になってくる。

普通にrake db:migrateやrake db:migrate RAILS_ENV=psql_developmentとやったら、hogeとfooが両方とも作られてしまう。こちらとして望んでるのは、hogeがPostgreSQLのみ、fooがMySQLのみにできて欲しい。

うーむ、弱った。

VimM#2でRails.vimについて話してきました

VimM#2

とりあえず資料。正直、これだけ見てもさっぱりな資料だとは思いますがw

Rails.Vim
View SlideShare presentation or Upload your own. (tags: 勉強会 rails)

Vimの人達の集りでRails.vimというRails使う人限定のプラグインの話はどうなんだろーとか思っていたけど、結構Rails使ってる人いたみたいでよかった。

発表内容自体はかなりgdgdというか、伝えたいことをうまく伝えられなかったなーと言った感じ。やっぱり事前に練習しとくべき。あとは慣れかなー。しばらくは積極的に発表する側で頑張りたい。

それと、RSpecの具体的な対応についてはrails.vimのファイル切り替えをRSpecに対応させる(仮)を参照。

それにしても、エディタ1つで4時間(懇親会含めるともっとある)発表があって、それに40人集まるってのもすごい話だ。

あと、VimMのMはみみのMらしいです。VimM#2はmickey24がうさみみをつけて主催するそうです。期待。

Selenium on Rspecを軽く試してみた

とりあえずはScaffoldで簡単なアプリを作って、そのテストケースを作成、実行してみるところまで。そんな難しいことはしない。Railsは2.0.2を使用。2.1だとSeleniumにパッチを当てなきゃいけないので面倒。2.1の人はSelenium on Rails patch for RoR 2.1からパッチをあててください。

準備

まずは準備としてプラグインのインストールと、Scaffoldで簡単なアプリを作成する。

$ rails _2.0.2_ blog
$ cd blog
$ ruby script/plugin install http://svn.openqa.org/svn/selenium-on-rails/selenium-on-rails
$ ruby script/generate scaffold entry title:string description:text
$ rake db:migrate

ここまでできたらtest環境でサーバーを起動し、seleniumがちゃんと起動するか確認する。

$ ruby script/server -e test

そうすると下記エラーがでるが、sessionをデータベースであつかうように変更するととりあえず回避できる。原因はよくわかんない。

CGI::Session::CookieStore::TamperedWithCookie in SeleniumController#support_file

sessionをデータベースで扱うようにするにはconfig/environment.rbの47行目のコメントアウトをはずし、migrateする。

config.action_controller.session_store = :active_record_store
$ rake db:sessions:create
$ rake db:migrate
$ rake db:test:prepare
$ ruby script/server -e test

ブラウザでhttp://localhost:3000/selenium/を表示して以下のような画面になればOK。

Selenium初期画面

Selenium初期画面

テストケースの作成

次にテストケースを作成する。SeleniumはテストケースをHTMLで書くので手書きでも書けるけど、面倒なのでFirefoxのaddonの1つ、Selenium IDEを使用する。

Selenium IDEを使用すると、自分の操作を記録しそのままテストケースに変換できるので非常にラク。Selenium IDEを開き、右上の赤いボタンを押すと操作の記録が始まる。あとはテストしたい操作を手動で行なえばいい。

Selenium IDE

Selenium IDE

今回のテストは「/entriesにアクセスし、そこから/entries/newに移動、タイトルと詳細を入力・保存し、/entries/showでタイトルが実際に表示されている」という内容にした。基本的には記録ボタンを押し、そのまま普通に入力していけばいい。最期の「タイトルが実際に表示されている」というものに関しては文言を範囲選択し、右クリックしverifyTextPresentを選択すればいい。

verityTextPresent

verityTextPresent

ここまでできたらもう一度赤いボタンを押し、操作の記録を終了させる。最期に今の操作をテストケースとして出力させればいい。

出力方法はIDEのウィンドウにフォーカスを合わせて、ファイル > テストケースに名前をつけて保存。ここではファイル名はadd_entryとしといた。本来なら命名規約的なものもあった方がいいかもしれないけどここでは気にしないことにする。保存先はseleniumに読み込ませるため、RAILS_ROOT/test/selenium/以下に保存。

以上でテストケースの作成は完了。

テストの実行

もう一度http://localhost:3000/selenium/を開くと、今つくったテストケースが読み込まれているはず。あとはそのテストを右側のSelenium TestRunnerから実行すると、ブラウザがガチャガチャ動いてテストケースを実行してくれる。

今回はさっきのテストケースを作ってから何も変更を加えていないので、オールグリーンになるはず。

まとめ

かなり単純なケースだけど、大体テストケースの作成から実行までを試してみた。まぁとりあえず導入してみるには丁度いいレベルかなと。Selenium IDEを使用すればプログラマ/エンジニアじゃなくてもテスト書けそうだし。

あとは確かSelenium on Railsの機能にfixturesのロード機能とかあった気がするので、その辺りも調べていきたいと思う。

Railsのpartialの使い方

あまりpartialをちゃんと使いこなせていない気がするので(いつもインスタンス変数で適当に値わたしてる)使い方をちょっとメモ。

render :partial => 'entry'

一番基本となるpartialの使い方。_entry.html.erbを描画。

render :partial => 'entry', :locals => {:title => "タイトル"}

localsでpartial側に値を渡す。partial側ではローカル変数titleで文字列”タイトル”を参照できる。

render :partial => 'entry', :object => @object
render :partial => @entry # インスタンス変数で単数形

objectでpartial側に値を渡す。@objectの内容をpartial側でローカル変数entryで参照できる。

render :partial => 'entry', :collection => @objects
render :partial => @entries # インスタンス変数で複数形

collectionで値を渡す。@objectsの値を1つずつ、partial側でentryとして参照できる。もちろん描画は@objects分繰り返される。

Ruby on Rails 2.0アプリを10秒で作る2.0

  • Railsのインストールは作業時間に含みません
  • shファイル作る時間も含みません
  • 筆者の環境がMacBookなのでそれ以外の環境は適当に脳内変換すべし
  • ネタです

適当なディレクトリにhoge.shを作る。

rails itpro
cd itpro
script/generate scaffold itpro userid:string name:string
rake db:migrate
$ chmod 775 hoge.sh
#別に775じゃなくてもとりあえず実行権限あたえればおk

んじゃ、こっからスタート。以下の3行を10秒でうちこむ。Tabキー使えば余裕でしょ。あと、hoge.shの実行時間とserverの起動は考慮してないから。

$ ./hoge.sh
$ cd itpro
$ ruby ./script/server

んで、http://localhost:3000/itprosにブラウザからアクセス。表示されればおk。

Ruby on Rails 2.0アプリを1分で作る : IT Pro

Home > Tags > Rails

Feeds
Meta
Others

Return to page top