07/09/2018, 16:04

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
0