Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Thành thạo RSpec Testing trong Ruby: Tutorial toàn diện với ví dụ thực tế

Bạn muốn học viết test cho Ruby application dùng RSpec?

Thì bạn ở đúng chỗ!

Trong tutorial này tôi show cách làm.

Nội dung

  • 1 Tại sao viết Test?
      • 1.0.1 Xây safety net chống lỗi (đặc biệt hữu ích refactoring)
      • 1.0.2 Giúp document code
      • 1.0.3 Cho feedback loop
      • 1.0.4 Đảm bảo code produce kết quả kỳ vọng
      • 1.0.5 Giúp xin việc Ruby
  • 2 Bắt đầu với RSpec
  • 3 Ví dụ RSpec Testing
  • 4 RSpec Let Method
  • 5 Cách dùng Subject Method
  • 6 Chạy code trước tất cả test
  • 7 Tạo Testing Subgroups
  • 8 Tạm disable Test
  • 9 Chạy Example theo tên
  • 10 RSpec Expectations & Matchers
  • 11 RSpec Formatters
  • 12 Tìm Slow Tests
  • 13 RSpec Video Tutorial
  • 14 Tóm tắt
    • 14.1 Related

Tại sao viết Test?

Lý do:

Xây safety net chống lỗi (đặc biệt hữu ích refactoring)

Nếu không có test suite bạn không muốn đụng code, vì sợ break…

…có test tăng confidence!

Giúp document code

Test mô tả application nên làm gì.

Cho feedback loop

Khi làm TDD bạn có feedback loop bảo focus gì tiếp, hữu ích nếu dễ distracted.

Đảm bảo code produce kết quả kỳ vọng

Này quan trọng!

Nếu viết logic phức tạp, muốn chắc chắn chạy đúng nhiều input khác nhau không chỉ một example bạn nghĩ ra.

Test giúp uncover corner case & document chúng.

Giúp xin việc Ruby

Đa số application đánh giá testing skill, tăng chance landing job.

Bắt đầu với RSpec

Hiểu RSpec hoạt động như nào, đi qua ví dụ step-by-step.

Viết app đơn giản tìm factorial.

Bước đầu:

 require 'rspec/autorun'
describe Factorial do
  # ...
end 

Đây code initial viết RSpec test đầu tiên.

Cần require rspec gem.

Sau đó tạo block describe group tất cả test & bảo RSpec test class nào.

Tiếp là block it:

 describe Factorial do
  it "does something" do
    # ...
  end
end 

Đây tên test, và cách group các component của test.

Component:

  • Setup
  • Exercise
  • Verify

Setup là nơi tạo object cần thiết.

Đây phase chuẩn bị.

Sau đó gọi method muốn exercise lấy return value.

Cuối cùng, verify kết quả với expectation (RSpec) hoặc assertion (Minitest).

Ví dụ RSpec Testing

Nếu muốn viết factorial method, tìm valid values online hoặc tính tay.

Rồi dùng với test.

Như sau:

 describe Factorial do
  it "finds the factorial of 5" do
    calculator = Factorial.new
    expect(calculator.factorial_of(5)).to eq(120)
  end
end 

Khi chạy code (như Ruby program thường) bạn nhận:

 uninitialized constant Factorial (NameError) 

Bình thường vì chưa có class Factorial.

Tạo class:

 class Factorial
end 

Lỗi tiếp:

 undefined method 'factorial_of' 

Fix bằng tạo method factorial_of:

 class Factorial
  def factorial_of
  end
end 

Chạy lại:

 wrong number of arguments (given 1, expected 0) 

Lỗi nữa! Nhưng biết gì?

Đó là điều TỐT 🙂

Lỗi không phải để frustrated.

Chúng là feedback.

Bây giờ:

Thêm argument cho factorial_of:

 class Factorial
  def factorial_of(n)
  end
end 

Giờ nhận test failure:

 expected: 120
got: nil 

Đây chính xác nơi bạn muốn ở điểm này!

Nhiệm vụ tiếp: implement method:

 class Factorial
  def factorial_of(n)
    (1..n).inject(:*)
  end
end 

Và bạn nhận test pass đầu tiên:

 .
Finished in 0.00315 seconds (files took 0.09083 seconds to load)
1 example, 0 failures 

Đây là test-driven development (TDD).

Viết test trước, để test guide bước tiếp theo.

RSpec Let Method

Nếu viết nhiều test & reuse cùng object định nghĩa object với statement let.

Trông như:

 describe Factorial do
  let(:calculator) { Factorial.new }
  it "finds the factorial of 5" do
    expect(calculator.factorial_of(5)).to eq(120)
  end
end 

Giờ reuse calculator tất cả test dưới describe block.

Một điều về let là "lazy".

Ý tôi là gì?

Object không tạo cho đến lần đầu bạn dùng.

Này tạo khác biệt nếu tạo object có side-effect, như tạo database entry, ghi file.

Tốt nhất tránh side-effect, nhưng nếu không được thì dùng let!.

Ví dụ:

 let!(:user) { User.create("rspec@rubyguides.com") } 

let! non-lazy, object tạo trước mọi test chạy.

Cách dùng Subject Method

Version khác của let là subject.

Khác biệt: chỉ có một subject, và meant to be instance của main object test.

RSpec đã tạo default subject:

 subject { Factorial.new } 

Này gọi "implicit subject".

Dùng như:

 describe Factorial do
  it "finds the factorial of 5" do
    expect(subject.factorial_of(5)).to eq(120)
  end
end 

Bạn đặt tên subject:

 subject(:calculator) { Factorial.new } 

Này behave giống let, nhưng enable one-line expectation:

 it { should be_empty } 

Chạy code trước tất cả Test

RSpec có execution hooks chạy trước & sau mỗi test, hoặc group test.

Ví dụ:

 describe Shop do
  before(:all) { Shop.prepare_database }
  after (:all) { Shop.cleanup_database }
end 

Nếu muốn chạy code mỗi example (example = test RSpec) dùng :each thay :all.

Tạo Testing Subgroups

Nếu test scenario khác nhau app thì group related test hữu ích.

Làm với context block RSpec.

Ví dụ:

 describe Course do
  context "when user is logged in" do
    it "displays the course lessons" do end
    it "displays the course description" do end
  end
  context "when user it NOT logged in" do
    it "redirects to login page" do end
    it "it shows a message" do end
  end
end 

Tạm disable Test

Có thể disable test cho debug.

Chỉ cần đổi it thành xit cho test muốn disable.

Ví dụ:

 xit "eats lots of bacon" do end 

Đừng quên bỏ x khi xong!

Chạy Examples By Name

Thay vì disable test, filter test muốn chạy với flag -e.

Ví dụ:

 > ruby person.rb -e bacon 

Filter dựa trên tên test, ví dụ match test có từ "bacon".

RSpec Expectations & Matchers

Bạn nhớ ví dụ dùng:

 expect(calculator.factorial_of(5)).to eq(120) 

Nhưng phần eq(120) là gì?

120 là giá trị expect…

…và eq gọi là matcher.

Matchers là cách RSpec so sánh output method với expected value.

Trường hợp eq, RSpec dùng operator == (đọc thêm Ruby operators).

Nhưng có matcher khác.

Ví dụ, matcher be_something:

 expect(nil).to be_nil 

Ở đây something là predicate method (như empty?) gọi trên test result.

Matchers hữu ích khác:

  • include (cho array & hash)
  • start_with
  • end_with
  • match (regex matching)
  • be_between
  • have_key / have_value (hash)
  • be_instance_of
  • respond_to
  • have_attributes (test instance variable)

Matcher cần chú ý đặc biệt là raise_error.

Lý do: để dùng phải wrap expectation trong block.

Như sau:

 expect{ :x.count }.to raise_error(NoMethodError) 

change matcher cũng work vậy:

 expect{ stock.increment }.to change(stock, :value).by(100) 

RSpec Formatters

Default RSpec output format "progress".

Với format này thấy dot (.) represent 1 passing test, F failed test (expected & actual không match), hoặc E error.

Nhưng có alternative formatting option.

Danh sách:

  • progress
  • documentation
  • json
  • html

Enable với flag -f:

 > ruby factorial.rb -f d
Person
  eats lots of healthy food
  writes many articles
Finished in 0.00154 seconds (files took 0.09898 seconds to load)
2 examples, 0 failures 

Documentation format dùng test description generate output.

Tìm Slow Tests

RSpec có option handy profile test.

Chỉ pass flag --profile bạn thấy mỗi test mất bao lâu & fix cái thật slow.

Ví dụ:

 > ruby factorial.rb --profile
Factorial
  finds the factorial of 5
  0.00043 seconds 

RSpec Video Tutorial

Tóm tắt

Bạn đã học viết test dùng RSpec testing framework.

Giờ lượt bạn bắt đầu viết test!