Jest — Javascript Testing on ES6/React
จะพาไปรู้จัก Jest — Testing Library จากค่าย Facebook
มีตัวช่วยครบครั้นสำหรับ Javascript รวมถึง UI อย่าง React
ที่จะช่วย Improve Quality ของการ Coding เราได้อีกด้วย
Installation
Example source code: github.com/ttanachot/jest-test
ทำการ setup dependencies ให้เราสามารถใช้ Jest กับ ES6 / React
- jest — Testing library for Javascript
- babel — Transpile syntax JS เราให้เป็น ES6 โดยมี preset หลายแบบตามสิ่งที่เราใช้งาน
- regenerator-runtime — A small runtime library จาก Facebook ที่ช่วย wrap generator ไว้ข้างหลังให้เรา
- enzyme — ตัวช่วยทำ testing ของ React
อย่าลืมสร้าง config ไว้ให้ .babelrc ด้วย จะช่วยเรา unlocked feature ของ ES6 แล้วยังช่วยเรา transform runtime ด้วย
/* .babelrc */{
"presets": ["env", "react", "stage-0"],
"plugins": [
["transform-runtime", {
"polyfill": false,
"regenerator": true
}]
]
}
ในไฟล์ package.json ใส่ให้ scripts ของเราใช้ test เป็น jest รวมถึงใช้ build-in feature ที่บอก code coverage ให้เรา แนะนำให้ใช้เลย ดีงาม
{
"scripts": {
"test": "jest --coverage"
}
}
Example
เริ่มด้วยเรามีฟังก์ชั่น isPrime โดยที่เราอยากลองจะเขียน testcase อย่างง่าย
สร้างอีกไฟล์หนึ่งขึ้นมา util.test.js ไว้ในทำการ import ไฟล์ util.js เข้ามา
- เขียนเป็น test-suite ที่รวมเคสที่เกี่ยวเนื่องกันไว้ได้ด้วย
describe('Test suite', () => { ... });
- เขียนแบบ standalone ได้เหมือนกัน
test('should get result correctly', () => { ... });
- มือใหม่ให้ คิดเคสง่ายก่อนแค่ลองดู input/output
แล้วมาลองรัน terminal ด้วย npm run test
เราจะเห็นได้ว่า test suite ที่เราเขียนขึ้นมาใช้งานได้เลยทันที มี Code coverage มาให้ด้วย กี่เปอร์เซ็น ตรงไหนยังไปไม่ถึง ใช้เวลาเท่าไหร่ บอกเรามาหมด
ถัดไปด้านล่างคือสิ่งที่ Jest ช่วยในการทำ Testing ของเราให้ใช้งานได้ง่ายขึ้น
Matcher
พื้นฐานของการเขียน Testing ก็คือการเทียบดูว่า result ตรงกับสิ่งที่คาดหวังไว้มั้ย
Jest ก็มี matcher พื้นฐานในการเทียบค่าให้เรามาแล้ว ไม่ว่าจะเทียบค่าตรงๆ หรือจะเป็น regex รวมทั้ง boolean
expect(result).toBe('result');
expect(result).toMatch(/test/);
expect(result).not.toBeNull();
expect(result).toBeThruthy();
expect(result).toBeFalsy();
ลองกับ array ของ object แบบ advance ขึ้นมาหน่อย
รวมถึงการเช็คแบบที่เราไม่ได้สนใจ value เลยก็ได้ขอแค่มีอยู่ก็พอแล้ว
expect(result).toBe(expect.arrayContaining(expected));
สามารถดูเพิ่มเติมได้ที่ : jest-using-matchers
Asynchronous things
ถ้าเรา fetch ใช้ async/await ช่วยจัดการได้เลย
ซึ่งจะช่วยลด LOC ได้ไปพอสมควรเลยนะ ไปดูตัวอย่างกัน
ถ้าเกิดว่าถ้า fetchUsers ไม่ได้มีปัญหาอะไรก็จะ resolves ได้ปกติ
หรือมีปัญหาในเรื่อง request แล้วไม่ได้ก็จะ rejects ไปเลย แถมดัก exception ได้ด้วย
Mock Function
บางที่การสร้างฟังก์ชั่นของเรามีการใช้งาน
- Read/Write database
- ไม่ว่าจะเป็น repository หรือจะ ORM library
- ส่วนที่จำเป็นต้องใช้ network ที่มีการ request/response
โดยรวมเรามองว่ามันเป็น side-effect เราสามารถใช้ Mock เข้ามาเป็นตัวช่วยได้โดยที่เราสามารถ override ให้ result เป็นแบบที่เราต้องการได้เลย
Unit Testing — ES6
Idea ของการเช็คคือเราจะดูว่า steps ในการเรียกฟังก์ชั่นนั้นถูกมั้ย
โดยการเช็คว่า mockFunction นั้นทำงานถูกมั้ย เรียกกี่ครั้งexpect(mockFunction).toHaveBeenCalledTime(1)
เรายังสามารถเช็คได้ว่า parameters ที่ส่งไปได้รับจริงๆมั้ยexpect(mockFunction).toHaveBeenCalledWith(args1, agrs2, ...);
โดยเรา override ทั้ง node_modules ของ node-fetch ให้เป็น mock ไปซะเลย
UI Testing — React
ขอยกเป็นตัวอย่างง่ายๆ โดยมี React Component ที่เป็น checkbox เมื่อเรากดเลือกก็จะนำ Text ที่เรา set ไว้เป็น props โชว์ขึ้นมา
เรายังมีตัวช่วยในการทำ Test UI กับ React ใช้ library enzyme จากค่าย AirBnB เป็น container ช่วยให้เรา render component แบบ simulate จนไปถึง children
เนื่องจาก React v.16+ จำเป็นต้องใช้ Adapter แปลงให้ backward compatible อาจจะต้องมีการ configuration เพิ่มเติม
ดูเพิ่มเติมได้ที่ : airbnb.io/enzyme/
Summary
การทำ Testing จุดประสงค์หลักคือ การช่วยลดปริมาณ Bug และช่วย Scope การ Coverage ของ Code ที่ถูกเขียนขึ้นมาก่อน โดยเป็นหลักประกันว่าจะช่วยลดปัญหาที่เคยเจอได้อีกด้วย
แน่นอนว่าคุณภาพของ Code จะดีขึ้น และ Maintainance รวมทั้ง Implementation จะค่อนข้าง Flow ได้ดีกว่าการไม่มี Testing ซึ่งแปลว่ามันคือการวางฐานต่อไปในอนาคตได้เร็วขึ้น ช่วยลด cost ทั้งด้าน money และ time โดยที่การเปลี่ยนแปลงของ business requirements อาจจะกระทบ Developer อย่างเราน้อยลง
ไว้เจอกันใหม่ ขอบคุณที่ติดตามครับ