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