From 6c61425ade4c6469a68ce1826c10c9bc716f112d Mon Sep 17 00:00:00 2001 From: Prasit Tongpradit Date: Fri, 12 Apr 2024 23:01:56 +0700 Subject: [PATCH 1/7] =?UTF-8?q?=E0=B8=81=E0=B8=B2=E0=B8=A3=E0=B8=97?= =?UTF-8?q?=E0=B8=94=E0=B8=AA=E0=B8=AD=E0=B8=9A=E0=B8=AD=E0=B8=B1=E0=B8=95?= =?UTF-8?q?=E0=B9=82=E0=B8=99=E0=B8=A1=E0=B8=B1=E0=B8=95=E0=B8=B4=E0=B8=94?= =?UTF-8?q?=E0=B9=89=E0=B8=A7=E0=B8=A2=20Mocha?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../05-testing-mocha/article.md | 411 ++---------------- 1 file changed, 28 insertions(+), 383 deletions(-) diff --git a/1-js/03-code-quality/05-testing-mocha/article.md b/1-js/03-code-quality/05-testing-mocha/article.md index 1e3ef14db..be43c5ad7 100644 --- a/1-js/03-code-quality/05-testing-mocha/article.md +++ b/1-js/03-code-quality/05-testing-mocha/article.md @@ -1,419 +1,64 @@ -## ทดสอบโค้ดแบบอัตโนมัติกับ Mocha +# การทดสอบอัตโนมัติด้วย Mocha -โค้ดที่เราจะเขียนต่อไปนี้ จะมีการทดสอบอัตโนมัติด้วยนะ ซึ่งเป็นเทคนิคที่ใช้กันทั่วไปในโปรเจ็กต์จริงด้วย +ในงานต่อๆ ไปจะมีการใช้การทดสอบอัตโนมัติ ซึ่งเป็นสิ่งที่ใช้กันอย่างแพร่หลายในโปรเจ็กต์จริงด้วย -## ทำไมเราต้องทดสอบ? +## ทำไมต้องมีการทดสอบ? -เวลาเขียนฟังก์ชัน เรานึกออกว่ามันควรจะทำงานยังไง ใส่อะไรเข้าไป แล้วได้อะไรออกมา +เวลาเขียนฟังก์ชัน เรามักจะจินตนาการได้ว่ามันควรจะทำงานอย่างไร พารามิเตอร์แบบไหนให้ผลลัพธ์แบบไหน -ตอนพัฒนา เราก็จะทดสอบโดยการรันฟังก์ชัน แล้วดูว่าผลลัพธ์เป็นอย่างที่คาดหวังไว้หรือเปล่า เช่น รันใน console อะไรแบบนั้น +ระหว่างพัฒนา เราตรวจสอบฟังก์ชันได้โดยการรันและเปรียบเทียบผลลัพธ์กับสิ่งที่คาดหวังไว้ เช่น อาจทำในคอนโซล -ถ้าอะไรผิด ก็แก้โค้ด รันใหม่ ดูผลลัพธ์... วนๆ ไปจนกว่ามันจะเวิร์ค +ถ้ามีอะไรผิดพลาด ก็แก้โค้ด รันใหม่ เช็คผลลัพธ์ วนไปแบบนี้จนกว่าจะใช้ได้ -แต่การ "รันซ้ำ" แบบแมนนวลเนี่ย มันไม่ค่อยเวิร์คอะดิ +แต่การ "รันซ้ำ" ด้วยมือแบบนั้นยังไม่สมบูรณ์แบบ -**เวลาทดสอบโค้ดแบบรันซ้ำเอง มันง่ายมากที่จะพลาดอะไรบางอย่าง** +**เมื่อทดสอบโค้ดโดยการรันซ้ำเอง มักจะมีบางอย่างหลุดไปได้ง่าย** -เช่น เราสร้างฟังก์ชัน `f` เขียนโค้ดเสร็จ ทดสอบ: `f(1)` ทำงาน แต่ `f(2)` ไม่ทำงาน เราแก้โค้ด แล้วตอนนี้ `f(2)` ทำงานแล้ว ดูเหมือนเสร็จแล้วใช่ไหม? แต่เราลืมทดสอบ `f(1)` อีกที อาจจะเจอ error ก็ได้ +ยกตัวอย่างเช่น สมมติเรากำลังเขียนฟังก์ชัน `f` เขียนโค้ดนิดหน่อย ลองทดสอบดู `f(1)` ใช้ได้ แต่ `f(2)` ไม่ทำงาน เราแก้โค้ดแล้วตอนนี้ `f(2)` ก็ใช้ได้แล้ว ดูเหมือนจะครบแล้วใช่ไหม แต่เราลืมย้อนกลับไปเทสต์ `f(1)` ใหม่ ซึ่งอาจจะมีข้อผิดพลาดโผล่ขึ้นมาก็ได้ -นี่เป็นเรื่องปกติมาก เวลาเราพัฒนาอะไร เรามักจะคิดถึงกรณีต่างๆ ที่อาจเกิดขึ้น แต่การคาดหวังให้นักเขียนโค้ดทดสอบทุกกรณีด้วยตัวเองหลังจากทุกการเปลี่ยนแปลง แต่มันเป็นไปได้ยาก ดังนั้น จึงง่ายที่จะแก้สิ่งหนึ่ง แล้วดันไปทำอีกสิ่งเสีย +นี่เป็นเรื่องปกติมาก ตอนที่เรากำลังพัฒนาอะไรบางอย่าง มักจะมีหลายกรณีการใช้งานที่เราต้องคำนึงถึง แต่การที่จะให้โปรแกรมเมอร์มานั่งเช็คทุกกรณีด้วยตัวเองทุกครั้งที่มีการเปลี่ยนแปลง เป็นเรื่องยาก เลยกลายเป็นว่าแก้ไขอย่างหนึ่ง แล้วไปทำให้อีกอย่างพังได้ง่าย -**การทดสอบอัตโนมัติ หมายถึงการเขียนทดสอบแยกต่างหากจากโค้ด พวกมันจะรันฟังก์ชันของเราในหลายๆ กรณี แล้วเปรียบเทียบผลลัพธ์กับที่คาดหวัง** +**การทดสอบอัตโนมัติหมายถึงการเขียนเทสต์แยกออกมาต่างหาก เป็นส่วนเสริมของโค้ด เทสต์เหล่านั้นจะรันฟังก์ชันของเราในหลากหลายวิธี และเปรียบเทียบผลลัพธ์กับสิ่งที่คาดหวัง** ## Behavior Driven Development (BDD) -เราจะเริ่มต้นด้วยเทคนิคที่เรียกว่า Behavior Driven Development: [http://en.wikipedia.org/wiki/Behavior-driven_development](http://en.wikipedia.org/wiki/Behavior-driven_development) หรือเรียกสั้นๆ ว่า BDD +เรามาเริ่มต้นด้วยเทคนิคที่เรียกว่า [Behavior Driven Development](http://en.wikipedia.org/wiki/Behavior-driven_development) หรือเรียกสั้นๆ ว่า BDD กัน -**BDD คือสามสิ่งในหนึ่งเดียว: ทดสอบ + เอกสาร + ตัวอย่าง** +**BDD คือ 3 สิ่งในหนึ่งเดียว ทั้งเทสต์ เอกสาร และตัวอย่างการใช้งาน** -เพื่อให้เข้าใจ BDD เราจะดูตัวอย่างการพัฒนาในทางปฏิบัติ +เพื่อทำความเข้าใจ BDD เราจะมาดูตัวอย่างกรณีศึกษาในการพัฒนาซอฟต์แวร์กัน -## ปฏิบัติการสร้าง "pow": รู้จัก "spec" ก่อน +## การพัฒนา "pow": ข้อกำหนด -สมมติว่าเราอยากสร้างฟังก์ชัน `pow(x, n)` ที่เอา x ยกกำลัง n โดยที่ n ต้องเป็นเลขเต็มบวกนะ +สมมติว่าเราอยากเขียนฟังก์ชัน `pow(x, n)` ที่คืนค่า `x` ยกกำลังด้วยเลขชี้กำลังจำนวนเต็ม `n` โดยเราสมมติว่า `n≥0` -ฟังดูน่าเบื่อใช่มั้ย? จริงๆ แล้วเจ้านี่มีตัว `**` ใน JavaScript ที่ทำได้อยู่แล้ว แต่เดี๋ยวก่อน! เราจะใช้ตัวอย่างนี้ฝึกฝนวิธีคิดในการสร้างฟังก์ชันที่ซับซ้อนกว่านี้ได้ด้วยนะ +จริงๆ แล้วนี่เป็นแค่ตัวอย่าง เพราะในจาวาสคริปต์มีตัวดำเนินการ `**` ที่ทำแบบนี้ได้อยู่แล้ว แต่ตรงนี้เราจะมุ่งเน้นไปที่วิธีการพัฒนาที่สามารถนำไปประยุกต์ใช้กับงานที่ซับซ้อนกว่านี้ได้ด้วย -ก่อนเขียนโค้ด `pow` เราต้องรู้จักมันก่อนว่ามันจะทำอะไรบ้าง และอธิบายมันออกมาเป็นคำพูด +ก่อนจะเขียนโค้ดของ `pow` เราสามารถจินตนาการถึงสิ่งที่ฟังก์ชันควรจะทำและบรรยายมันไว้ก่อนได้ -คำอธิบายนี้เรียกว่า *specification* หรือเรียกสั้นๆ ว่า *spec* ซึ่งจะรวมถึงวิธีการใช้งานและการทดสอบว่ามันทำงานถูกต้องหรือเปล่า - -```js -describe("pow", function() { - - it("ยกกำลัง n ได้", function() { - assert.equal(pow(2, 3), 8); - }); - -}); -``` - -spec จะมี 3 ส่วนหลักๆ ตามที่เห็นเลย: - -`describe("ชื่อ", function() { ... })` -: บอกเราว่ากำลังอธิบายอะไร ในกรณีนี้เรากำลังอธิบายฟังก์ชัน `pow` นั่นเอง เหมือนหัวข้อของกลุ่มคนงานที่กำลังจะมาทำงานในบล็อก `it` - -`it("คำอธิบายการใช้งาน", function() { ... })` -: ในหัวข้อของ `it` เราจะ *อธิบายด้วยภาษาคน* ว่าฟังก์ชันนี้ควรทำงานอย่างไรในสถานการณ์นั้นๆ และตัว argument ที่สองเป็นฟังก์ชันทดสอบ - -`assert.equal(ค่า1, ค่า2)` -: โค้ดภายในบล็อก `it` ถ้าการทำงานของฟังก์ชันถูกต้อง จะต้องทำงานโดยไม่มี error - - ฟังก์ชัน `assert.*` ใช้ตรวจสอบว่า `pow` ทำงานตามที่คาดหวังหรือไม่ ในตัวอย่างนี้เราใช้ `assert.equal` เพื่อเปรียบเทียบค่าสองค่า ถ้าไม่เท่ากัน จะมี error แสดงขึ้นมา ที่นี่เราทดสอบว่าผลลัพธ์ของ `pow(2, 3)` เท่ากับ `8` หรือเปล่า นอกจากนี้ยังมีฟังก์ชันอื่นๆ สำหรับการเปรียบเทียบและตรวจสอบ อันนี้ไว้ค่อยมาดูกันทีหลัง - -spec สามารถเรียกใช้เพื่อทดสอบฟังก์ชันของเราได้ เราจะมาดูวิธีการนั้นในตอนต่อไป! - -## ขั้นตอนการพัฒนา - -ขั้นตอนการพัฒนาโดยทั่วไปมีหน้าตาแบบนี้: - -1. เขียน spec เบื้องต้นพร้อมทดสอบสำหรับฟังก์ชันเบสิก -2. สร้างการทำงานเบื้องต้น -3. เพื่อตรวจสอบว่ามันทำงานหรือเปล่า เราจะรันเฟรมเวิร์คทดสอบ Mocha: [http://mochajs.org/](http://mochajs.org/) (เดี๋ยวจะอธิบายเพิ่มเติม) ซึ่งจะรัน spec ตอนนี้ฟังก์ชันยังไม่เสร็จ ก็อาจจะมี error ขึ้นมา เราก็แก้โค้ดไปเรื่อยๆ จนกว่ามันจะเวิร์ค -4. ตอนนี้เรามีการทำงานเบื้องต้นที่ใช้ได้พร้อมกับทดสอบแล้ว -5. เราเพิ่มกรณีการใช้งานเพิ่มเติมลงใน spec ซึ่งอาจจะยังไม่รองรับโดยการทำงานตอนนี้ ทดสอบก็จะล้มเหลวก่อน -6. กลับไปข้อ 3 อัปเดตการทำงานจนกระทั่งทดสอบผ่าน -7. ทำซ้ำข้อ 3-6 จนกว่าฟังก์ชันพร้อมใช้งาน - -ดังนั้นการพัฒนาเป็นแบบ *วนซ้ำ* เราเขียน spec ทำงาน ทดสอบ จนผ่าน แล้วก็เขียนทดสอบเพิ่ม ทำให้มันผ่านอีก จนสุดท้ายเราก็ได้ทั้งการทำงานที่ใช้ได้และทดสอบสำหรับมัน - -มาดูขั้นตอนการพัฒนานี้ในกรณีตัวอย่างของเรา - -ขั้นตอนแรกเสร็จไปแล้ว: เรามี spec เบื้องต้นสำหรับ `pow` ตอนนี้ก่อนที่จะสร้างการทำงาน ลองใช้ไลบรารี่ JavaScript สักสองสามตัวเพื่อรันทดสอบดู แค่เพื่อดูว่ามันทำงาน (ก็จะล้มเหลวหมดแหละ) - -## ทดสอบ spec กัน! - -ในบทช่วยสอนนี้ เราจะใช้ไลบรารี JavaScript สำหรับทดสอบดังต่อไปนี้: - -- Mocha: [http://mochajs.org/](http://mochajs.org/) -- เฟรมเวิร์คหลัก: มันให้ฟังก์ชันการทดสอบทั่วไปรวมถึง `describe` และ `it` และฟังก์ชันหลักที่รันทดสอบ -- Chai: [http://chaijs.com](http://chaijs.com) -- ไลบรารีที่มี assertions มากมาย มันช่วยให้ใช้ assertions ที่แตกต่างกันได้มากมาย ตอนนี้เราต้องการแค่ `assert.equal` -- Sinon: [http://sinonjs.org/](http://sinonjs.org/) -- ไลบรารีสำหรับสอดแนมฟังก์ชัน เลียนแบบฟังก์ชันในตัวและอื่นๆ เราจะใช้มันในภายหลัง - -ไลบรารีเหล่านี้เหมาะสำหรับการทดสอบทั้งในเบราว์เซอร์และฝั่งเซิร์ฟเวอร์ ในที่นี้เราจะพิจารณาตัวแปรเบราว์เซอร์ - -หน้า HTML เต็มรูปแบบพร้อมเฟรมเวิร์คเหล่านี้และ spec ของ `pow`: - -```html src="index.html" -``` - -หน้าแบ่งออกเป็น 5 ส่วน: - -1. `` -- เพิ่มไลบรารีของบุคคลที่สามและสไตล์สำหรับการทดสอบ -2. `