Angular Testing part 1

Phai Panda
4 min readOct 30, 2019

--

ว่างมากเหรอถึงมาเขียน Test?

background vector created by freepik

ผมเป็นคนหนึ่งที่ไม่ได้เขียน Test ก่อนจะเขียนโปรแกรม และนายจ้างผมไม่สนใจมันด้วย เวลาผมเขียนโปรแกรมส่ง พวกเราจะสนเพียงผลลัพธ์ที่ต้องได้ในตอนนั้น อนาคตเป็นเรื่องของวินาทีถัดไปหรือวันพรุ่งนี้ ไม่ใช่วันนี้ที่ต้องเสียเวลาเพื่อจะมาเขียน Test ในสิ่งที่รู้ๆกันอยู่แล้ว คุณคิดเหมือนผมไหมล่ะ?

“คุณ Pros คุณทำอะไร” นายจ้างถามผมขณะที่ผมกำลังอ่าน Angular 2 “อ่อ ผมจะเขียนหน้า contact ลูกค้าครับ”

“ดี หน้านี้ผมขอแค่ email กับ phone number ที่เหลือคุณถามกับผู้จัดการว่าควรมีอะไรเพิ่มหรือเปล่า” นายจ้างกล่าวก่อนจะเดินจากไป “พักเที่ยงแล้วเดี๋ยวเราไปทานข้าวกัน”

ในแต่ละวันที่ผ่านก็มีเท่านี้ เราอยู่กันแบบครอบครัว เหมือนพี่เหมือนน้อง ไม่ได้มีเวลาว่างมากไปกว่านี้ หรือถึงมีก็ไม่ใช่เรื่องการเขียน Test แน่นอน

“เฮ้ย Outsource เขาไม่มาเสียเวลากับอะไรแบบนี้หรอก รีบปิดงานให้เสร็จ รับเงิน หมดสัญญาก็ไปได้” เพื่อนสุดที่รักบอกกับผม “อยากรู้อะไรก็ไปเปิด source code เอาดิ ไม่มีใครมานั่งอธิบายให้ฟังหรอก มันเสียเวลาเขา”

“เปิดโค้ดดิ ก็หน้าใบสมัครกับแก้ไขใบสมัครไง email ต้องไม่ว่าง แล้วเช็คด้วยว่าใส่มาถูกหรือเปล่า Angular มันมี Validation ไหม” หัวหน้าทีมใช้งานผม “ดู Database ด้วยนะว่า length เท่าไร”

“พวกนายมีเวลาน้อย อย่าว่าแต่เวลาเขียนโปรเจกต์ จะเอาเวลาที่ไหนไปเขียน Test ไม่ต้องเขียน แค่นี้ก็ไม่ทันทำส่งก่อนเทอมสองแล้ว” ผมพูดกับน้องฝึกงาน ด้วยความจริงที่ว่าที่ผ่านมาผมก็ไม่เขียน Test คือกัน

แต่นี่คือบทความว่าด้วยเรื่อง Angular Testing part 1 ก็จะนำเพื่อนๆให้มาเขียน Test ร่วมกันแบบมือใหม่หัดขับ เฉียดบ้างชนบ้างเรื่องธรรมดาครับ ไม่เสียเวลาแล้ว มาดูกันเลย

ทำไมต้องเขียน Test ก่อนเขียนโค้ด?

อันนี้เรียกว่า TDD ย่อมาจาก Test-Driven Development โอเคร~ ไปดูที่เขาอธิบายไว้แล้วดีกว่า เนื้อหาจะได้ไม่ซ้ำซ้อน

สรุป TDD แบบง่ายๆคือ เขียน Test ก่อนแล้วให้ Test สร้าง Code ให้

Angular Test

เมื่อค้น Google ด้วยคีย์เวิร์ดว่า angular test จึงได้หน้านี้มา

เขาบอกว่าเมื่อเราสร้าง Angular project ด้วย Angular CLI เจ้า Angular CLI จะจัดเตรียมทุกอย่างให้พร้อมสำหรับการเขียน Test

Test ดังกล่าวนี้คือ Jasmine Test Framework หรือกรอบความคิดสำหรับเขียน Test ชื่อ Jasmine (จะแปลทำไม)

Angular team เลือกใช้ Jasmine ก็แสดงว่ามันต้องมีอะไรดีสักอย่าง มาดูกัน

Jasmine Test Framework

ที่เครื่องของเพื่อนๆคงติดตั้ง Node.js ไว้แล้ว ดังนั้น

1. ติดตั้ง jasmine

npm install -g jasmine
เวอร์ชันที่ผมติดตั้งตอนนี้ jasmine@3.5.0

2. สร้าง project

ผมสร้าง folder ชื่อ contacts ไว้ใน folder ชื่อ jasmine อีกที กะว่าให้เป็นโปรเจกต์สำหรับเบอร์โทรศัพท์ผู้ติดต่อ

mkdir -p jasmine/contacts

เข้าไป

cd jasmine/contacts

3. init jasmine project

jasmine init

มันจะสร้าง folder ชื่อ spec ไว้ให้

มาดูรายละเอียดของ jasmine.json กันครับ

  • spec_dir คือ Spec directory path relative to the current working dir when jasmine is executed ง่ายๆว่าพื้นที่ทำงาน จะเรียกว่า working directory ก็ได้
  • spec_files คือ Array of filepaths (and globs) relative to spec_dir to include and exclude ส่วนประกอบของไฟล์ที่อยากให้สนใจ (include) หรือไม่ต้องสนใจ (exclude) ใน working directory (ใส่เครื่องหมาย ! นำถ้าต้องการให้ exclude)
  • helpers คือ Array of filepaths (and globs) relative to spec_dir to include before jasmine specs
  • stopSpecOnExpectationFailure คือ Stop execution of a spec after the first expectation failure in it หยุดกระบวนการเลยไหมถ้าเกิดมีเคส failure กำหนดเป็น true ก็ได้นะ
  • random คือ Run specs in semi-random order สุ่มทำเคสไหม กำหนดเป็น false ดีกว่านะ

4. ขอตัวอย่าง

เพื่อให้เริ่มต้นได้ง่ายขึ้น ขอตัวอย่างมาดูหน่อย

jasmine examples

5. run test

jasmine spec/jasmine_examples/PlayerSpec.js

ผลลัพธ์

มีทั้งหมด 5 cases และผ่านทั้งหมด

ลอก เริ่มต้นและทำความเข้าใจ

เพื่อนๆเห็นแล้วว่าการเขียน Test นั้นจะต้องรู้ก่อนว่าจะทดสอบอะไร? แล้วคาดหวังผลลัพธ์เช่นไร หากผลลัพธ์เป็นไปตามที่คาดหวังก็ผ่าน หากไม่ก็ไม่ผ่าน แค่นี้แหละ

สมมติเรามีเงินในมือ 100 บาท เรานึกในใจว่า ฉันหิวข้าว จากนั้นก็เดินไปสั่งข้าวผัดไก่ สิ่งที่คาดหวังก็คือข้าวผัดไก่ ปรากฏว่าได้ข้าวผัดปลาทูมาซะงั้น สั่งผิดป่าว อย่างนี้ผลการ Test คือไม่ผ่าน ลองมาเขียนเป็นโค้ดง่ายๆครับ

describe( ฉันหิวข้าว , function() { ... })

จากนั้นก็เดินไปสั่งข้าวผัดไก่

describe( ฉันหิวข้าว , function() {
it( ควรได้ข้าวผัดไก่ , function() {
แม่ครัวรับรายการ ข้าวผัดไก่
expect( แม่ครัวได้ทำอาหาร ).toEqual​( ข้าวผัดไก่ ) //true
})
})

กรณีนี้คือ ผ่าน เพราะสั่งข้าวผัดไก่ ก็ได้ ข้าวผัดไก่ สมปรารถนา แต่ถ้าเกิดว่า

describe( ฉันหิวข้าว , function() {
it( ควรได้ข้าวผัดไก่ , function() {
แม่ครัวรับรายการ ข้าวผัดไก่
expect( แม่ครัวทำอาหาร ).toEqual​( ข้าวผัดไก่ ) //false ข้าวผัดปลาท
})
})

กรณีนี้คือ ไม่ผ่าน เพราะสั่งข้าวผัดไก่ ดันได้ ข้าวผัดปลาทู

เอาล่ะครับ พอจินตนาการได้แล้วผมก็ขอพาเข้าเนื้อหากันเลย คลิกลิงก์ด้านล่างจ้า

Your First Suite

อันดับแรกเลยที่เพื่อนๆเห็นคือฟังก์ชันชื่อ describe ตามมาคือ it แล้วก็ expect

describe พูดว่า กลุ่มของ specs (เคสต่างๆ) ที่มีความสัมพันธ์กัน ในแต่ละไฟล์ Test จะมี describe อยู่บนสุดหนึ่งตัวเสมอ ฟังก์ชันนี้มีพารามิเตอร์ 2 อย่าง

  1. ชื่อกลุ่ม specs
  2. ฟังก์ชันการทำงาน

อ๋อ หากถามว่าในหนึ่ง describe มี describe อีกได้ไหม ตอบว่า ได้นะ

it พูดถึง spec หรือก็คือกรณีหรือเคสต่างๆที่เราต้องการทดสอบ โดย 1 it ก็คือ 1 spec ฟักง์ชันนี้มีพารามิเตอร์ 2 อย่าง

  1. ชื่อของ spec
  2. ฟังก์ชันการทำงาน

อ๋อ หากถามว่าในหนึ่ง it มี it อีกได้ไหม ตอบว่า ไม่ควรมี

expect ก็คือ expectation จะอยู่ภายในฟังก์ชันการทำงานของ it สามารถมีได้มากกว่า 1 expectation ต่อ 1 it แต่มีข้อแม้ว่า expect ท้ายสุดของ it ใดๆจะต้องเป็น assertion ของการทดสอบ ดังนั้น expect ก่อนหน้านี้ (กำลังพูดในกรณีที่มีหลาย expect ต่อ 1 it) ควรให้ค่าเป็น true เท่านั้น

expect แบ่งออกเป็น 2 ส่วน

  1. actual value สิ่งที่คาดหวังนำมาทดสอบ
  2. expected value ผลลัพธ์ที่ต้องได้

ในแต่ละ spec (หรือ it) จะต้องได้คำตอบออกมา คำตอบนี้เรียกว่า assertion ซึ่งมีค่าเป็น true หรือ false เท่านั้น ด้วยเหตุนี้ถ้าทุก spec ให้ผลเป็น true ทั้งหมดจึงถือว่าผลการทดสอบนี้ผ่าน แต่หากมีเพียง spec เดียวให้ผลเป็น false ผลการทดสอบก็คือไม่ผ่าน

Matchers

เมื่อเพื่อนๆสังเกตให้ดีจะพบว่าแต่ละ expect นั้นมีสิ่งหนึ่งคั่นระหว่าง actual value กับ expected value สิ่งนี้เรียกว่า matchers ตัวอย่าง

expect(true).toBe(true)expect( แม่ครัวทำอาหาร ).toEqual​( ข้าวผัดไก่ )expect(false).not.toBe(true)expect(player.isPlaying).toBeFalsy()expect(player.isPlaying).toBeTruthy()

แต่ละ matcher ให้ผลลัพธ์เป็น true หรือ false มีทั้งที่ต้องระบุ expected value เช่น toBe, toEqual หรือไม่ต้องระบุ expected value เช่น toBeFalsy, toBeTruthy

Jasmine ได้จัดเตรียม matchers ไว้ให้แล้วจำนวนหนึ่ง หรืออยากจะเขียน matcher เองก็ได้ (custom matchers) อ่านเพิ่มเติม

ทฤษฎีใน part แรกนี้ก็น่าจะเพียงพอแล้วสำหรับมือใหม่ที่เพิ่งเริ่มศึกษาเขียน Angular Test ประมาณว่า เออ อยากเขียน TDD ก็มาเจอกับ Jasmine Test Framework เนี่ยต้องไปยังไง

หัวใจคือ เขียน Test แล้วให้ Test ไปเขียน Code ให้

ไว้มาเจอกัน part ต่อไป เดี๋ยว implement ให้ดูเป็นฉากๆ ลาล่ะ สวัสดีครับ

--

--

No responses yet