หน้าเว็บ

วันศุกร์ที่ 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

วันจันทร์ที่ 21 กรกฎาคม พ.ศ. 2557

ตัวอย่างการใช้ subject และ should ใน RSpec


นการ Test อ็อบเจกต์หรือคลาสใดๆ ด้วย Rspec รูปแบบหรือฟอร์มที่เป็นพื้นฐานที่สุดสำหรับทดสอบ incomming message (หรือ instance เมธอด) ก็คือการใช้ expect(object) to respond_to(something) รูปแบบนี้มีความหมายชัดเจนในตัวอยู่แล้ว แต่ถ้าหากเราจะต้อง test เมธอดหลายๆ เมธอด การใช้ฟอร์มแบบนี้อาจก่อให้เกิดโค้ดที่ซ้ำกันโดยไม่จำเป็นและที่สำคัญมันอาจทำให้เราตาลายได้

วิธีแก้ ง่ายมาก ให้กำหนดอ็อบเจกต์เบื้องต้น (default object) ให้กับ rspec เพื่อทำการทดสอบในขอบเขตนั้นๆ โดยใช้ subject และใช้ should เพื่ออ้างถึง default object ดังกล่าว

สมมติว่า test โค้ดของเรามักจะมีหน้าตาประมาณนี้

describe MyClass do
    before { @my_object = MyClass.new(color: "Red", weight: 60) }

    it "should respond to 'color'" do
      expect(@my_object).to respond_to(:color)
    end

    it "should respond to 'weight'" do
      expect(@my_object).to respond_to(:weight)
    end
end 

โค้ดข้างต้นสามารถลดรูปให้สั้นลงและอ่านง่ายขึ้นได้ โดยใช้ subject และ should เข้ามาช่วย ดังนี้

describe MyClass do
    before { @my_object = MyClass.new(color: "Red", weight: 60) }
    subject { @my_onject  }

    it { should respond_to(:color)  }
    it { should respond_to(:weight)  }
end

เมธอด subject {@my_object} เป็นตัวหนดให้ @my_object กลายเป็น default object ที่จะถูกนำมารัน test ด้วยเมธอดต่างๆ ของ rspec ส่วนเมธอด should จะทำหน้าที่ตรวจสอบเงื่อนไขว่าเป็นจริงหรือไม่
จากตัวอย่างนี้ respond_to(:color) จะมีความหมายเดียวกับการใช้เมธอด respond_to? (จากคลาส Object ใน Ruby) ซึ่งจะคืนค่า true ออกมา ทำให้ผ่านเงื่อนไขของการ test ในที่สุด

ลองดูผลลัพธ์ที่เกิดจากเมธอด respond_to? กัน

>> class MyClass
>> def color
>> end
>> end
=> nil
>> obj = MyClass
=> MyClass
>> obj.respond_to?("color")
=> true
>> obj.respond_to?("other")
=> false

วันศุกร์ที่ 18 กรกฎาคม พ.ศ. 2557

เริ่มใช้ Rails

มี project ส่วนตัวที่อยากทำออกมาให้เป็น web app
ตั้งใจว่าจะใช้ sinatra เหมือนที่เคยทำกับ project ก่อน แต่ก็ต้องเปลี่ยนใจกลับมาลองใช้ Rails อีกครั้ง

จริงๆ แล้วเคยลองใช้มาก่อนช่วง Rails 2.0 ออกใหม่ (ตอนนี้ 4.1 แล้ว) แล้วไม่ค่อยประทับใจ
ในตอนนั้น รู้สึกว่าต้องเสียเวลาทำความเข้าใจกับการ config ที่เยอะมาก MVC ก็ยังงงใช้ผิดๆ ถูกๆ และเหมือนถูกบังคับให้ต้อง migrate database อยู่บ่อย ๆ ซึ่งในตอนนั้นยังมองไม่เห็นภาพ ไม่รู้ว่าทำเพื่ออะไร
ซึ่งเมื่อเปรียบเทียบกับ sinatra ผมรู้สึกว่าชีวิตมันง่ายกว่า เข้าใจถึงคำว่าเบากว่า (lighweight) ขึ้นมาเลย

อย่างไรก็ตาม หลังจากที่ลองศึกษาข้อมูลเพิ่มขึ้นเรื่อยๆ ทำให้เกิดอาการอยากลองของดูอีกสักครั้ง
ตอนนี้กำลังไล่อ่าน tutorial ของ Rails อยู่จากหลายๆ แหล่ง หลักๆ เลยตามอยู่ที่ Ruby On Rails Tutorial (อ่าน online ฟรี :) )

ยังคงเกิดความรู้สึกเดิมคือ ทำไมมันเยอะ จะเยอะจะเทอะไปไหน
คือจะ build web app นี่ต้อง dependency เยอะอย่างงี้เลย ? (gem มากมาย รายการใน Gemfile ยาวขึ้นทุกที)
แต่ เอาน่ะ ไหนๆ จะลองแล้วคราวนี้อยากเอาให้สุดซะที

วันอาทิตย์ที่ 28 เมษายน พ.ศ. 2556

สร้าง Wafer Map ด้วย ruby และ Cairo

Wafer Probe หรือ Wafer Sort เป็นขั้นตอนที่สำคัญขั้นตอนหนึ่งของกระบวนการผลิต IC chip โดยจะเป็นการนำ Silicon Wafer ที่ทำการขึ้นลายวงจรเรียบร้อยแล้วมาทดสอบ "ได"(Die, ตัว chip ที่ยังอยู่บนแผ่น Wafer โดยยังไม่ได้ถูกตัดออกมาเป็นชิ้นๆ) ที่อยู่บน Wafer นั้นว่าดีหรือเสีย ซึ่งหลังจากทดสอบไดทุกตัวบนเวเฟอร์เรียบร้อยแล้ว เราจะได้ soft file ซึ่งระบุตำแหน่ง coordinate x,y และสถานะของไดที่ถูก probe ไปแล้วว่าตำแหน่ง coordinate นั้นๆ เป็นไดที่ดีหรือเสีย ไฟล์ที่ว่าอาจเป็น text file ง่ายๆ ซื่งผมขอเรียกมันว่า coordinate file แล้วกันครับ

อันนี้่คือตัวอย่างของแผ่นซิลิกอนเวเฟอร์จริงครับ


วันนี้เราจะทดลองนำไฟล์ coordinate มาใช้ในการสร้างกราฟฟิกในรูปของ Wafer map image file
Wafer map ที่เราต้องการจะมีหน้าตาประมาณนี้ครับ สีเขียวที่เห็นคือไดที่ดี (good die) ส่วนสีอื่นๆคือไดเสีย (rejected die) ซึ่งสีที่ต่างกันแสดงถึงสาเหตุของอาการเสียที่แตกต่างกันออกไป

วันอังคารที่ 9 เมษายน พ.ศ. 2556

กลับมาแล้ว


ตอนนี้งานประจำที่ทำอยู่ซาลงเยอะครับ มีเวลากลับมาทบทวนสิ่งต่างๆที่ผ่านมามากขึ้น และก็นึกขึ้นได้ว่าเราเคยเขียน blog เกี่ยวกับ Ruby อยู่นี่หว่า
กลับมาดูอีกที โอ้ว มันผ่านมา 2 ปีแล้วเหรอเนี่ย
ปล่อย blog ล้างมานานขนาดนี้ จะมาเขียนต่อก็รู้สีกยังไงๆ แอบอายอยู่ลึกๆ
แต่สิ่งที่ทำให้ตัดสินใจว่าจะเขียนต่อ คือ ผมยังคงเห็นยอด view ของ blog นี้มีความเคลื่อนไหวอยู่จนถึงทุกวันนี้
ตัวเลขไม่มาก แต่ผมคิดว่ามีความหมายนะ อย่างน้อยก็ยังมีคนสนใจใน Ruby เหมือนกับผม มันทำให้มีความอยากขึ้นมาอีกครั้งที่จะเห็นบ้านเราพูดถึง Ruby และใช้กันอย่างกว้างขวางมากขึ้น
แม้จะเป็นส่วนน้อยๆ และคิดเข้าข้างตัวซะมาก แต่ก็ยังอยากจะลองดู

ก่อนลากันวันนี้ ขอฝาก link นี้ไว้แล้วกันครับ

เป็น note Ruby2.0 ที่ Matz พูดไว้ในงาน Waza 2013
http://showyou.com/popular/v:61043050

อันนี้เป็น full slide ครับ
https://speakerdeck.com/yukihiro_matz/ruby-2-dot-0-en