no_picture

CucumberとTurnipとSpinachと。

最近 spinach というライブラリがあることを知って Cucumber や Turnip と同じようなものだということはわかっていたのですが、ちゃんと調べてみることにした。 Cucumber Turnip Spinach 「きゅうり」と「かぶ」と「ほうれん草」ですね。 一応ざっくり解説しておくと ビヘイビア駆動開発 を実践するためのテスティングフレームワークです。 Gherkin という書式を利用して自然言語をならべて記述した文書を使い、自動テストとの結びつけができます。 動くことを確認することができる仕様書として使えます。 今回登場している3つのソフトウェアは Gherkin を使っている Cucumber がら派生したライブラリです。 Turnip は Cucumber から派生して、使いやすく改良したものです。 Spinach は Cucumber から機能を削減して見通しをよくしています。 Turnip Cucumber から派生して、Cucumber のイケてないところが修正されており、最近徐々に人気が出ているようです。 るびまで取り上げられているので知っている方も多いと思います。 また rspec コマンドから実行することになります。 Spinach Spinach は GitLab で利用されています。 Cucumber から強い機能が外されてます。 ステップから引数をうけとったり、シナリオテンプレートが廃止されていたり。 「重複を排除するための機能は Ruby のレイヤーでやらせてしまおう」という感じがしました また、Gherkin は独自のものが再実装されていて国際化がされてないので、Cucumberだとできることが一部できません。 When や Given などは、日本語で「前提」や「もし」とかけましたが、Spinach 日本語が使えません。 もうちょっと詳しく 例として Feature をひとつ作成してみました Feature: Cucumber と Turnip と Spinach ちょっと遊んでみる Scenario: 配列の作成 Given

no_picture

Cucumber-js を試した。

広島Webシステム開発勉強会 で Cucumber-js を試してました。 先に雑感をかきます。 まだ完成度が高くない感じです。 アサーションが用意されてないので、使いやすくするには自分でなんとかしないといけないような感じでした。 step_definision が Ruby版より難しそうでした。 日本語への対応がまだできていませんでした。 色がまだつきません コマンドラインオプションをまちがえるとコールスタックが表示されます。 インストール npm install -g cucumber cucumber.js というコマンドがインストールされます。 試してみる Feature: hogehoge Scenario: hogehoge Given hoge Then goro 試しにこんな feature を書いてみました。 他に作成したのは features/step_defnition/myStepDefinitions.js と features/support/world.js です。 world.js で step で使える DSL を強化できますが今回は特になにもしていません。 Stepの定義は以下のように書きました。 var myStepDefinitionsWrapper = function () { this.World = require("../support/world.js").World; this.Given(/^hoge$/, function(callback) { // express the regexp above with the code you wish you had callback(); });

no_picture

cleditor の内容を javascirptで変更する in Cucumber

Cucumber の @javascript で実行しているシナリオに ]cleditor](https://github.com/cleditor/cleditor/blob/master/jquery.cleditor.js) という WYGSYG が利用されていて 普通に値を代入しただけだと反映されない問題に直面した。 caybara のドライバーには poltergeist を使用しています。 バリデーションをかけていて、入力しないと進めないので、javascriptを使って入力することにした。 $(selector).data('cleditor').$area.html( content ); .data(‘cleditor’).$area というところに情報が保存されていることがわかったので、そこのHTMLをさしかえます。 Safari で実行した場合は画面表示は変更されないので注意です。 これを cucumber の step で実行したいので、 description = "hogehoge" code = <<"JAVASCRIPT" $('#hogehoge').data('cleditor').$area.html('#{description}'); JAVASCRIPT evaluate_script(code) として、 evalute_script を利用して実行しました。 data属性にデータを保存しておくのは一般的なのかな?この辺の事情はよくしりません。

no_picture

cucumber で PhantomJS を使う

Cucumber で使うブラウザを PhantomJS にしたい。 Cucumber -> Capybara -> Poltergeist -> PhantomJS という感じに利用します。 PhantomJS は画面のないブラウザと言うと、伝わりやすいでしょうか。 統合的なテストを行う場合、Rails プロジェクトでは Cucumber がよく使われています。 Cucumberのシナリオに @javascript というタグをつけると Selenium を利用して Firefox を制御してテストを行うことができます。 非常に便利なのですが、処理が長かったり、また、X11の起動してない Linux などで動かそうとするとちょっと問題がおきます。 そこで、画面の表示をしないブラウザでテストしたくなります。 また、実際によく使うわれるのはレンダリングエンジンは Webkit です。 そのためのブラウザとしての有力候補が PhantomJS です。 PhantomJS のレンダリングエンジンは Webkit で、必要であればスクリーンショットがとれます。 Travis CI でも利用できるようです。(未確認) 利用までの手順としては PhantomJS のインストール Rails プロジェクトに Poltergeistを追加 featrue/support/env.rb を設定 となります。 PhantomJS のインストール http://phantomjs.org/download.html から ダウンロードできます。 Mac であれば Homebrew や Macport でインストール可能なようです。ダウンロードしても bin/phantomjs を 環境変数PATH に入っているところに配置するだけです。 Rails プロジェクトに Poltergeist を追加 Gemfile に group :test do gem 'poltergeist' end と追記すれば良いです。 feature/support/env.rb を設定 設定しないと使えません。 feature/support/env.rb に以下を追記すればよいでしょう。 require 'capybara/poltergeist' Capybara.javascript_driver = :poltergeist @javascript つけるのがめんどくさい!

no_picture

cucumber で シナリオを Ruby のバージョンによって実行しなかったり

どんなにがんばっても ruby 1.8.7 では動かすことができない cucumber のシナリオがあって、これを対応した時のメモ。 結論から書いておきます。 @fails_on_1_8_7 というタグを使って、失敗するシナリオをタグづけ cucumber の profile に ruby_1_8_7 をつくり --tags ~@failds_on_1_8_7 を設定 rake 実行時に RUBY_VERSION をみて profile を ruby_1_8_7 になるように設定 です。 テストは Travis CI で実行します。Travis では rake が実行されるので、rake 実行時にprofile を切り替えるようにしました。 もともとそのプロジェクトには ruby_1_9 というのと ruby_2_0 というのがあったのでそれらに合わせました。 具体的には cucumber/cucumber に出したpull request です。 ちょっと掘り下げておく。 cucumber には profile という機能があって、あらかじめ設定しておいたオプションを切り替える機能があります。 cucumber --profile ruby_1_8_7 みたいに使います。 これを rake task に割り当てるには Cucumber::Rake::Task のインスタンスのprofile に渡してやればいいです。 new したときのブロックの第1引数が Cucumber::Rake::Task そのものです。 例: Cucumber::Rake::Task.new do |t|

no_picture

Cucumber のフィーチャの文法 - Gherkin

Cucumber 利用していますか? 基本的な使い方はわかるんだけど、なんだかもっと上手く使えるんじゃないだろうか?と、もやもやしながら使っています。 少くとも私の周りには Cucumber について情報交換できる人がいないです。 それでも、SlideShare や Speaker Deck なんかに公開されたスライドでよくみかけるので、使い込んでるところでは使い込まれているのだと思います。 Cucumber は Rails プロジェクト以外でも利用されているようで、範囲が広いです。もうちょっといろんな情報がWeb上に流れていても良い気がします。 私が知る限りでは Cucumber についてもっと詳しく書かれているのは The Rspec Book です。 前置きはさておき、 Cucumber の *.feature は Gherkin という 言語で書きます。 その文法について調べたのでそのメモを整理しておきます。 ちなみにこの内容はソース読んだり、Wikiに書かれているものを参考したもので、仕様として記述されてない情報もあるので未来のバージョンでは予告なく変更される部分があるかもしれません。 こんな長くて不正確な記事読みたくないよ!という人は BNF を読むのが手っ取りです。 というか、BNFが読める人は読みましょう。 むしろ、もっとはやく読めばよかった。 具体例 Gherukinのという言語で書いた文書の例を上げておきます。 内容はシステムに関するものにしませんでした。 # language: ja @blog フィーチャ: ブログを書く ブログを書くには本人のやる気と書く時間が必要です。 アウトプットは次のインプットに繋がるので積極的に行なうべきです。 # これはコメントでタグの後にはかけない # @ではじまるのはタグ @good シナリオ: ブログが書ける ブログが書ける場合はやる気と時間があるのです。 # ネタがないとかけないです。 前提 ネタがある # 時間がないとかけないです かつ 納期に終われていない # 先輩とかいないですけど もし 先輩にブログを書けと言われた # オチがない ならば ブログが書けている @bad シナリオ: デスマ中はブログが書けない デスマ中ダトソレドコロジャナインダ!!

no_picture

Cucumber の Capybara で 複数の同じ名前のリンクに対応するステップ

Cucumber のステップで もし /^"(.+)"をクリック$/ do |name| click_on name end というステップを書いていますが、name に複数マッチしてしまうとエラーが発生しています。同じ名前にならないようにすればいいのですが、そうもいかない場合もあります。結局、以下の方法を用意しました。 もし /^(\d+)番目の"(.*?)"をクリック_$/ do |n, name| n = n.to_i - 1 all(:link_or_button, name)[n].click end 何番目のリンクか指定することで回避しました。 もうちょっと詳しく click_buttonと同じことをやろうとすると find(name) や all(name) ではうまくいきません。調べてみると XPath::HTML.link_or_button というメソッドを使用して、findに渡すXPathを生成してることがわかりました。。 これをどうやって使うというと all の第一引数に使えばいいことがわかりました。 さらに詳しく page.class # => Capybara::Session click_onメソッドやallメソッドのレシーバである page オブジェクトは Capybara::Session でした。pry で調べました。 Capybara::Session NODE_METHODS = [ :all, :first, :attach_file, :text, :check, :choose, :click_link_or_button, :click_button, :click_link, :field_labeled, :fill_in, :find, :find_button, :find_by_id, :find_field, :find_link, :has_content?, :has_text?, :has_css?, :has_no_content?, :has_no_text?, :has_no_css?, :has_no_xpath?, :resolve, :has_xpath?, :select, :uncheck, :has_link?, :has_no_link?, :has_button?, :has_no_button?, :has_field?, :has_no_field?, :has_checked_field?, :has_unchecked_field?, :has_no_table?, :has_table?, :unselect, :has_select?, :has_no_select?, :has_selector?, :has_no_selector?, :click_on, :has_no_checked_field?, :has_no_unchecked_field?, :query, :assert_selector, :assert_no_selector NODE_METHODS.each do |method| define_method method do |*args, &block| @touched = true current_node.send(method, *args, &block) end end https://github.com/jnicklas/capybara/blob/2.0.2/lib/capybara/session.rb#L338-L343 これらの メソッドは動的に生成されるようです。 def current_node scopes.last end def scopes @scopes ||= [document] end https://github.com/jnicklas/capybara/blob/2.0.2/lib/capybara/session.rb#L351-L358 current_node は document という変数に格納されたオブジェクトのようです。 def document @document ||= Capybara::Node::Document.new(self, driver) end https://github.com/jnicklas/capybara/blob/2.0.2/lib/capybara/session.rb#L334-L336 documentは Capybara::Node::Document クラスのインスタンスだとわかりました。 class Document < Base https://github.com/jnicklas/capybara/blob/2.0.2/lib/capybara/node/document.rb#L11 ここには all メソッドがなく Capbyra::Node::Base を継承しているようです。 include Capybara::Node::Finders https://github.com/jnicklas/capybara/blob/2.0.2/lib/capybara/node/base.rb#L27 この辺にありそうですね。 def click_link_or_button(locator) find(:link_or_button, locator).click end alias_method :click_on, :click_link_or_button https://github.com/jnicklas/capybara/blob/2.0.2/lib/capybara/node/actions.rb#L12-L15 cloick_on みつけました。このあたりを grep でみつけた時点で all :link_or_button でいけそうなのはわかります。 def all(*args) query = Capybara::Query.new(*args) elements = synchronize do base.find(query.xpath).map do |node| Capybara::Node::Element.new(session, node, self, query) end end Capybara::Result.new(elements, query) end https://github.com/jnicklas/capybara/blob/2.0.2/lib/capybara/node/finders.rb#L110-L118 all みつけたー!

no_picture

Capybaraでtitleタグの内容が取得できなくなってしまった。

Capybaraを2.0にしたら動かなくなった Cucumber の step がありました。titleタグ のtextをとる部分。visible でない要素のtextは取得できなくなったんでしょうか。 コードを追う余裕がなかったので、Nokogiriで対処した。 target = find("title").text expect(target).to eq(title) を target = Nokogiri::HTML.parse(page.source).css("title").text expect(target).to eq(title) に書き換えました。 ちょっと無理矢理。 Cucumberについてやりとりする仲間がいないので、titleタグのテキストの中身なんて確認しなくていいよ!とか、そういうい話ができないのが寂しいですね。

no_picture

cucumber で表示した画面がXMLを出力しているか確認する

rspec でマッチャーがあればよいのですが、とりあえず心当たりがなかったので、適当にごまかしました。良いgemがあれば紹介して欲しいです。 page.source が サーバからの出力を返してくださるので、これを Nokogiri で parse させてエラーがないかどうかで確認しました。 ならば /XMLを出力する/ do errros = Nokogiri::XML(page.source).errors expect(errors).to be_empty end どうせなら下記のように書きたいですね。 ならば /XMLを出力する/ do should render_xml end マッチャーを書いてみます。 RSpec::Matchers.define :render_xml do match do |actual| Nokogiri::XML(actual.source).errors.empty? end end ほとんどそのままです。matcher つくるのは難しくないので気軽に作りたいです。 少しだけ解説。 y cucumberの中では subject を省略した場合は page になります。なので、 ならば /XMLを出力する/ do page.should render_xml end と書いたのと等しいです。なので、acutual には page オブジェクトがバインドされていますので、そこから source を取り出してチェックします。page オブジェクトには html というメソッドが存在しますが、ブラウザが解釈したあとのDOMをdumpしたような感じになってるので期待通りの動きをしませんでした。