หน้าเว็บ

วันศุกร์ที่ 1 สิงหาคม พ.ศ. 2557

Mock ด้วย MiniTest

จุดประสงค์ของการ mock คือ ต้องการรู้ว่าเมธอดของ mock อ็อบเจกต์ถูกเรียกใช้อย่างถูกต้องตามที่คาดหวัง (expect) ไว้หรือไม่

สรุปก็คือการ mock เป็นการทดสอบให้แน่ใจว่าว่าเมธอดที่เราดูอยู่จะต้องถูกเรียกออกมาใช้จากอ็อบเจกต์ที่เราได้ mock เอาไว้ และค่าที่คืนออกมาจากเมธอดนั้นต้องเป็นค่าที่ถูกต้องด้วย
ลองดูตัวอย่างของการใช้ mock ดังนี้

require 'minitest/autorun'

class Label
  def initialize(note)
    @note = note
  end

  def emphasize
    @note.highlight
  end
end

describe Label do
  it "has all charecter upcase when emphasize" do
    special_note = "*** HAPPY DAY ***"

    note = MiniTest::Mock.new
    note.expect(:highlight, special_note)
    
    label = Label.new(note)
    label.emphasize.must_match(/\*\*\*/)
    note.verify
  end
end

ตัวอย่างข้างต้นอ็อบเจกต์ note เป็นอ็อบเจกต์ที่เราสมมติขึ้นมาหรือเรียกว่า mock มันเอาไว้ ทีนี้ เมธอดที่ต้อวการจะตรวจสอบว่าจะต้องถูกเรียกด้วยอ็อบเจกต์ note ก็คือ เมธอด highlight ดั้งนั้นจึงต้องใส่โค้ดที่ระบุความคาดหวังจากมธอด highlight ซึ่งคือโค้ด note.expect(:highlight, special_note)
จากนั้นจึงเป็นการทดสอบโดยกำหนดให้ label เรียกเมธอด emphasize แล้วจะต้องได้ค่าที่สอดคล้อง (must_match) กับ *** แล้วจึงสั่งให้ note.verify

ถ้าดูจาก spec อย่างเดียว ความหมายก็คือ ข้างในเมธอด emphasize จะทำงานอย่างไรไม่รู้ รู้แต่ว่า spec ข้อนี้จะผ่านได้ก็ต่อเมื่อ เมธอด highlight จะต้องถูกเรียกใช้จาก mock อ็อบเจกต์ (ในที่นี้คือ note) ตามที่ระบุไว้ใน expect ดังนั้น จึงเป็นการบอกโดยนัย หรือโดยปริยายว่า ภายใน emphasize จะต้องมีการเรียก note.highlight แน่นอน และค่าที่คืนออกมานั้นก็จะต้องสอดคล้องกับสตริง *** ด้วย

ถ้าเอาโค้ดข้างต้นไปรันด้วย minitest ผลของการทดสอบก็จะผ่านไปได้

C:\CodeProject\Ruby\TDD\minitest\example1>ruby spec/book_spec.rb
Run options: --seed 8903

# Running:

.

Finished in 0.005000s, 200.0000 runs/s, 400.0000 assertions/s.

1 runs, 2 assertions, 0 failures, 0 errors, 0 skips


ลองดูอีกสักตัวอย่างกับการ mock
spec มีหน้าตาแบบนี้...

describe Book do
  it "uses authors name in description" do
    author = MiniTest::Mock.new
    author.expect(:name, "Worrawut")

    book = Book.new("Royal Assassin", :author => author)
    book.description.must_match(/Written by Worrawut/)
    author.verify
  end
end

จากโค้ดจะเห็นว่าเรา mock เพื่อต้องการย้ำว่าเมธอด name ของอ็อบเจกต์ note จะต้องถูกเรียกใช้พร้อมกับให้ค่าที่ถูกต้อง เมื่อเมธอด description ของ book ถูกเรียกขึ้นมาใช้งาน
ดังนั้นโค้ดที่จะทำให้ spec นี้รันผ่านไปได้จึงมีหน้าตาดังนี้

class Book
  def initialize(title, h)
    @title = title
    @author = h[:author]
  end
  def description
    "#{@title} is Written by #{@author.name}"
  end
end

ไม่มีความคิดเห็น:

แสดงความคิดเห็น