ผลต่างระหว่างรุ่นของ "Afgu/unit testing 1"
Jittat (คุย | มีส่วนร่วม) |
Jittat (คุย | มีส่วนร่วม) |
||
แถว 31: | แถว 31: | ||
เราต้องการเขียนฟังก์ชัน | เราต้องการเขียนฟังก์ชัน | ||
− | + | <syntaxhighlight lang="javascript"> | |
− | + | function cirIntersect(x1, y1, r1, x2, y2, r2) { | |
+ | } | ||
+ | </syntaxhighlight> | ||
ที่รับข้อมูล | ที่รับข้อมูล | ||
แถว 57: | แถว 59: | ||
ใน <tt>test.js</tt> เราจะอธิบายกรณีทดสอบนี้ได้ดังนี้ | ใน <tt>test.js</tt> เราจะอธิบายกรณีทดสอบนี้ได้ดังนี้ | ||
− | < | + | <syntaxhighlight lang="javascript"> |
describe('cirIntersect', function(){ | describe('cirIntersect', function(){ | ||
แถว 65: | แถว 67: | ||
}); | }); | ||
− | </ | + | </syntaxhighlight> |
ในส่วนด้านบนเราระบุ: | ในส่วนด้านบนเราระบุ: | ||
แถว 82: | แถว 84: | ||
เราอาจจะเขียนฟังก์ชันดังกล่าวดังนี้ | เราอาจจะเขียนฟังก์ชันดังกล่าวดังนี้ | ||
− | + | <syntaxhighlight lang="javascript"> | |
− | + | function cirIntersect(x1, y1, r1, x2, y2, r2) { | |
− | + | return false; | |
+ | } | ||
+ | </syntaxhighlight> | ||
แต่เรามองแว่บเดียวก็เห็นแล้วว่าฟังก์ชันดังกล่าวทำงานไม่ถูกแน่ ๆ แต่ถ้ากรณีทดสอบเรามีแค่ข้อเดียวดังข้างต้น เราก็ไม่สามารถบอกได้ว่าโค้ดขำ ๆ ข้างบนนี้ทำงานผิดพลาด | แต่เรามองแว่บเดียวก็เห็นแล้วว่าฟังก์ชันดังกล่าวทำงานไม่ถูกแน่ ๆ แต่ถ้ากรณีทดสอบเรามีแค่ข้อเดียวดังข้างต้น เราก็ไม่สามารถบอกได้ว่าโค้ดขำ ๆ ข้างบนนี้ทำงานผิดพลาด | ||
แถว 100: | แถว 104: | ||
และเพิ่มโค้ดใน <tt>test.js</tt> เป็น | และเพิ่มโค้ดใน <tt>test.js</tt> เป็น | ||
− | < | + | <syntaxhighlight lang="javascript"> |
describe('cirIntersect', function(){ | describe('cirIntersect', function(){ | ||
แถว 111: | แถว 115: | ||
}); | }); | ||
}); | }); | ||
− | </ | + | </syntaxhighlight> |
เราสามารถแก้โค้ดให้ทำงานผ่านได้ง่าย ๆ โดยเปรียบเทียบระยะทางแกน x ได้ดังด้านล่าง | เราสามารถแก้โค้ดให้ทำงานผ่านได้ง่าย ๆ โดยเปรียบเทียบระยะทางแกน x ได้ดังด้านล่าง | ||
− | < | + | <syntaxhighlight lang="javascript"> |
function cirIntersect(x1, y1, r1, x2, y2, r2) { | function cirIntersect(x1, y1, r1, x2, y2, r2) { | ||
return Math.abs(x2 - x1) < (r1+r2); | return Math.abs(x2 - x1) < (r1+r2); | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
โค้ดถูกต้องหรือยัง? แน่นอนว่ายัง... | โค้ดถูกต้องหรือยัง? แน่นอนว่ายัง... | ||
แถว 131: | แถว 135: | ||
ลองเขียนกรณีทดสอบเพิ่ม โดยระบุในส่วน <tt>it('.....',function(){});</tt> ดังตัวอย่าง: | ลองเขียนกรณีทดสอบเพิ่ม โดยระบุในส่วน <tt>it('.....',function(){});</tt> ดังตัวอย่าง: | ||
− | < | + | <syntaxhighlight lang="javascript"> |
describe('cirIntersect', function(){ | describe('cirIntersect', function(){ | ||
// ... | // ... | ||
แถว 138: | แถว 142: | ||
}); | }); | ||
}); | }); | ||
− | </ | + | </syntaxhighlight> |
และสลับไปเขียนโค้ดให้ทำงานผ่าน ถ้าโค้ดทำงานผ่านอยู่แล้วก็ไม่ต้องแก้ไขอะไร | และสลับไปเขียนโค้ดให้ทำงานผ่าน ถ้าโค้ดทำงานผ่านอยู่แล้วก็ไม่ต้องแก้ไขอะไร | ||
แถว 148: | แถว 152: | ||
ในการพัฒนาโปรแกรมหลาย ๆ แบบเราต้องการรับ "วันที่" จากผู้ใช้ อย่างไรก็ตามรูปแบบในการป้อนวันที่มีหลากหลาย และไม่ใช่ว่าการกางปฏิทินให้ผู้ใช้กดจะเป็นทางเลือกที่ดีเสมอไป ใน JavaScript มีคลาส [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FDate Date] แต่เมทอดสำหรับแปลงวันที่จากสตริงนั้นค่อนข้างมีขอบเขตที่จำกัด และถ้าสตริงเป็นวันที่ภาษาไทย เช่น <tt>11 พ.ย. 56</tt> เมทอดนี้คงจะทำงานไม่ได้แน่ ๆ เราต้องการเขียนฟังก์ชัน | ในการพัฒนาโปรแกรมหลาย ๆ แบบเราต้องการรับ "วันที่" จากผู้ใช้ อย่างไรก็ตามรูปแบบในการป้อนวันที่มีหลากหลาย และไม่ใช่ว่าการกางปฏิทินให้ผู้ใช้กดจะเป็นทางเลือกที่ดีเสมอไป ใน JavaScript มีคลาส [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FDate Date] แต่เมทอดสำหรับแปลงวันที่จากสตริงนั้นค่อนข้างมีขอบเขตที่จำกัด และถ้าสตริงเป็นวันที่ภาษาไทย เช่น <tt>11 พ.ย. 56</tt> เมทอดนี้คงจะทำงานไม่ได้แน่ ๆ เราต้องการเขียนฟังก์ชัน | ||
− | + | <syntaxhighlight lang="javascript"> | |
− | + | function toDate(str) {} | |
+ | </syntaxhighlight> | ||
ที่รับสตริงและคืนค่าเป็น <tt>Date</tt> | ที่รับสตริงและคืนค่าเป็น <tt>Date</tt> |
รุ่นแก้ไขเมื่อ 02:45, 11 พฤศจิกายน 2556
เราใช้หัดเขียน unit test บน java script ซึ่งเป็นภาษาที่ทุกคนน่าจะสามารถเรียกให้ทำงานได้ ในครั้งแรกเราจะเน้นให้เข้าใจว่า unit test คืออะไร และสามารถเขียน unit test แบบทั่วไปได้ ในครั้งถัด ๆ ไปเราจะศึกษาเทคนิคเพิ่มเติมเช่นการทำ isolation รวมไปถึงการเขียน unit test ที่ดี
เราสามารถทำ unit testing ได้โดยไม่ต้องใช้ framework ใด ๆ เลยก็ได้ แต่ในที่นี้เราจะใช้ mocha เป็น framework mocha รองรับไลบรารีการ assert/expect ได้หลายแบบ เราเลือกใช้ chai นอกจากนี้ mocha ยังต้องการใช้ jquery ในการแสดงผล เราจึงต้องเรียก jquery ด้วย
ไลบรารีที่ใช้:
ที่เราเลือกใช้ mocha และ Chai นั้นเป็นตามรสนิยมผู้สอน ในการใช้งานจริง แนะนำให้เลือกไลบรารี/เฟรมเวิร์คที่ชอบตามสะดวก
เนื้อหา
โครงสร้างไดเร็กทอรี
ในแต่ละตัวอย่างและแบบฝึกหัดที่เราจะเขียน เราจะใช้โครงสร้างไดเร็กทอรีดังนี้
- project/ - *.js (ไฟล์ js ของ project) - test/ - index-test.html - test.js (เก็บโค้ดสำหรับ test) - lib/ - mocha.js - mocha.css - chai.js - jquery.js
สามารถดาวน์โหลด template ดังกล่าวได้: project.tgz, project.zip และเปลี่ยนชื่อไดเร็กทอรีตามความเหมาะสม
ตัวอย่าง
เราต้องการเขียนฟังก์ชัน
function cirIntersect(x1, y1, r1, x2, y2, r2) {
}
ที่รับข้อมูล
- วงกลมวงแรก ที่มีจุดศูนย์กลางที่ตำแหน่ง (x1,y1) รัศมี r1 และ
- วงกลมวงที่สอง ที่มีจุดศูนย์กลางที่ตำแหน่ง (x2,y2) รัศมี r2
จากนั้นคืนค่า
- true ถ้าวงกลมทั้งสองวงมีเส้นรอบวงที่ตัดกันหรือสัมผัสกัน (ถ้าวงกลมที่ซ้อนกันโดยที่วงเล็กอยู่ภายในวงใหญ่โดยไม่สัมผัสกันเลยจะไม่นับ)
สมมติว่ามีคนเขียนฟังก์ชันดังกล่าวมาให้เรา เราจะ "ทดสอบ" อะไรบ้าง ที่ทำให้เราเชื่อได้ว่าฟังก์ชันดังกล่าวทำงานได้ถูกต้อง?
กรณีทดสอบตัวอย่าง
ตัวอย่างหนึ่งที่เราทดสอบได้คือกรณีที่วงกลมสองวงห่างกันมาก ๆ จนเส้นรอบวงไม่ทับกัน (เราควรวาดรูปประกอบด้วย)
กรณี | กรณีที่ทดสอบ | x1 | y1 | r1 | x2 | y2 | r2 | return |
---|---|---|---|---|---|---|---|---|
1 | วงกลมห่างกัน | 0 | 0 | 10 | 100 | 0 | 10 | false |
ใน test.js เราจะอธิบายกรณีทดสอบนี้ได้ดังนี้
describe('cirIntersect', function(){
it('should return false when two circles are far apart', function(){
assert(cirIntersect(0,0,10,100,0,10) == false);
});
});
ในส่วนด้านบนเราระบุ:
- describe ระบุว่าจะอธิบายอะไร
- it (should) ระบุว่าสิ่งที่จะอธิบายจะต้องทำอะไรได้
- function ที่ระบุใน it นั้นเป็นโค้ดสำหรับทดสอบว่าสิ่งที่จะอธิบาย ทำสิ่งที่ระบุได้
- assert เป็น "เงื่อนไข" ที่เราจะทดสอบ
เขียนโค้ด
สังเกตว่าเรายังไม่ได้เขียนโค้ดอะไรเลยของฟังก์ชัน cirIntersect เราลองใช้ browser เปิด index-test.html เราจะเห็นว่าโปรแกรมของเรายังไม่ผ่านการทดสอบดังกล่าว
จากกรณีทดสอบตัวอย่าง เราจะแก้โค้ดของฟังก์ชันเพื่อให้ทำงานให้ผ่าน (ถ้าเราต้องการทำตามหลักการ เราจะเขียนโค้ดให้ง่ายที่สุดให้โปรแกรมทำงานผ่านข้อมูลทดสอบนี้)
เราอาจจะเขียนฟังก์ชันดังกล่าวดังนี้
function cirIntersect(x1, y1, r1, x2, y2, r2) {
return false;
}
แต่เรามองแว่บเดียวก็เห็นแล้วว่าฟังก์ชันดังกล่าวทำงานไม่ถูกแน่ ๆ แต่ถ้ากรณีทดสอบเรามีแค่ข้อเดียวดังข้างต้น เราก็ไม่สามารถบอกได้ว่าโค้ดขำ ๆ ข้างบนนี้ทำงานผิดพลาด
กรณีที่ตัดกันแบบง่าย ๆ
เราจะเพิ่มกรณีง่าย ๆ ที่เรานึกออก โดยให้วงกลมใหญ่หน่อยและตัดกัน
กรณี | กรณีที่ทดสอบ | x1 | y1 | r1 | x2 | y2 | r2 | return |
---|---|---|---|---|---|---|---|---|
2 | วงกลมตัดกันบนแกน x | 0 | 0 | 10 | 15 | 0 | 10 | true |
และเพิ่มโค้ดใน test.js เป็น
describe('cirIntersect', function(){
it('should return false when two circles are far apart', function(){
assert(cirIntersect(0,0,10,100,0,10)==false);
});
it('should return true when two circles on the x-axis intersect', function(){
assert(cirIntersect(0,0,10,15,0,10)==true);
});
});
เราสามารถแก้โค้ดให้ทำงานผ่านได้ง่าย ๆ โดยเปรียบเทียบระยะทางแกน x ได้ดังด้านล่าง
function cirIntersect(x1, y1, r1, x2, y2, r2) {
return Math.abs(x2 - x1) < (r1+r2);
}
โค้ดถูกต้องหรือยัง? แน่นอนว่ายัง...
ขั้นตอนที่เราทำนี้ ถ้าเราคิดได้เร็วหน่อย เราอาจจะไม่ต้องทำซ้ำ ๆ หลายรอบแบบนี้ก็ได้ แต่เพื่อเป็นการฝึกหัด เราจะทยอยแก้แบบง่าย ๆ แบบนี้ไปเรื่อย ๆ ก่อน
เพิ่มกรณีทดสอบ
โค้ดเรายังทำงานไม่ถูก แต่เราจะทำอย่างไรให้ทำงานถูกต้อง เราจะใช้กรณีทดสอบบังคับโค้ดให้ทำงานให้ถูกต้อง
ลองเขียนกรณีทดสอบเพิ่ม โดยระบุในส่วน it('.....',function(){}); ดังตัวอย่าง:
describe('cirIntersect', function(){
// ...
it('should blah blah', function(){
// ...
});
});
และสลับไปเขียนโค้ดให้ทำงานผ่าน ถ้าโค้ดทำงานผ่านอยู่แล้วก็ไม่ต้องแก้ไขอะไร
สิ่งที่ต้องใส่ใจ
แบบฝึกหัด 0: การแปลงวันที่
ในการพัฒนาโปรแกรมหลาย ๆ แบบเราต้องการรับ "วันที่" จากผู้ใช้ อย่างไรก็ตามรูปแบบในการป้อนวันที่มีหลากหลาย และไม่ใช่ว่าการกางปฏิทินให้ผู้ใช้กดจะเป็นทางเลือกที่ดีเสมอไป ใน JavaScript มีคลาส Date แต่เมทอดสำหรับแปลงวันที่จากสตริงนั้นค่อนข้างมีขอบเขตที่จำกัด และถ้าสตริงเป็นวันที่ภาษาไทย เช่น 11 พ.ย. 56 เมทอดนี้คงจะทำงานไม่ได้แน่ ๆ เราต้องการเขียนฟังก์ชัน
function toDate(str) {}
ที่รับสตริงและคืนค่าเป็น Date
ตัวอย่างแรก
อย่างน้อยฟังก์ชันของเราควรจะแปลสตริงแบบมาตรฐาน (ที่ Date แปลงได้) เป็นวันที่ให้ได้ก่อน