RSpecでフィルターを使って実行するテストを絞り込む

RSpecネタです。

Guardを使ってテストをしていると、変更が起きたRSpecファイルのテストを全実行されるので、デグレを起こしてないかどうかを調べるという意味では安心なのですが、テストが長くなると実行時間が長くて辛い…ということがあると思います(というかありました)。

今書いているところだけテストしてほしいんよ!!と思って、The RSpec Book
を調べていたらありました。フィルター機能。

# coding: utf-8
require 'spec_helper'
# フィルター用の設定を記述
RSpec.configure do |c|
  c.filter = {focus: true}
end

describe User do
  shared_examples_for "ユーザーの各権限共通の動作" do
    its(:name) { should == "hogehoge" }
    its(:email) { should == "hogehoge@example.com" }
  end
  # 実行したいテストにfocus: trueを追記する
  context "管理者の場合", focus: true do
    subject { FactoryGirl.create(:user, type: "admin") }
    it_behaves_like "ユーザーの各権限共通の動作"
    its(:type) { should == "admin" }
  end
  # 略
end

これを書くと、focus: trueが書かれているテストのみ実行してくれます。
describe, context, it単位で書くことができるので、自由に粒度を調節できます。
終わったら、フィルターを削除するなりコメントアウトするなりしましょう。

また、逆バージョンの除外フィルターもあります。
このテストだけ実行しない、というパターンです。

RSpec.configure do |c|
  c.exclusion_filter = {slow: true}
end

上手に使い分けたいところです。


東京Ruby会議に参加してきました。

昨日、今日と、東京Ruby会議に参加してきました。
場所は千葉県なのですが(ぇ。しかも雪が降りまくっているのですが(ぇ。

雪の影響で交通機関のダイヤが乱れて、帰宅できなくなる人がでてきそうな勢いだったので、あえなく14時で中止ということになってしまったのが残念でしたが、なかなか面白かったです。

また、初めてアンチぼっちランチ、アンチぼっちディナーに参加したのですが、人見知りの自分としては、こういう機会はとても助かるなぁと思いました。やっぱり話すタイミングとか全然ないので(普段twitterでフォロー関係にあるとかそういうのがあればいいんだけど)、こういう機会を作ってもらえれば自然と交流できるしいいなぁと。

それにしても、発表内容がぼっち前提の話になっていたのが、うぅん?という感じでしたが、よく考えたら俺もぼっち開発してるわーとも思うし、でもなんかちょっと違和感があるというか。どちらかというと、勉強会やるには主催者がやりたいことをやり続けないとしんどいよーというスタンスの話のほうが共感もできてよかったなぁ〜と思いました。やっぱりみんな似たようなことを思ってるんすねぇ。

岡山のメンバーでメタプログラミングRuby読書会とThe RSpec Book読書会とかやっているけれど、これが面白く続けることができるのは、自分たちがやりたいことをやっているからと、やる気のあるメンバーでやっているからということだと思うので、妙な義務感が生じてしまうと楽しめないし、「それが僕には楽しかったから」でいいと思うんすよね。周りのレベルとか別に考えなくてもよくて(技術の普及を狙うような勉強会とかなら違うかもしれんけど)。

あとは黒Ruby会議がとても面白かったです。

さて、あとはBDD(Beer Drinking Development)するだけです。


gehirnでnginxをcronで起動させる(未検証)

今日(日付的には昨日だけど)、ゲヒルンのs11サーバが2回ダウンしてました。
自分が使っているサーバがまさにこれで、作りかけのAndroidアプリが通信に失敗して落ちてしまい、それでサーバが落ちてることに気づいたのですが、サーバが再起動したときにnginxを再起動するようにしないとなー…と思っていたところ、@MegaBlackLabelさんと@isidaiのやりとりが参考になったので、これを参考にcronの設定をしてみました。

参考情報は以下。

で、実際に設定した内容は以下の通り。crontab -eでcronの設定を書きました。
ちなみにnginxは事前にいれてあるものとします。参考はこちらの記事を。

@reboot /home/*****/nginx/sbin/nginx > /dev/null 2>&1

これで保存しておきました。正しい動作をするかどうかは、gehirnのサーバが再起動しない限りわからないので試せないのですが(ぇ、多分大丈夫じゃないかなと思います。でも未検証なので、自分でも不安ですね…。


slimでhoge.js.slimを書く方法

今やっている個人プロジェクトのテンプレートエンジンはslimを使っています。
erbに比べると完結に書くことができるし、閉じタグを書かなくていいので、htmlを書くのは非常に楽です。

で、ですよ。
昨今のWebアプリはAjaxは当たり前。なので、slimでhoge.js.slimというAjaxでのアクセスに対するレスポンスを書こうとしたら、どうすればいいのか?最初全然わかりませんでした。
ググってみたら、上手に書いている人がいたので、メモ書きとして残します。

| $(".content").append("#{escape_javascript(render "hoge")}"); // _hoge.html.slimをレンダリングしたものをエスケープ

こうです!
slimの場合、先頭に|を持ってくることで、テキストとしてそのまま書くことができます。
Rubyの部分は#{…}の中に書くことができるので、それで対応できるということでした。

しかし、このソースを見ていると個人的にはなんだか気持ち悪いです…。気持ち悪いというかダサいというか…。そこかしこにJavaScriptのコードが散るのはあまり好きでないですし、ましてやassets側のほうはCoffeeScriptで書いているので、ここでJavaScriptかよ…という感じが非常に違和感。

かといってjsonで受け取ってCoffeeScript側でjsonを分解してhtmlを作ってappendするのもダサい。
hoge.html.slimをレイアウトなしで返して、CoffeeScript側のコールバックでappendするのが見た目にもソース的にもよいのかなぁというイメージです。やっぱりhoge.js.slimは止めようかなぁ…。

追記:hoge.js.slimやめました。
やっぱりJavaScriptが散らかるので、止めることにしました。
コントローラー側では、

render layout: false

に設定して、CoffeeScriptのコールバック側でappendしました。
このほうが、CoffeeScript側でやっていることが明確でいいですね。

というわけで、hoge.js.slimはオススメしません。個人的に。


RSpecでshared_examples_forを使って共通動作をテストする

先日からguard + spork + rspecを使った自動テストを行うようになってノリノリでテストをしています。いちいち自分でテストを走らせないでいいので便利ですね!sporkのおかげでテストのロードも速くて快適です。

さて、現在仕事でもゴリゴリとテストを書いているのですが、同じような処理を毎回書くことがあります。例えば、ユーザーの権限が管理者・会員・ゲストなどがある場合、それぞれの権限での動作を検証しなければなりません。でも権限毎に同じ振る舞いのテストを書いていると冗長ですよねぇ…。
会社で@kazuhisa1976@ore_publicと一緒にThe RSpec Book読書会をやっているのだけれど、そこで学んだ検証方法で、共通処理をまとめることができることを知りました。それが、shared_examples_forとit_behaves_likeです。

使い方ですが、まずはユーザーの振る舞いの検証をやっていきましょう。
まずは全部書いたやつ。以下のテストが全て通るとします。
(factory_girlをfixture生成で使っています)

# coding: utf-8
require 'spec_helper'

describe User do
  context "管理者の場合" do
    subject { FactoryGirl.create(:user, type: "admin") }
    its(:name) { should == "hogehoge" }
    its(:email) { should == "hogehoge@example.com" }
    its(:type) { should == "admin" }
  end
  context "会員の場合" do
    subject { FactoryGirl.create(:user, type: "normal") }
    its(:name) { should == "hogehoge" }
    its(:email) { should == "hogehoge@example.com" }
    its(:type) { should == "normal" }
  end
  context "ゲストの場合" do
    subject { FactoryGirl.create(:user, type: "guest") }
    its(:name) { should == "hogehoge" }
    its(:email) { should == "hogehoge@example.com" }
    its(:type) { should == "guest" }
  end
end

はい、冗長ですねぇ〜。各権限毎にテストしていますが、type以外の検証は全て同じです。

これをshared_examples_forを使ってリファクタリングしましょう。
まず、shared_examples_forで共通の振る舞いを定義します。

shared_examples_for "ユーザーの各権限共通の動作" do
  its(:name) { should == "hogehoge" }
  its(:email) { should == "hogehoge@example.com" }
end

共通の検証項目をここに書き出すだけです。
そして、実際の各ユーザーの検証部分では、it_behaves_likeで呼び出します。

context "管理者の場合" do
  subject { FactoryGirl.create(:user, type: "admin") }
  it_behaves_like "ユーザーの各権限共通の動作"
  its(:type) { should == "admin" }
end

では、これで全部書き直してみます。

# coding: utf-8
require 'spec_helper'

describe User do
  shared_examples_for "ユーザーの各権限共通の動作" do
    its(:name) { should == "hogehoge" }
    its(:email) { should == "hogehoge@example.com" }
  end
  context "管理者の場合" do
    subject { FactoryGirl.create(:user, type: "admin") }
    it_behaves_like "ユーザーの各権限共通の動作"
    its(:type) { should == "admin" }
  end
  context "会員の場合" do
    subject { FactoryGirl.create(:user, type: "normal") }
    it_behaves_like "ユーザーの各権限共通の動作"
    its(:type) { should == "normal" }
  end
  context "ゲストの場合" do
    subject { FactoryGirl.create(:user, type: "guest") }
    it_behaves_like "ユーザーの各権限共通の動作"
    its(:type) { should == "guest" }
  end
end

権限毎の検証項目が多くても短く記述することが可能になりますね!
テストは割とコピペで量産していることが多かったのですが、これでまとめられることがわかったので、色んなところでリファクタリングできそうです。