牌語備忘録 -pygo

あくまでもメモです。なるべくオフィシャルの情報を参照してください。

牌語備忘録 -pygo

Rails 3 でやってみた --『スはスペックのス 【第 1 回】 RSpec の概要と、RSpec on Rails (モデル編)』

*1

元は Rails 2.0.2 と RSpec1.1.3 だったので、今現在最新の Rails 3.2.3 と RSpec 2.10.1 でやってみた
(ruby 1.9.3p194)

New Project

$ rails new myblog
:
$ cd myblog
$ ls
Gemfile		app/		doc/		script/
Gemfile.lock	config/		lib/		test/
README.rdoc	config.ru	log/		tmp/
Rakefile	db/		public/		vendor/

Gemfile

add

gem "rspec-rails", :group => [:development, :test]

bundle install

$bundle install
:
Using rspec-core (2.10.1) 
Using rspec-expectations (2.10.0) 
Using rspec-mocks (2.10.1) 
Using rspec (2.10.0) 
Using rspec-rails (2.10.1) 
:

rspec:install

$ rails generate rspec:install
      create  .rspec
      create  spec
      create  spec/spec_helper.rb

Make test model

$ rails generate model Blog name:string
      invoke  active_record
      create    db/migrate/20120526005010_create_blogs.rb
      create    app/models/blog.rb
      invoke    rspec
      create      spec/models/blog_spec.rb
$ rake db:migrate

Make fixtures

$ mkdir spec/fixtures
make file
  • spec/fixtures/blogs.yml
one:
  id: 1
  name: 今日の出来事その1
two:
  id: 2
  name: 今日の出来事その2

define the spec

-*- coding: utf-8 -*-
require 'spec_helper'

describe Blog, "#name が設定されていない場合:"  do
  # pending "add some examples to (or delete) #{__FILE__}"
  before(:each) do
    @blog = Blog.new
  end

  it "バリデーションに失敗すること" do
    @blog.should_not be_valid
  end
end

config: .rspec

--colour --format d
Option
  • --colour
    • 出力に色付ける(default)
  • --format
  • --profile
    • 時間のかかったテストを終了時に表示

run "rake spec SPEC=spec/models/blog_spec.rb"

when failure
$ rake spec SPEC=spec/models/blog_spec.rb
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec spec/models/blog_spec.rb

Blog#name が設定されていない場合:
  バリデーションに失敗すること (FAILED - 1)

Failures:

  1) Blog#name が設定されていない場合: バリデーションに失敗すること
     Failure/Error: @blog.should_not be_valid
       expected valid? to return false, got true
     # ./spec/models/blog_spec.rb:11:in `block (2 levels) in <top (required)>'

Finished in 0.02428 seconds
1 example, 1 failure

Failed examples:

rspec ./spec/models/blog_spec.rb:10 # Blog#name が設定されていない場合: バリデーションに失敗すること
rake aborted!
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec spec/models/blog_spec.rb failed

Tasks: TOP => spec
(See full trace by running task with --trace)
fix "app/models/blog.rb"

テストに成功するように修正(モデルにバリデーション追加)

class Blog < ActiveRecord::Base
  attr_accessible :name

  validates :name, presence: true
end
when success
$ rake spec SPEC=spec/models/blog_spec.rb
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec spec/models/blog_spec.rb

Blog#name が設定されていない場合:
  バリデーションに失敗すること

Finished in 0.02955 seconds
1 example, 0 failures
edig "app/model/blog_spec.rb"

"#name が設定されていない場合:"

describe Blog, "#name が設定されていない場合:"  do
:
  it ":name にエラーが設定されていること" do
    @blog.should have(1).errors_on(:name)
  end
end
Check
$ rake spec SPEC=spec/models/blog_spec.rb
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec spec/models/blog_spec.rb

Blog#name が設定されていない場合:
  バリデーションに失敗すること
  :name にエラーが設定されていること

Blog
  は複数の記事を所有できること

Finished in 0.21581 seconds
3 examples, 0 failures

「Blog に Entry を追加できること」をスペックとして定義

edit "spec/models/blog_spec.rb"
describe Blog, "に記事を投稿できた場合:" do
  fixtures :blogs, :entries
  before do
    @blog = blogs(:one)
  end

  it "記事の件数が1件増えること" do
    lambda {
      @blog.entries.create(
        :title => 'new_post', :body => 'hello',
        :posted_on => Date.today)
    }.should change(Entry, :count).by(1)
  end
end
Check
$ rake spec SPEC=spec/models/blog_spec.rb
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec spec/models/blog_spec.rb

Blog#name が設定されていない場合:
  バリデーションに失敗すること
  :name にエラーが設定されていること

Blog
  は複数の記事を所有できること

Blog に記事を投稿できた場合:
  記事の件数が1件増えること

Finished in 0.27299 seconds
4 examples, 0 failures

model "Entry"

$ rails generate model entry title:string body:text posted_on:date created_at:timestamp updated_at:timestamp blog_id:integer
      invoke  active_record
      create    db/migrate/20120526023402_create_entries.rb
      create    app/models/entry.rb
      invoke    rspec
      create      spec/models/entry_spec.rb
rake db:migrate

Make fixtures

  • spec/fixtures/entries.yml
earliest:
  id: 1
  blog_id: 1
  title: "吾輩は猫である"
  body: "名前はまだない"
  posted_on: 2012-05-01
latest:
  id: 2
  blog_id: 1
  title: "hoge"
  body: "hogehoge"
  posted_on: 2012-05-10
_upgrading:
  id: 3
  blog_id: 2
  title: "fuga"
  body: "fugafuga"
  posted_on: 2012-06-01

spec/models/entry_spec.rb

# -*- coding: utf-8 -*-
require 'spec_helper'

describe Entry do
  # pending "add some examples to (or delete) #{__FILE__}"
  fixtures :entries, :blogs

  before(:each) do
    @entry = entries(:earliest)
  end

  it "は特定のブログに属すること" do
    @entry.blog.should == blogs(:one)
  end  
end

run "rake spec SPEC=spec/models/entry_spec.rb"

$ rake spec SPEC=spec/models/entry_spec.rb
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec spec/models/entry_spec.rb

Entry
  は特定のブログに属すること (FAILED - 1)

Failures:

  1) Entry は特定のブログに属すること
     Failure/Error: @entry.blog.should == blogs(:one)
     NoMethodError:
       undefined method `blog' for #<Entry:0x007f92aca85d68>
     # ./spec/models/entry_spec.rb:13:in `block (2 levels) in <top (required)>'

Finished in 0.18317 seconds
1 example, 1 failure

Failed examples:

rspec ./spec/models/entry_spec.rb:12 # Entry は特定のブログに属すること
rake aborted!
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec spec/models/entry_spec.rb failed

Tasks: TOP => spec
(See full trace by running task with --trace)
fix
  • app/models/entry.rb
class Entry < ActiveRecord::Base
  attr_accessible :blog_id, :body, :created_at, :posted_on, :title, :updated_at

  belongs_to :blog
end

edit "spec/models/blog_spec.rb"

add

:
describe Blog do
  fixtures :blogs, :entries
  before do
    @blog = blogs(:one)
  end

  it "は複数の記事を所有できること" do
    @blog.should have_at_least(2).entries
  end

Run "rake spec SPEC=spec/models/blog_spec.rb"

$ rake spec SPEC=spec/models/blog_spec.rb
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec spec/models/blog_spec.rb

Blog#name が設定されていない場合:
  バリデーションに失敗すること

Blog
  は複数の記事を所有できること (FAILED - 1)

Failures:

  1) Blog は複数の記事を所有できること
     Failure/Error: @blog.should have_at_least(2).entries
     NoMethodError:
       undefined method `entries' for #<Blog:0x007fca94f87328>
     # ./spec/models/blog_spec.rb:26:in `block (2 levels) in <top (required)>'

Finished in 0.20175 seconds
2 examples, 1 failure

Failed examples:

rspec ./spec/models/blog_spec.rb:25 # Blog は複数の記事を所有できること
rake aborted!
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec spec/models/blog_spec.rb failed

Tasks: TOP => spec
(See full trace by running task with --trace)
fix "app/models/blog.rb"

add

  has_many :entries
Check
$ rake spec SPEC=spec/models/blog_spec.rb
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec spec/models/blog_spec.rb

Blog#name が設定されていない場合:
  バリデーションに失敗すること

Blog
  は複数の記事を所有できること

Finished in 0.32601 seconds
2 examples, 0 failures

define the spec

「Blog に Entry を追加できること」

  • spec/models/blog_spec.rb
:

describe Blog, "に記事を投稿できた場合:" do
  fixtures :blogs, :entries
  before do
    @blog = blogs(:one)
  end

  it "記事の件数が1件増えること" do
    lambda {
      @blog.entries.create(
        :title => 'new_post', :body => 'hello',
        :posted_on => Date.today)
    }.should change(Entry, :count).by(1)
  end
end
Run
$ rake spec SPEC=spec/models/blog_spec.rb
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec spec/models/blog_spec.rb

Blog#name が設定されていない場合:
  バリデーションに失敗すること

Blog
  は複数の記事を所有できること

Blog に記事を投稿できた場合:
  記事の件数が1件増えること

Finished in 0.27593 seconds
3 examples, 0 failures

define the spec

記事の投稿日に期待する振舞を定義する

  • spec/models/entry_spec.rb
:
describe Entry, "#posted_on が入力されずに保存された場合:" do
  fixtures :blogs
  before do
    @entry = Entry.new(:blog_id => blogs(:one),
                       :title => "タイトル", :body => "本文")
    @entry.save!
    @entry.reload
  end

  it "Entry の作成日は投稿日であること" do
    @entry.posted_on.should == Date.today
  end
end
:
edit app/models/entry.rb

add

  before_save :posted_on_today

  def posted_on_today
    self.posted_on = Date.today
  end
Check "rake spec SPEC=spec/models/entry_spec.rb"
$ rake spec SPEC=spec/models/entry_spec.rb
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec spec/models/entry_spec.rb

Entry
  は特定のブログに属すること

Entry#posted_on が入力されずに保存された場合:
  Entry の作成日は投稿日であること

Finished in 0.21315 seconds
2 examples, 0 failures

define the spec

"#posted_on を明示して保存された場合:"

describe Entry, "#posted_on を明示して保存された場合:" do
  fixtures :blogs
  before do
    @posted_on = Date.today - 10
    @entry = Entry.new(:blog_id => blogs(:one),
                       :title => "タイトル", :body => "本文",
                       :posted_on => @posted_on)
    @entry.save!
    @entry.reload
  end

  it "入力された日付が投稿日であること" do
    @entry.posted_on.should == @posted_on
  end
end
edit app/models/entry.rb

fix

    # self.posted_on = Date.today
    self.posted_on ||= Date.today
Check
$ rake spec SPEC=spec/models/entry_spec.rb
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec spec/models/entry_spec.rb

Entry
  は特定のブログに属すること

Entry#posted_on が入力されずに保存された場合:
  Entry の作成日は投稿日であること

Entry#posted_on を明示して保存された場合:
  入力された日付が投稿日であること

Finished in 0.26126 seconds
3 examples, 0 failures

check all

$ rake spec
/Users/username/.rvm/rubies/ruby-1.9.3-p194/bin/ruby -S rspec ./spec/models/blog_spec.rb ./spec/models/entry_spec.rb

Blog#name が設定されていない場合:
  バリデーションに失敗すること
  :name にエラーが設定されていること

Blog
  は複数の記事を所有できること

Blog に記事を投稿できた場合:
  記事の件数が1件増えること

Entry
  は特定のブログに属すること

Entry#posted_on が入力されずに保存された場合:
  Entry の作成日は投稿日であること

Entry#posted_on を明示して保存された場合:
  入力された日付が投稿日であること

Finished in 0.65501 seconds
7 examples, 0 failures

*1:修正: 20120527