RSpec_logo-07
พอดีมีโอกาสศึกษาเกี่ยวกับ RSpec บ้างเล็กน้อย
แล้วพบว่า RSpec มันมีโครงสร้างของตัวรูปแบบภาษาที่สวยงาม
แถมพวก function ต่างๆ ที่ให้ใช้งานนั้น
ล้วนมีที่มาที่ไป แตกต่างกัน
ในช่วงแรก ของการศึกษาก็ไม่เข้าใจว่า
ทำไมต้องใช้คำสั่งนั้น ทำไมต้องใช้คำสั่งนี้
แต่เมื่อลองเริ่มจากสิ่งง่ายๆ แล้วทำการปรับปรุงโครงสร้างไปเรื่อยๆ
ทำให้เข้าใจของแต่ละคำสั่งมากขึ้น


โดยตัวอย่างของ code เกี่ยวกับการสั่งเบอร์เกอร์ว่าจะให้ใส่ซอสมาเลยหรือไม่
ซึ่งได้ตัวอย่างมาจาก web แต่ผมหา link ของ web ไม่ได้
เริ่มต้น code เป็นดังนี้

describe Burger do
  describe "#apply_ketchup" do
    context "with ketchup" do
      before do
        @burger = Burger.new(:ketchup => true)
        @burger.apply_ketchup
      end
 
      it "sets the ketchup flag to true" do
        @burger.has_ketchup_on_it?.should be_true
      end
    end
 
    context "without ketchup" do
      before do
        @burger = Burger.new(:ketchup => false)
        @burger.apply_ketchup
      end
 
      it "sets the ketchup flag to false" do
        @burger.has_ketchup_on_it?.should be_false
      end
    end
  end
end

คำอธิบาย
สังเกตได้ว่าจะมีคำว่า context ซึ่งเอาไว้สำหรับแบ่งแยกกลุ่มของการทดสอบที่เหมือนๆ กันไว้ด้วยกัน
แนวคิดเดียวกับ Test Suite นั่นเอง
ทำให้สามารถกำหนดค่าเริ่มต้นให้กับแต่ละกลุ่มได้อย่างสะดวก
โดยในการทดสอบนี้สำหรับ function ชื่อว่า apply_ketchup() ใน class Buger
เพื่อบอกว่าจะสั่ง Burger แบบใส่ซอสหรือไม่ใส่ซอสนั่นเอง

จาก code ข้างต้น พบว่าใน before ของแต่ละ context มีการประกาศตัวแปรชื่อว่า @burger ขึ้นมาทุกครั้ง
ดังนั้น RSpec จึงสร้าง keyword ช่วยขึ้นมา 1 ตัวชื่อว่า let
เพื่อทำการสร้างตัวแปรขึ้นมาอย่างอัตโนมัติในครั้งแรกที่เข้าทดสอบ
ดังนั้น สามารถใช้งาน let ดังนี้

describe Burger do
  describe "#apply_ketchup" do
    context "with ketchup" do
      let(:burger) { Burger.new(:ketchup => true) }
      before  { burger.apply_ketchup }
 
      it "sets the ketchup flag to true" do
        burger.has_ketchup_on_it?.should be_true
      end
    end
 
    context "without ketchup" do
      let(:burger) { Burger.new(:ketchup => false) }
      before  { burger.apply_ketchup }
 
      it "sets the ketchup flag to false" do
        burger.has_ketchup_on_it?.should be_false
      end
    end
  end
end

ดูเหมือนว่า code ของ RSpec ที่เราเขียนขึ้นมานั้นดีขึ้นแล้ว
แต่ทาง RSpec บอกว่า มันยังไม่ดีพอ … แต่สำหรับผมมันดีแล้วนะ !!!

ทาง RSpec บอกว่า สามารถที่จะทำให้ code สวยขึ้นด้วย
การใช้ subject method เพื่อบอกว่า กำลังทำการทดสอบบน object อะไร
และใช้ร่วมกับ specify method ซึ่งทำงานเหมือนกับ it method แต่ไม่จำเป็นต้องใส่คำอธิบายอะไร
เพียงแต่ใส่ code block ไปได้เลย ดังนี้

describe Burger do
  describe "#apply_ketchup" do
    subject { burger }
    before  { burger.apply_ketchup }
 
    context "with ketchup" do
      let(:burger) { Burger.new(:ketchup => true) }
 
      specify { subject.has_ketchup_on_it?.should be_true }
    end
 
    context "without ketchup" do
      let(:burger) { Burger.new(:ketchup => true) }
 
      specify { subject.has_ketchup_on_it?.should be_false }
    end
  end
end

แต่สังเกตไหมว่าใน specify method มันดูยาวๆ เกินไปนะ
ดังนั้นกลับมาใช้ความสามารถของ matcher กันเล็กน้อย
ซึ่งมันทำให้เกิด naming convention ที่สวยงามกว่าขึ้นมา
นั่นก็คือ RSpec จะมองหา method ที่ชื่อขึ้นด้วยคำว่า has
จะจบชื่อ method ด้วยเครื่องหมายคำถาม (?)
ใน class ที่ทำการทดสอบให้เอง
ดังนั้นเราสามารถเขียน code test แบบ declarative ได้ดังนี้

describe Burger do

	describe "#apply_ketchup" do
		subject { burger }
		before  { burger.apply_ketchup }

		context  "with ketchup" do
			let(:burger) { Burger.new(:ketchup => true) }
			
			it { should have_ketchup_on_it }
		end

		context  "without ketchup" do
			let(:burger) { Burger.new(:ketchup => false) }

			it { should_not have_ketchup_on_it }
		end
	end
end

ผลที่ได้คือ code ดูของ test ด้วย RSpec ดูสวยงามมากขึ้น
แต่สิ่งที่ต้องระลึกไว้ก็คือ test จะต้องอ่านง่ายและทำความเข้าใจได้ง่าย
รวมทั้งมีโครงสร้างที่เหมือนกันทั้ง project ด้วยนะ
และนี่คือเรื่องราวต่างๆ ของ method ต่างๆ ใน RSpec ที่ผมใช้งานเพียงเล็กน้อย
ยังต้องศึกษาและนำไปใช้งานอีกมากมาย

Tags: