昨日に引き続き、書籍「ソフトを他人に作らせる日本、自分で作る米国」より、気になった部分に関して。第3章『「言葉のインフレ」は恐ろしい』の中で、キーワードとして「Web2.0」「クラウド」「ビッグデータ」が出てきます。ITの世界に少しでも関わりを持つ人なら必ず聴いたことのある言葉だと思いますが、どれも明確な定義のないまま言葉だけが独り歩きしているという点では共通しています。
僕がIT業界に入った時には「Web2.0」は既に死語になりつつあった気がしますが、「クラウド」は流行り始めた頃でした。当時からこの「クラウド」という言葉が氾濫し、なんでもかんでも「クラウド」とつけりゃいいと思ってんじゃないかと疑いたくなるほど節操なしに使われるのを、物凄い違和感とともに見てました。
昔からあったものと結局中身は同じだという本書の指摘は僕だけでなく多くのIT関係者(特に技術者・エンジニア)には共感できるものだと思います。正直、言葉は悪いですが、クラウドクラウドと浮かれている連中はバカなんじゃないかと呆れている、というのが本音のところでしょう。「クラウド」という言葉をグーグルの社員は軽蔑している、という話が出てくるのですが、全く驚くような話ではなく、「やっぱりそうか」と納得したというのが素直な印象です。
最近ではまた性懲りもなく「ビッグデータ」という新たな言葉が氾濫してますが、結構うんざりしてます。残念なことは、そういう曖昧な言葉を喧伝する連中が業界内部にいて、商売目的に使ってるってことです。もっと露骨な表現をするなら、ITの知識が無い顧客をうまいこと騙して、さも全く新しい製品であると見せかけてカネを搾り取りカモにする都合のいい道具として、「クラウド」とか「ビッグデータ」とかいう曖昧な言葉を使っている、と言っても過言ではないと思います。僕の目にはほとんど詐欺に見えます。
それでも一度広まってしまった言葉は使わざるを得ない、と本書でも述べられてますが、だったら最初に広めるのが悪いわけでしてね。結局IT業界が曖昧な言葉を使えば稼げるという経験に味をしめているように思えて仕方ありません。
もっと技術に対して、あるいは顧客に対して誠実であるべきだと思うのは僕だけではないんじゃないでしょうか。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年6月11日水曜日
2014年6月10日火曜日
内製化はエンジニアにとって良い流れだ
書籍「ソフトを他人に作らせる日本、自分で作る米国」を読みました。タイトルに惹かれて買った本ですが、面白かったです。読みながら思ったことは、やっぱり日本の場合、ソフトウェア開発に対しての評価が低いっていう、認めたくないが厳然たる事実があるってことなんですよね。
ソフトウェア開発を外注にホイホイ投げてしまうのだって、ソフトウェア開発がビジネスでクリティカルな役割を担っているならばありえない選択肢ですし。いや、もし本当にソフトウェア開発がクリティカル「ではない」領域ならば構わないです。しかしやはり、本来は内製すべきソフトウェア開発を安易に外注に投げている場合が多すぎる、と僕の感覚では思います。
プロジェクトのマネジメント手法やソフトウェア開発手法を、ただ輸入して真似してみたところで上手くいかないのも、そもそも内製を前提としている手法なのに、外注に無理やり当てはめているから、という側面もあるでしょう。アジャイルなんて特にそうであって、あれはどう見ても内製を前提としてるようにしか思えないです。
とまあ、なんかネガティブな感じに書いてしまいましたが、近年の内製化の流れは、このへんのソフトウェア開発軽視の価値観が改善されてきたことの1つの表れかもしれないと思ったりします。
ちなみに僕はソフトウェア開発の経験として内製と外注の両方の経験がありますけれども、明らかに内製の方がいい環境だと思いました。内製と外注では、工数・スケジュール・要件・技術仕様・そしてエンジニアへの報酬に至るまで、考え方が全然違ってしまいます。例えば外注を請け負う立場の場合、考え方は「受注・納品」であり、この場合エンジニアへの報酬は単なるコストでしかありません。一方内製の場合には考え方は「投資・回収」です。するとエンジニアへの報酬は投資の一部となります。この辺の考え方の違いについては別の機会にまた書きたいですね。
ともあれ、実感としてはソフトウェア開発の内製はエンジニアにとっても良い環境を生みやすいので、近年の内製化の流れはいい流れだと思っております。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
ソフトウェア開発を外注にホイホイ投げてしまうのだって、ソフトウェア開発がビジネスでクリティカルな役割を担っているならばありえない選択肢ですし。いや、もし本当にソフトウェア開発がクリティカル「ではない」領域ならば構わないです。しかしやはり、本来は内製すべきソフトウェア開発を安易に外注に投げている場合が多すぎる、と僕の感覚では思います。
プロジェクトのマネジメント手法やソフトウェア開発手法を、ただ輸入して真似してみたところで上手くいかないのも、そもそも内製を前提としている手法なのに、外注に無理やり当てはめているから、という側面もあるでしょう。アジャイルなんて特にそうであって、あれはどう見ても内製を前提としてるようにしか思えないです。
とまあ、なんかネガティブな感じに書いてしまいましたが、近年の内製化の流れは、このへんのソフトウェア開発軽視の価値観が改善されてきたことの1つの表れかもしれないと思ったりします。
ちなみに僕はソフトウェア開発の経験として内製と外注の両方の経験がありますけれども、明らかに内製の方がいい環境だと思いました。内製と外注では、工数・スケジュール・要件・技術仕様・そしてエンジニアへの報酬に至るまで、考え方が全然違ってしまいます。例えば外注を請け負う立場の場合、考え方は「受注・納品」であり、この場合エンジニアへの報酬は単なるコストでしかありません。一方内製の場合には考え方は「投資・回収」です。するとエンジニアへの報酬は投資の一部となります。この辺の考え方の違いについては別の機会にまた書きたいですね。
ともあれ、実感としてはソフトウェア開発の内製はエンジニアにとっても良い環境を生みやすいので、近年の内製化の流れはいい流れだと思っております。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年6月7日土曜日
SCSSが便利すぎる
WebアプリのUIを組んでいて、今回初めてSCSSを使ってみているわけですが、便利さにびっくりしてます。以前からその便利さはネット上で色々語られているので知識としては知っていましたが、やっぱり自分で実際に使うと実感できますね。
今回SCSSを書くこと自体初めてでしたが、ものの30分くらいで全く問題なく作業を進められるようになりました。記法としてはSASS記法ではなくSCSS記法です。ネットを見ているとHAMLを採用したSASS記法の方が記述量が少なくすっきりしていて、そちらが好みの人もいるらしいですが、僕はCSSの書き方が流用できるSCSS記法を支持します。
なお、LESSという選択肢もありますが、Rubyを採用したプロジェクトなのでSCSSと相性が良さそうという理由でSCSSを選択しました。Padrinoフレームワークは、.scssファイルを自動的に.cssファイルに変換してくれます。
うーん、一度実際に体験してしまうと、もはや生のCSSなんて書きたくない(笑)
ちなみにSCSSの書き方詳細についてはネット上に良質な解説サイトが沢山あるのでそちらをご参照ください。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
今回SCSSを書くこと自体初めてでしたが、ものの30分くらいで全く問題なく作業を進められるようになりました。記法としてはSASS記法ではなくSCSS記法です。ネットを見ているとHAMLを採用したSASS記法の方が記述量が少なくすっきりしていて、そちらが好みの人もいるらしいですが、僕はCSSの書き方が流用できるSCSS記法を支持します。
なお、LESSという選択肢もありますが、Rubyを採用したプロジェクトなのでSCSSと相性が良さそうという理由でSCSSを選択しました。Padrinoフレームワークは、.scssファイルを自動的に.cssファイルに変換してくれます。
うーん、一度実際に体験してしまうと、もはや生のCSSなんて書きたくない(笑)
ちなみにSCSSの書き方詳細についてはネット上に良質な解説サイトが沢山あるのでそちらをご参照ください。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年6月6日金曜日
Capybaraで入力欄・ボタン・リンクをCSSセレクタによって指定する
CapybaraはWebアプリケーションをテストする際に便利なライブラリです。ところで、フォームに入力するメソッドとしてはfill_inがありますが、要素の指定にCSSセレクタが使えません。click_button, click_linkも同様にCSSセレクタが使えません。ではXPathは使えるかというと、XPathも使えません。指定できるのは単なる文字列で、例えばfill_inの場合、その文字列がid属性またはname属性または対応するlabel要素の文字列に合致するものを探すことになっています。他のfindなどの要素指定メソッドではCSSセレクタを使うので、他のメソッドとの統一性がなくて分かりにくいし、細かい指定もできないので、CSSセレクタを使いたいところです。
単純な解決策としては、findメソッドで要素を絞り込んでsetやclickすればいいわけですが、それだけだともとの機能と同一にはになりません。というのは、fill_in, click_button, click_linkの特長として、それぞれのタイプに合致する要素だけを探してくれるのです。例えばfill_inならば入力可能フィールドのみを検索対象としてくれます。
これは、Capybaraのこのソースコードを見るとよくわかります。
例えばfill_inの場合、"fillable_field"という条件で探してsetしています。これと同じことをできればいいわけです。条件の詳細はXPathライブラリのこのソースコードを見るとわかります。
これによるとfill_inに使われる"fillable_field"の条件は、
click_buttonに使われる"button"の条件は、
click_linkに使われる"link"の条件は、
となっています。これらの条件を満たすように、CSSセレクタに条件を付加してXPathに変換するメソッドを作ってみました。
少々解説します。Nokogiri::CSS.xpath_for は、複数条件CSSに対応するため、返り値はXPath文字列を要素に持つ配列になります。入力欄を選ぶという状況的に、1つのみになると考えられるので最初の要素を選択すればよいから、Nokogiri::CSS.xpath_for(css)[0]としています。
変換したXPathの後ろに例えば [../input or ../textarea] といった要素指定を付加しています。これは、要素自身がinput要素あるいはtextarea要素であることを後から無理やり指定するためです。スマートではない形式ですが、CSSセレクタを変換して条件を付加するという状況なので仕方ありません。
最初はメソッド中のコメントアウトしてある部分のようにかなり動的にXPathを生成していましたが、テスト実行を少しでも早くするために、文字列をベタ書きしておくことにしました。
さて、使い方としてはCSSセレクタを渡してXPathに変換し、その条件でノードを取得して動作を行えばよいです。Cucumberによる例を挙げると、こんな感じでステップ定義に使えます。何やら余計なものが色々付いているように見えますが、その辺の詳細は以前に書いたこの記事を参照。
Gerkinによるfeature定義としては、例えば
のようになります。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
単純な解決策としては、findメソッドで要素を絞り込んでsetやclickすればいいわけですが、それだけだともとの機能と同一にはになりません。というのは、fill_in, click_button, click_linkの特長として、それぞれのタイプに合致する要素だけを探してくれるのです。例えばfill_inならば入力可能フィールドのみを検索対象としてくれます。
これは、Capybaraのこのソースコードを見るとよくわかります。
例えばfill_inの場合、"fillable_field"という条件で探してsetしています。これと同じことをできればいいわけです。条件の詳細はXPathライブラリのこのソースコードを見るとわかります。
これによるとfill_inに使われる"fillable_field"の条件は、
- input要素あるいはtextarea要素であり、
- type属性が次のいずれでも「ない」:'submit', 'image', 'radio', 'checkbox', 'hidden', ‘file'
click_buttonに使われる"button"の条件は、
- input要素かつ、type属性が次のいずれかである:'submit', 'reset', 'image', ‘button'
- または、button要素である
click_linkに使われる"link"の条件は、
- a要素かつ、href属性が存在する
となっています。これらの条件を満たすように、CSSセレクタに条件を付加してXPathに変換するメソッドを作ってみました。
# CSSセレクタをXPathに変換し、さらに入力可能フィールドの条件を付加する
# @param [String] css CSSセレクタ
# @return [String] 変換後のXPath
def convert_css_to_fillable_xpath(css)
base_xpath = %(#{Nokogiri::CSS.xpath_for(css)[0]})
# init_xpath = %(#{base_xpath}[../input or ../textarea])
# new_xpath = ['submit', 'image', 'radio', 'checkbox', 'hidden', 'file'].inject(init_xpath) do |tmp_xpath, attr|
# %(#{tmp_xpath}[not(@type='#{attr}')])
# end
# new_xpath
# 高速化のためベタ書き
"#{base_xpath}[../input or ../textarea][not(@type='submit')][not(@type='image')][not(@type='radio')][not(@type='checkbox')][not(@type='hidden')][not(@type='file')]"
end
# CSSセレクタをXPathに変換し、さらにボタンの条件を付加する
# @param [String] css CSSセレクタ
# @return [String] 変換後のXPath
def convert_css_to_button_xpath(css)
base_xpath = %(#{Nokogiri::CSS.xpath_for(css)[0]})
# xpath_input = %(#{base_xpath}[../input][@type='#{['submit', 'reset', 'image', 'button'].join("' or @type='")}'])
# xpath_button = %(#{base_xpath}[../button])
# "(#{xpath_input} | #{xpath_button})”
# 高速化のためベタ書き
"(#{base_xpath}[../input][@type='submit' or @type='reset' or @type='image' or @type='button'] | #{base_xpath}[../button])"
end
# CSSセレクタをXPathに変換し、さらにリンクの条件を付加する
# @param [String] css CSSセレクタ
# @return [String] 変換後のXPath
def convert_css_to_link_xpath(css)
base_xpath = %(#{Nokogiri::CSS.xpath_for(css)[0]})
"#{base_xpath}[../a][@href]"
end
少々解説します。Nokogiri::CSS.xpath_for は、複数条件CSSに対応するため、返り値はXPath文字列を要素に持つ配列になります。入力欄を選ぶという状況的に、1つのみになると考えられるので最初の要素を選択すればよいから、Nokogiri::CSS.xpath_for(css)[0]としています。
変換したXPathの後ろに例えば [../input or ../textarea] といった要素指定を付加しています。これは、要素自身がinput要素あるいはtextarea要素であることを後から無理やり指定するためです。スマートではない形式ですが、CSSセレクタを変換して条件を付加するという状況なので仕方ありません。
最初はメソッド中のコメントアウトしてある部分のようにかなり動的にXPathを生成していましたが、テスト実行を少しでも早くするために、文字列をベタ書きしておくことにしました。
さて、使い方としてはCSSセレクタを渡してXPathに変換し、その条件でノードを取得して動作を行えばよいです。Cucumberによる例を挙げると、こんな感じでステップ定義に使えます。何やら余計なものが色々付いているように見えますが、その辺の詳細は以前に書いたこの記事を参照。
もし(/^".*?\((.*?)\)" 欄に "(.*?)" と入力(?:し(、)|した)(\(非同期\))?( debug)?$/) do |t_css, t_with, t_eos, t_async, debug| show_debug_info if debug xpath = convert_css_to_fillable_xpath(t_css) page.find(:xpath, xpath, :visible => true, :wait => t_async ? async_time : 0).set(t_with) puts '' if t_eos.blank? end もし(/^".*?\((.*?)\)" ボタンを押下(?:し(、)|した)(\(非同期\))?( debug)?$/) do |t_css, t_eos, t_async, debug| show_debug_info if debug xpath = convert_css_to_button_xpath(t_css) page.find(:xpath, xpath, :visible => true, :wait => t_async ? async_time : 0).click puts '' if t_eos.blank? end もし(/^".*?\((.*?)\)" リンクを押下(?:し(、)|した)(\(非同期\))?( debug)?$/) do |t_css, t_eos, t_async, debug| show_debug_info if debug xpath = convert_css_to_link_xpath(t_css) page.find(:xpath, xpath, :visible => true, :wait => t_async ? async_time : 0).click puts '' if t_eos.blank? end
Gerkinによるfeature定義としては、例えば
もし "アカウント名(#account_name)" 欄に “foo" と入力し、 かつ "パスワード(#password)" 欄に “bar" と入力し、 かつ "ログイン(#login-button)" ボタンを押下した ならば・・・(以下略)
のようになります。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年6月5日木曜日
Sinatra・PadrinoでセッションIDを変更する
Webアプリケーションでは例えばログインやログアウトしたタイミングなど、セッションハイジャックのリスクを下げるためにセッションIDを変更したほうがよい場面があります。
PHPですと、言語に組み込みでsession_regenerate_idという関数が用意されています。さすがWebに特化した言語ですね。
で、Rubyでは言語そのものにセッションIDを変更する方法はありません。各フレームワーク依存ですね。ということで、軽量フレームワークとしてよく使われるSinatraやその派生のPadrinoにおいてセッションIDを変更する方法を調べてみました。
ます、セッション内容を破棄して構わないのであれば、
と単にdestoryメソッドを呼んでセッションを破棄するだけです。自動的に新しいセッションIDでセッションが再生成されます。
セッション内容を引き継ぎたい場合は、事前にセッション内容を取得しておき、
新しいセッションの内容としてまるごと置き換えます。
Padrinoの場合、以下の様な感じでヘルパーメソッドにしておくとよいでしょう。メソッド名はphpのマネです。ヘルパーを定義している部分に適当に追加すればいいと思います。
上記 session_regenerate_id を呼べば、セッションIDが変更されてセッション内容が引き継がれます。
※2014/06/12追記
RackミドルウェアでCSRF対策をしている場合には、セッションIDの変更とともに、セッション内に保持しているCSRFトークンも変更したいと思うでしょう。そういう時は、引き継ぐセッション情報からCSRFトークンの情報を削っておくと、自動的にCSRFトークンを再生成してくれます。ということで修正したコードを載せておきます。
上記の例はCSRFトークンをセッションに格納する際のキーが"csrf"である場合です。このキーは状況に応じて変更してください。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
PHPですと、言語に組み込みでsession_regenerate_idという関数が用意されています。さすがWebに特化した言語ですね。
で、Rubyでは言語そのものにセッションIDを変更する方法はありません。各フレームワーク依存ですね。ということで、軽量フレームワークとしてよく使われるSinatraやその派生のPadrinoにおいてセッションIDを変更する方法を調べてみました。
ます、セッション内容を破棄して構わないのであれば、
session.destroy
と単にdestoryメソッドを呼んでセッションを破棄するだけです。自動的に新しいセッションIDでセッションが再生成されます。
セッション内容を引き継ぎたい場合は、事前にセッション内容を取得しておき、
新しいセッションの内容としてまるごと置き換えます。
old_session_hash = session.to_hash session.destroy session.replace(old_session_hash)
Padrinoの場合、以下の様な感じでヘルパーメソッドにしておくとよいでしょう。メソッド名はphpのマネです。ヘルパーを定義している部分に適当に追加すればいいと思います。
# セッションIDを再生成する # @return [void] def session_regenerate_id old_session_hash = session.to_hash session.destroy session.replace(old_session_hash) end
上記 session_regenerate_id を呼べば、セッションIDが変更されてセッション内容が引き継がれます。
※2014/06/12追記
RackミドルウェアでCSRF対策をしている場合には、セッションIDの変更とともに、セッション内に保持しているCSRFトークンも変更したいと思うでしょう。そういう時は、引き継ぐセッション情報からCSRFトークンの情報を削っておくと、自動的にCSRFトークンを再生成してくれます。ということで修正したコードを載せておきます。
# セッションIDを再生成する
# @return [void]
def session_regenerate_id
old_session_hash = session.to_hash
old_session_hash.delete('csrf')
session.destroy
session.replace(old_session_hash)
end
上記の例はCSRFトークンをセッションに格納する際のキーが"csrf"である場合です。このキーは状況に応じて変更してください。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年6月4日水曜日
Capybaraでテスト実行中にセッションIDを取得する
テストにCapybaraを使ってるんですが、セッションIDが変化するかどうかをテストしたいと思いまして。ところが、セッションの中身を見る場合は rack_session_access っていうライブラリが使えるんですが、rack_session_accessではセッションID自体を取得する方法は提供してくれません。
Rack::Session::Cookieを使っている場合にはセッションの中身にセッションIDが入っているので、rack_session_access でセッションIDを取得できますが、Rack::Session::Cookie以外によるセッション機能を使っている場合はrack_session_accessではセッションIDが取得できません。
何とかできないものかと思案し、発想を転換しました。Rack::Session::Cookie以外によるセッション機能を使っている場合、ブラウザのCookieにはセッションIDそのものが入っているわけです。つまり、Cookie経由でセッションIDが取得できるはずです。
ということで調べると、Capybaraのドライバを呼び出して、ドライバ内部のCookieを取得するメソッドを呼び出せばいいみたいです。なお、ドライバ内部のCookieを取得するメソッドの呼び出し方法は、ドライバごとに異なります。次の例は、ドライバがPoltergeist の場合の例です。
などとすれば取得できます。
なお、セッションIDが変化するかどうかをテストしたい場合はセッションIDを保存しておく必要がありますが、単純にインスタンス変数に入れておけば、後のステップでそのままインスタンス変数として呼び出すことができます。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
Rack::Session::Cookieを使っている場合にはセッションの中身にセッションIDが入っているので、rack_session_access でセッションIDを取得できますが、Rack::Session::Cookie以外によるセッション機能を使っている場合はrack_session_accessではセッションIDが取得できません。
何とかできないものかと思案し、発想を転換しました。Rack::Session::Cookie以外によるセッション機能を使っている場合、ブラウザのCookieにはセッションIDそのものが入っているわけです。つまり、Cookie経由でセッションIDが取得できるはずです。
ということで調べると、Capybaraのドライバを呼び出して、ドライバ内部のCookieを取得するメソッドを呼び出せばいいみたいです。なお、ドライバ内部のCookieを取得するメソッドの呼び出し方法は、ドライバごとに異なります。次の例は、ドライバがPoltergeist の場合の例です。
@session_id = page.driver.browser.cookies['rack.session'].value
などとすれば取得できます。
なお、セッションIDが変化するかどうかをテストしたい場合はセッションIDを保存しておく必要がありますが、単純にインスタンス変数に入れておけば、後のステップでそのままインスタンス変数として呼び出すことができます。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
2014年6月3日火曜日
RSpecで外部コマンド実行結果を判定する
RSpecによる自動テスト中に、外部コマンドを実行して成功したかどうかを判定したいとします。そういう時は、実行ステータスを取得でき、なおかつ外部コマンドの終了を待ち合わせるような外部コマンドの実行方法を採用すればいいでしょう。
Rubyには外部コマンドの実行方法がいくつか存在しますが、Open3.capture2あたりが適しているように思います。コマンドの標準出力への出力結果はテストで使ったりするため取得したいことが多く、なおかつ標準エラー出力はそのままコンソール上に表示することでテスト中に外部コマンドでエラーが起きた際に原因が分かりやすくなります。
コマンドの実行が成功したかどうかを終了ステータスで判定すれば、エラーが起きた際に分かりやすくなるでしょう。コード例を挙げておきます。
上記コードでは終了ステータスが0、つまりコマンドが成功することを期待しています。コマンドが異常終了して終了ステータスに0以外が返ると、テストは失敗となります。
なお、僕の場合実際には上記コードをRSpecから直接実行するのではなく、Cucumberのステップ定義に組み込んで使っています。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
Rubyには外部コマンドの実行方法がいくつか存在しますが、Open3.capture2あたりが適しているように思います。コマンドの標準出力への出力結果はテストで使ったりするため取得したいことが多く、なおかつ標準エラー出力はそのままコンソール上に表示することでテスト中に外部コマンドでエラーが起きた際に原因が分かりやすくなります。
コマンドの実行が成功したかどうかを終了ステータスで判定すれば、エラーが起きた際に分かりやすくなるでしょう。コード例を挙げておきます。
stdout, status = Open3.capture2 %(some_command), :stdin_data => stdin_string expect(status.exitstatus).to eq(0)
上記コードでは終了ステータスが0、つまりコマンドが成功することを期待しています。コマンドが異常終了して終了ステータスに0以外が返ると、テストは失敗となります。
なお、僕の場合実際には上記コードをRSpecから直接実行するのではなく、Cucumberのステップ定義に組み込んで使っています。
※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうぞ。
登録:
投稿 (Atom)