RSpec animals
Animals có đáng được RSpec (respect :D) hay không? Sau khi học 4OOP và RSpec, ta thực hành viết spec cho các class đó xem sao. Cấu trúc thư mục Tạo cấu trúc thư mục như sau: animals_with_rspec/ animal/ animal.rb cat.rb dog.rb zoo.rb spec/ animals_spec.rb ...
Animals có đáng được RSpec (respect :D) hay không?
Sau khi học 4OOP và RSpec, ta thực hành viết spec cho các class đó xem sao.

Cấu trúc thư mục
Tạo cấu trúc thư mục như sau:
animals_with_rspec/
animal/
animal.rb
cat.rb
dog.rb
zoo.rb
spec/
animals_spec.rb
zoo_spec.rb
Animals
animal.rb
class Animal
attr_reader :name
def initialize(name)
@name = name
end
def say_hello
puts "I am animal, my name is "
end
end
cat.rb
require File.expand_path(File.dirname(__FILE__) + '/animal')
class Cat < Animal
def say_hello
puts "Meow, my name is "
end
end
dog.rb
require File.expand_path(File.dirname(__FILE__) + '/animal')
class Dog < Animal
def say_hello
puts "Woof woof, my name is "
end
end
zoo.rb
class Zoo
attr_reader :animals
def initialize
@animals = []
end
def add(animal)
@animals << animal
end
def remove(animal)
@animals.delete(animal)
end
def say_hello
@animals.each { |a| a.say_hello }
end
end
Spec
Đúng ra phải viết spec trước, định nghĩa WHAT xong rồi mới sang bước HOW là viết chương trình. Mặc dù làm ngược thế này cho bài viết dễ hiểu, đúng là:
Sinh con rồi mới sinh cha
Sinh cháu giữ nhà rồi mới sinh ông
animals_spec.rb
Dir.glob(File.dirname(__FILE__) + '/../animal/*.rb') do |file|
require file
end
[Animal, Cat, Dog].each do |klass|
describe klass do
it 'should be initialized with a name' do
name = 'Bush'
animal = klass.new(name)
expect(animal).not_to be_nil
expect(animal.name).to eq(name)
end
it 'should be able to say hello' do
animal = klass.new('Bush')
animal.say_hello
end
end
end
zoo_spec.rb
Dir.glob(File.dirname(__FILE__) + '/../animal/*.rb') do |file|
require file
end
describe Zoo do
before(:each) do
@zoo = Zoo.new
end
it 'should have 0 animal after created' do
expect(@zoo.animals.size).to eq(0)
end
it 'should have 1 more animal when one is added' do
cat = Cat.new('Bush')
@zoo.add(cat)
expect(@zoo.animals.size).to eq(1)
end
it 'should have 1 less animal when one is removed' do
cat = Cat.new('Bush')
@zoo.add(cat)
@zoo.remove(cat)
expect(@zoo.animals.size).to eq(0)
end
it 'should let all animals say hello' do
@zoo.add(Cat.new('Bush1'))
@zoo.add(Dog.new('Bush2'))
@zoo.say_hello
end
end
Chạy spec
Sau khi cài RSpec (gem install rspec), trên máy có thêm lệnh rspec. Để chạy test spec, cú pháp căn bản: rspec <tên tập tin>. Khi chạy lệnh này, các tập tin của thư viện RSpec sẽ được require tự động, ta không cần tự require trong các tập tin _spec.rb.
Từ thư mục animals_with_rspec: rspec spec/animals_spec.rb
.I am animal, my name is Bush ..Meow, my name is Bush ..Woof woof, my name is Bush . Finished in 0.032561 seconds 6 examples, 0 failures
Để xuất ra đẹp hơn, dùng thử tham số --format documentation
Animal - should be initialized with a name I am animal, my name is Bush - should be able to say hello Cat - should be initialized with a name Meow, my name is Bush - should be able to say hello Dog - should be initialized with a name Woof woof, my name is Bush - should be able to say hello Finished in 0.020232 seconds 6 examples, 0 failures
Để biết các tham số khác (ví dụ để xuất ra HTML rất đẹp, có thể in ra thành tài liệu báo cáo sếp hoặc khách hàng): rspec -h.
Dùng wildcard để chạy tất cả test trong thư mục: rspec spec/* --format documentation
Animal - should be initialized with a name I am animal, my name is Bush - should be able to say hello Cat - should be initialized with a name Meow, my name is Bush - should be able to say hello Dog - should be initialized with a name Woof woof, my name is Bush - should be able to say hello Zoo - should have 0 animal after created - should have 1 more animal when one is added - should have 1 less animal when one is removed Meow, my name is Bush1 Woof woof, my name is Bush2 - should let all animals say hello Finished in 0.024873 seconds 9 examples, 0 failures