最近のテスト事情

むかしに比べると、かなりテストが書けるようになってきたし、TDDもだんだん慣れてきた。最近テスト書いてて便利だと思ったことについてメモっておく。

スタブを使ってbefore_filterをスキップする

describe 'GET index' do
  context 'ログインしている場合' do
    before(:each) do
      controller.any_instances.stub(:authenticate_user).and_return(true)
    end

    it 'hogehogeなfugafugaを取得する' do
      get index, params
      assigns[:fugafuga].should be_hogehoge
    end
  end
end

ログイン判定のような、リクエストをはじく処理をbefore_filterで実装することがよくあるけど、そういうコントローラーをテストする場合、スタブが便利だということにようやく気づいた。スタブによって、メソッドの中身をごまかして好きな値を返すようにできる。だから、before_filterをスキップしたい場合は、とにかくスタブしてtrueを返すようにしとけばいい。skip_before_filterでもスキップすることはできるけど、僕はスタブを使う方が好み。

FactoryGirlを使いこなす

FactoryGirl.define do
  factory :user do
    # 連番を使えばuniquenessのバリデーションにかからなくなる
    sequence(:name)  {|n| "user #{n}" }
    sequence(:email) {|n| "user#{n}@example.com" }
    age { rand(18..30) }

    after(:build) do |user|
      # 余計なデータを作るコールバックがあればスキップできる
      User.skip_callback(:after, :create, :create_data)
    end

    # ネストしたfactoryで上書きできる
    factory :naoty do
      name 'naoty'
      email 'naoty@example.com'
      age 18
    end

    # traitで属性のグループに名前をつけられる
    trait :resigned do
      resigned_at { Time.now }
    end
  end
end

user = FactoryGirl.create(:user)
p user.name #=> "user 1"

naoty = FactoryGirl.create(:naoty)
p naoty.name #=> "naoty"

resigned_user = FactoryGirl.create(:user, :resigned)
p resigned_user.name #=> "user 2"
p resigned_user.resigned_at #=> "2013-01-19 00:43:59 +0900"

factory_girlはテスト用のデータを簡単につくるためのgem。似たようなgemは他にもあるけど、こういうgemを使うと、テストデータを作るロジックとテストコードを分離できる。なので、いろんなテストで使われるテストデータを重複なく簡単に作ることができる。

FactoryGirlでテストデータを作成するときに、よくひっかかるのがバリデーションやafter_saveなどのコールバック内の余計な処理だと思う。こういう鬱陶しい処理は、FactoryGirlのコールバックを使ってスキップしてる。

traitは特殊なデータを作る場合にすごく役に立つ。上記の例のような「退会ユーザー」をテストに使いたいときなど、特殊なデータの属性をひとまとめにしてFactoryGirl.create(:user, :resigned)のように簡単に作成できる。

changeマッチャが便利

describe '#resign' do
  let(:user) { FactoryGirl.create(:user) }

  it 'resigned_atを更新する' do
    lambda {
      user.resign
    }.should change(user, :resigned?).from(false).to(true)
  end
end

モデルの更新系のメソッドをテストするとき、changeマッチャが非常に便利。上の例で言うと、user.resigned?の結果がlambda内の処理を実行した前後でfalseからtrueに変わることをテストしている。

東京Ruby会議10にいってきた

1/13, 1/14に千葉で行われた"東京"Ruby会議10にいってきた。

受付でまず渡されたのが、砂浜でMacを開く謎のバッヂ×4(後にささたつバッヂであると知らされる)。幸運にも、受付から3時間程度でバッヂをコンプし、レアバッヂをゲットできた。最初はよくわからなかったけど、楽しかった。スタッフの皆さんが楽しんでるのが伝わってきてよかった。

内容はというと、個人的にはmrubyの話がよかった。ルーターのこととかはよくわからなかったけど、組み込み機器の世界はまだまだレガシーらしく、mrubyにかかる期待は大きいみたい。"Internet of Things"的なことに興味があるので、CとかC++とかやる必要あるのかなと思ってたけど、mrubyでできるならmruby身につけたい。mrubyでつくられたスマートなルーターは実用性がありそうなデバイスでよかった。僕もmruby覚えて、一発ネタじゃない実用的なデバイスつくってみたい。mruby興味あるけど、どこから始めていいかよくわかってないので、なんとかしたい。

あとは、これまでTwitterでのみ知ってた方と初顔合わせできたのがよかった。#p4dをはじめ、コミュニティの楽しさも再確認できた。ひきつづき何かしらのコミュニティに参加したい。

2日目は大雪で途中で中断してしまったけど、スタッフの皆さんの英断のおかげで何事もなく帰宅できました。有難うございました。

Limechatのテーマつくった

昨年末から社内にIRCが導入されてから、クライアントツールにLimechatを使ってる。とりあえずデフォルトのLimelightというテーマを使ってたけど、ビミョーに違和感があったので、自分でテーマを作ってみた。

f:id:naoty_k:20130113000307p:plain

https://github.com/naoty/Nakameguro

1時間くらいであっさりできてしまったので、そのコツをメモしておく。

cssyamlを使う

Limechatのテーマはcssyamlの2つで定義していく。cssはチャットのログが表示される部分(上下)のスタイルを定義するのに使い、yamlはユーザー一覧・サーバー一覧・入力部分のスタイルを定義するのに使う。これらを~/Library/Application Support/Limechat/Themes/以下におけば、LimechatのPreferencesからテーマを設定できるようになる。

サンプルを参考にする

~/Library/Application Support/Limechat/Themes/Sample.cssSample.yamlがある。これを参考に、どのクラスがどの部分にあたるのかだいたい把握できる。例えば、時間のテキストは.timeというクラスがついてるし、.sender[type=myself]というクラスは自分のニックネームにあたる。

sassが便利

今回はcssは直接書かずにsassで書いてみた。色を多用するから、それぞれの色に変数名をつけたかった。

$ gem install sass
$ sass --watch Nakameguro.sass:Nakameguro.css

とすると、Nakameguro.sassを保存するたびにcssに変換されるため、すぐに変更を確認できる。便利。

小言

毎日使うツールは自由にカスタマイズできた方がいい。自分に合うようにカスタマイズすることで快適に開発できる。今はGUILimechatを使ってるけど、より柔軟にカスタマイズしたくなったらWeechatに移行するかも。

IntelliJ IDEAをvimっぽくする

Androidアプリ開発しててEclipseが重くてつらかったので、IntelliJ IDEAなるものを試してる。見た目がかっこいいからこっち使ってみようと思い、vimっぽく使えるか試してみた。

バージョン

テーマを黒にする

  • こっちの方がイケてる。
  • Preferences > Appearance > ThemeをDarculaに変更する。

f:id:naoty_k:20130104233050p:plain

IdeaVimを追加

  • キーマップをvim化する。
  • Preferences > Plugins > Browse repositories > IdeaVimでダブルクリックするとインストールできる。

f:id:naoty_k:20130104233555p:plain

カーソルがどこまでも右にいける設定をオフにする

  • Preferences > Editor > Allow placement of caret after end of lineをオフにする。

f:id:naoty_k:20130104233620p:plain

行番号を表示する

  • Preferences > Editor > Appearance > Show line numbersをオンにする。

f:id:naoty_k:20130104233646p:plain

キーマッピングをカスタマイズする

  • Preferences > Keymapからいろいろ変更できる。
  • Second Strokeを指定することでPrefixみたいなキーマッピングも設定できる。
  • 変更したのは以下のとおり。これでだいたいvimと同じ動きになる。
  • Run:実行
  • Select Next Tab, Select Previous Tab:タブを前後に移動
  • Recent Files:最近開いたファイルのファイラーを起動(unite.vimっぽく使える)
  • Split Horizontally, Split Vertically:エディタを水平分割、垂直分割
  • Goto Next Splitter, Goto Previous Splitter:分割したエディタを前後に移動
  • Close:エディタを閉じる

f:id:naoty_k:20130104233706p:plain

Eclipseだとタブの移動とか画面分割をショートカットからできなかった気がするので、これだけでもインテリJ氏に替える価値があると思う。作業効率がだいぶ上がる。

2012年の振り返り・2013年の目標

KDDIの障害が復旧するのを見守るハメになったので、それまで2012年の振り返りをしようと思う。

2012年の技術面での目標は「iPhoneアプリをリリースすること」だったような気がする。これは無事に達成できた。おまけにAndroidアプリもリリースできた。あと、いくつかRailsでサービスをつくった。特にcui-about.meはなかなか好評で、海外の方にも使っていただいた。使ってもらえるサービスを作ったということも大きい成果だったと思う。覚え始めてもう2年くらいになるRailsでは、特にRSpecを書けるようになったことが一番の進歩だったと思う。まあ大半がAndroidかiOSを書いていたので、そこまで大きい進歩はなかったんだけど。最近はnode.jsをちょっとやり始めて、だんだんとわかってきたところ。Arduinoも買ってみて、ハードウェアに強い興味がでてきた。

総括すると、未知の領域にどんどんチャレンジできた年だった。

2013年の目標は「ハードウェアを開発すること」にしようと思ってる。ハードウェアといってもいろいろあるけど、とりあえず製品として売れるものを開発したい。いま一番興味があるのはハードウェアで、特にモノとモノとがインターネットにつながった未来に興味がある。Webサービスやアプリの開発自体もすごく楽しいんだけど、これまでの路線から想像がついてしまう未来ではなく、一歩先の未来を実現できるエンジニアになりたいと思う。そのために、これまでとはまったく異なる領域にチャレンジしたい気持ちが強い。2013年は2012年よりもっと大きくステップアップしたい。

HTMLも分からないまったくのド素人だった2年半前からここまで来れたという事実は今ではすごく自信になっていて、新しくチャレンジすることにポジティブになれる。今年だって、iPhoneアプリだけじゃなくAndroidアプリも作ることができて、さらに自信につながった。だから、来年もまた大きなチャレンジをしたいと思う。毎年そうやってチャレンジを繰り返して成功体験を重ねていけたら幸せだなぁと、年の瀬にしみじみ思いました。

今年お世話になった皆様、ありがとうございました。来年も宜しくお願い致します。よいお年をお迎えください。

Herokuでhubotを使ったIRC botを動かす

最近、会社でIRCブームが来てるので、僕もhubotを使ってなにかbotを作ってみることにした。hubotはGithubが作ったbotフレームワークで、TwitterbotとかIRCbotを簡単に作ることができる。

で、できたのがこれ。

f:id:naoty_k:20121230030802p:plain

https://github.com/naoty/diobot

ディオ(DIO)様の名言をランダムに返してくれる、最高に「ハイ!」なbotが出来上がりました。

Herokuでhubotをデプロイするときにhubotのwiki*1を参考にしてみたんだけど、Herokuにpushすると、下のようなエラーが出てぜんぜんうまくいかなかった。

Starting process with command `bin/hubot -a irc -n Hubot`
Stopping all processes with SIGTERM
bin/hubot: 3: npm: not found
Process exited with status 0

結局、このwikiは参考にせずに自力でなんとかしたので、その記録をちゃんとメモに残しておこうと思う。

package.json

$ mkdir diobot
$ cd diobot
$ vi package.json
{
  "name": "diobot",
  "version": "0.0.1",
  "author": "naoty",
  "description": "DIO sama at irc",
  "license": "MIT",

  "dependencies": {
    "hubot": "*",
    "hubot-scripts": "*",
    "optparse": "*",
    "hubot-irc": "*",
    "coffee-script": "*",
    "underscore": "*"
  },

  "engines": {
    "node": "0.8.x",
    "npm": "1.1.x"
  }
}
$ npm install
  • dependenciesにあるパッケージは、先述のwikiに従ってたときに使ってたpackage.jsonに書いてあったのでそのまま使った。
  • あと、coffee-scriptはデプロイ時にエラーになったので追加した。
  • underscoreは便利なので、とりあえず追加した。

Procfile

$ vi Procfile
bot: hubot -a irc -n DIO
  • Herokuによると*2、Heroku側でbin:node_modules/.bin:/usr/local/bin:/usr/bin:/binにPATHを通してくれるので、Procfileではhubotとすればいい。
  • -a <Adapter名> -n <IRCのニックネーム>をオプションにつける。

Herokuにデプロイ

$ git push heroku master
...
$ heroku ps:scale bot=1
$ git config:add HUBOT_IRC_SERVER='irc.example.com' HUBOT_IRC_ROOMS='#hoge' HUBOT_IRC_NICK='DIO' HUBOT_IRC_PORT='6667' HUBOT_IRC_PASSWORD='hogehoge'
  • IRCサーバーやチャンネルへの接続のための情報を環境変数として渡す。
  • 必要な情報はこちら*3に載ってる。

スクリプトを追加

$ mkdir scripts
$ vi scripts/ping.coffee
module.exports = (robot) ->
  robot.respond /PING$/i, (msg) ->
    msg.send 'PONG'
  • scripts/以下にあるスクリプトは自動で読み込まれて有効になる。
  • これをデプロイしてdio pingって送ったらPONGとDIOが返したら成功。
  • あとは、工夫して面白いbotを作るだけ。

Enjoy!

node.js環境構築

websocketを使ったリアルタイムなアプリケーションを作りたくなったので、node.jsを始めようと思った。とりあえず、いろいろ必要なものをインストールしたので、それをメモに残しておく。

nodebrew

$ curl https://raw.github.com/hokaccha/nodebrew/master/nodebrew | perl - setup
$ vi .zprofile
export PATH=$HOME/.nodebrew/current/bin:$PATH
$ source .zprofile
  • node.jsはバージョンがどんどん更新されるようなので、Homebrewではなくパッケージマネージャーでインストールする。
  • 他にもnvmやnaveというものがあるらしいが、zshとの相性がよくないという話なので、nodebrewを選択した。

node.js & npm

$ nodebrew install stable
$ nodebrew use stable
$ node -v
v0.8.16
$ npm -v
1.1.69
  • nodebrewを使ってnode.jsとnpmをインストールする。

bower

$ npm install bower -g
  • bowerTwitter謹製のクライアントサイドのライブラリに特化したパッケージマネージャー。
  • jQuery, underscore.js, bootstrapなどのリソースをpackage.jsonと同じようにプロジェクト毎にインストールできるのが便利。
  • Railsだとjquery-railsなどgemのなかにそれらのリソースが含まれることもあるけど、node.jsはそういうのなさそうなので、重宝しそう。

例として、いま見てた本のサンプルプロジェクトで扱うリソースをbowerで管理してみる。

// component.json

{
  "name": "fileupload",
  "version": "0.0.1",
  "main": "./public/stylesheets/style.css",
  "dependencies": {
    "jquery": "*",
    "jquery-ui": "*",
    "jquery-file-upload": "*",
    "jquery-masonry": "*",
    "fancybox": "*"
  }
}

これでbower installとすればインストール完了。

NoSQL

$ brew install couchdb
$ mkdir -p ~/Library/LaunchAgents
$ cp /usr/local/Cellar/couchdb/1.2.0/Library/LaunchDaemons/org.apache.couchdb.plist ~/Library/LaunchAgents/
$ launchctl load -w ~/Library/LaunchAgents/org.apache.couchdb.plist
  • MongoDB, Redis, CouchDBなどのNoSQLはbrewでインストールするだけ。
  • インストール後に表示されるメッセージに従って、自動起動ファイルをロードしておくことも忘れずに。

追記(2012/12/22)

node.jsでbowerを使うには

$ tree -I node_modules
.
├── app.js
├── package.json
├── public
│   ├── component.json // <- ここに配置
│   ├── components     // <- ここにインストールされる
│   ├── images
│   ├── javascripts
│   └── stylesheets
│       └── style.css
├── routes
│   └── index.js
└── views
    ├── layout.jade
    └── index.jade
$ cd public
$ bower install

bower installでインストールされるライブラリは同じディレクトリ内のcomponentsというディレクトリに入る。なので、public内にcomponent.jsonを配置してbower installすればよさげ。

node-dev

$ npm install node-dev -g
$ node-dev app.js

node app.jsで起動すると、ファイルを変更するたびに再起動する必要がありめんどくさい。node-devを使うとその必要がなくなるので、とても便利。インストール必須だと思う。