# Unit Testing with Jest - Example Project

## เป้าหมายของวันนี้

1. เข้าใจว่า Unit Testing คืออะไร
2. รันโปรแกรมการทดสอบได้
3. เข้าใจว่า test ทำงานยังไง

---

## ขั้นตอนที่ 1: ดูว่ามีไฟล์อะไรบ้าง

เปิดโฟลเดอร์ `example-project` ใน VS Code เพื่อดูไฟล์ทั้งหมด

**ที่เราจะเห็น:**

```
example-project/
├── src/                    ← 📁 โฟลเดอร์ที่ 1
│   ├── calculator.js       ← ไฟล์โค้ดปกติ
│   └── stringUtils.js      ← ไฟล์โค้ดปกติ
│
├── __tests__/              ← 📁 โฟลเดอร์ที่ 2
│   ├── calculator.test.js  ← ไฟล์สำหรับทดสอบ
│   └── stringUtils.test.js ← ไฟล์สำหรับทดสอบ
│
├── package.json            ← ไฟล์ข้อมูลโปรเจค
└── jest.config.js          ← ไฟล์ตั้งค่า
```

**อธิบาย:**

- **`src/`** = โปรแกรมที่เราเขียนจริง ๆ
- **`__tests__/`** = โปรแกรมสำหรับตรวจสอบว่าโปรแกรม src ทำงานถูกไหม

---

## 📄 ขั้นตอนที่ 2: เปิดไฟล์ `src/calculator.js` และอ่าน

**คลิกไฟล์:**

```
src/calculator.js
```

**จะเห็นโค้ดแบบนี้:**

```javascript
function add(a, b) {
  return a + b;
}

function subtract(a, b) {
  return a - b;
}

function multiply(a, b) {
  return a * b;
}

function divide(a, b) {
  if (b === 0) throw new Error("Division by zero");
  return a / b;
}

module.exports = { add, subtract, multiply, divide };
```

**อธิบายทีละบรรทัด:**

```javascript
// ฟังก์ชันที่ 1 - บวก
function add(a, b) {
  // ← รับเลข 2 ตัว (a กับ b)
  return a + b; // ← บวกกันแล้วส่งผลลัพธ์ออกไป
} // ← ตัวอย่าง: add(2, 3) = 5
```

```javascript
// ฟังก์ชันที่ 4 - หาร
function divide(a, b) {
  // ← รับเลข 2 ตัว
  if (b === 0) throw new Error("Division by zero"); // ← ถ้า b = 0 ให้ยกขึ้น error
  return a / b; // ← ถ้า b ≠ 0 ให้หารได้
}
// ตัวอย่าง: divide(10, 2) = 5 ✓
// ตัวอย่าง: divide(10, 0) = ERROR ❌
```

**สรุป:** นี่คือโปรแกรมปกติ 4 ฟังก์ชันสำหรับบวก ลบ คูณ หาร

---

## ขั้นตอนที่ 3: เปิดไฟล์ `__tests__/calculator.test.js` และอ่าน

**คลิกไฟล์:**

```
__tests__/calculator.test.js
```

**จะเห็นโค้ดแบบนี้:**

```javascript
const { add, subtract, multiply, divide } = require("../src/calculator");

describe("Calculator Functions", () => {
  describe("add()", () => {
    it("should add 2 + 3 = 5", () => {
      expect(add(2, 3)).toBe(5);
    });
  });
});
```

**อธิบายทีละบรรทัด:**

### บรรทัด 1: นำเข้าฟังก์ชัน

```javascript
const { add, subtract, multiply, divide } = require("../src/calculator");
//                                           ↑
//                    บอก: "ไปเอาฟังก์ชันจากไฟล์นี้มา"
//                    ../src/calculator = ไฟล์ src/calculator.js
```

**ความหมาย:**

- ต้องการใช้ฟังก์ชัน `add` จากไฟล์ `src/calculator.js`
- ต้องนำเข้าให้ก่อนใช้งาน

### บรรทัด 3: เริ่มกลุ่มทดสอบ

```javascript
describe("Calculator Functions", () => {
  // ← กลุ่มทดสอบที่ชื่อ "Calculator Functions"
  // ← การทดสอบทั้งหมดจะวางไว้ข้างใน { }
```

### บรรทัด 4-5: กลุ่มทดสอบย่อย

```javascript
  describe("add()", () => {
    // ← กลุ่มทดสอบเฉพาะสำหรับฟังก์ชัน add
```

### บรรทัด 6: การทดสอบเดี่ยว

```javascript
    it("should add 2 + 3 = 5", () => {
    // ↑ it ← หมายถึง "ลองทำอะไรสักอย่าง"
    // ↓ ชื่อของการทดสอบ (อธิบายว่าเราทำอะไร)
```

### บรรทัด 7: ตรวจสอบผลลัพธ์ (สำคัญที่สุด!)

```javascript
expect(add(2, 3)).toBe(5);
//     ↑                ↑
//   เรียก add(2,3)    ต้องเท่ากับ 5
```

**ความหมาย:**

- รัน `add(2, 3)` ได้ผลลัพธ์อะไร?
- ตรวจสอบว่าผลลัพธ์เป็น `5` หรือไม่?
  - ถ้าใช่ → TEST PASS
  - ถ้าไม่ → ❌ TEST FAIL

---

## ขั้นตอนที่ 4: รันการทดสอบ

### หลังเปิด Terminal ใน VS Code:

**คำสั่ง:**

```bash
cd example-project
npm install
npm test
```

### ขั้นตอนย่อย:

#### ขั้น 1: เข้าโฟลเดอร์

```bash
cd example-project
```

- พิมพ์คำสั่งแล้วกด Enter
- ตอนนี้ terminal อยู่ในโฟลเดอร์ example-project แล้ว

#### ขั้น 2: ติดตั้งไลบรารี่

```bash
npm install
```

- Jest ต้องติดตั้งก่อนใช้
- รอจนเสร็จ (อาจใช้เวลา 10-30 วินาที)
- เมื่อเสร็จจะมีโฟลเดอร์ `node_modules/` สร้างขึ้นมา

#### ขั้น 3: รันการทดสอบ

```bash
npm test
```

- เรียก Jest ให้ทำงาน
- Jest จะหาไฟล์ `.test.js` ทั้งหมด
- รันการทดสอบแต่ละไฟล์

**ผลลัพธ์ที่คาดหวัง:**

```
 PASS  __tests__/calculator.test.js
  Calculator Functions
    add()
      ✓ should add 2 + 3 = 5 (2 ms)
      ✓ should add negative numbers (-3 ms)
    ✓ subtract()
    ✓ multiply()
    ✓ divide()

Test Suites: 2 passed, 2 total
Tests:       9 passed, 9 total
```

**ความหมาย:**

- ✓ = ผ่านการทดสอบ ✅
- ตัวเลข = ใช้เวลากี่มิลลิวินาที

---

## ขั้นตอนที่ 5: ทำความเข้าใจ Test ที่เขียน

### ลองอ่านทีละบรรทัด:

**Test ที่ 1:**

```javascript
it("should add 2 + 3 = 5", () => {
  expect(add(2, 3)).toBe(5);
});
```

**ความหมาย:**

- "ลองเรียก add(2, 3) ได้ผลลัพธ์เท่ากับ 5 หรือไม่"
- ผ่าน เพราะ 2 + 3 = 5 จริง

**Test ที่ 2:**

```javascript
it("should add negative numbers", () => {
  expect(add(-2, -3)).toBe(-5);
});
```

**ความหมาย:**

- "ลองเรียก add(-2, -3) ได้ผลลัพธ์เท่ากับ -5 หรือไม่"
- ผ่าน เพราะ -2 + (-3) = -5 จริง

---

## ขั้นตอนที่ 6: ลองแก้ไขแล้วดูเกิดอะไรขึ้น

**จุดประสงค์:** เข้าใจว่า test ตรวจสอบอะไร

### ทดลองที่ 1: แก้ไขโค้ด calculator

**(1) เปิด `src/calculator.js`**

**(2) ค้นหา function `add` แล้วแก้ไข:**

```javascript
// เดิม:
function add(a, b) {
  return a + b;
}

// เปลี่ยนเป็น:
function add(a, b) {
  return a + b + 1; // ← เพิ่ม + 1
}
```

**(3) บันทึก (Ctrl + S)**

**(4) รัน test อีกครั้ง:**

```bash
npm test
```

**ผลลัพธ์:**

```
✗ should add 2 + 3 = 5
  Expected: 5
  Received: 6   ← เพราะ 2 + 3 + 1 = 6
```

**สรุป:** Test ตรวจสอบให้เห็นว่าเราแก้ไขโค้ดผิด! 🎉

### ทดลองที่ 2: แก้กลับเป็นปกติ

**(1) เปิด `src/calculator.js`**

**(2) เปลี่ยนกลับ:**

```javascript
function add(a, b) {
  return a + b; // ← ลบ + 1 ออก
}
```

**(3) รัน test:**

```bash
npm test
```

**ผลลัพธ์:**

```
✓ should add 2 + 3 = 5  ← ผ่านอีกครั้ง
```

---

## ขั้นตอนที่ 7: อ่านไฟล์คำสั่ง `package.json`

**เปิด `package.json`:**

```json
{
  "name": "jest-example",
  "version": "1.0.0",
  "description": "Jest Unit Testing Example",
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  },
  "devDependencies": {
    "jest": "^29.0.0"
  }
}
```

**อธิบาย:**

```json
"scripts": {
  "test": "jest",           // npm test          → รัน jest 1 ครั้ง
  "test:watch": "jest --watch",    // npm run test:watch → รัน jest ตลอดเวลา
  "test:coverage": "jest --coverage"  // npm run test:coverage → ดู coverage
}
```

**ความหมาย:**

- ข้างใน `scripts` คือคำสั่งที่เราสร้างเอง
- เมื่อพิมพ์ `npm test` → Jest จะรัน
- เมื่อพิมพ์ `npm run test:watch` → Jest จะดู code ตลอดและทดสอบทุกครั้งที่เปลี่ยน

---

## ขั้นตอนที่ 8: ลองเองทำ Test สำหรับ String

### ข้อมูล String Functions:

**ไฟล์ `src/stringUtils.js`:**

```javascript
function capitalize(str) {
  if (typeof str !== "string") throw new Error("Input must be string");
  return str.charAt(0).toUpperCase() + str.slice(1);
}
```

**ความหมาย:**

- ทำให้อักษรตัวแรกเป็นใหญ่
- ตัวอย่าง: `"hello"` → `"Hello"`

### Test สำหรับ Capitalize:

**ไฟล์ `__tests__/stringUtils.test.js` บางส่วน:**

```javascript
describe("capitalize()", () => {
  it("should capitalize first letter", () => {
    expect(capitalize("hello")).toBe("Hello");
    //                           ↑ อักษรตัวแรก H ใหญ่
  });

  it("should handle already capitalized strings", () => {
    expect(capitalize("Hello")).toBe("Hello");
    //                           ↑ เดิมใหญ่อยู่แล้ว
  });
});
```

---

## ขั้นตอนที่ 9: ทำความเข้าใจ `expect()` และ `toBe()`

นี่คือสิ่งสำคัญที่สุด! ⭐⭐⭐

### `expect()` = กล่องใส่ผลลัพธ์

```javascript
expect(add(2, 3));
//     ↑ เรียก add(2,3)
//     ↑ ผลลัพธ์ = 5
//     ↑ ใส่ 5 ลงไปใน expect()
```

### `toBe()` = เปรียบเทียบ

```javascript
expect(5).toBe(5);
//        ↑ เปรียบเทียบ
//        ↑ 5 = 5 ? ใช่! ✅
```

### อื่น ๆ ที่มี:

```javascript
expect(value).toBe(5); // = ตรวจสอบว่าเท่ากับ 5
expect(value).not.toBe(5); // = ตรวจสอบว่า ≠ 5
expect(array).toContain(3); // = ตรวจสอบว่ามี 3 ใน array
expect(() => fn()).toThrow(); // = ตรวจสอบว่าจะ error
expect(true).toBeTruthy(); // = ตรวจสอบว่า true
expect(false).toBeFalsy(); // = ตรวจสอบว่า false
```

---

## ขั้นตอนที่ 10: ลองสร้าง Test สำหรับ Function ใหม่

### โครงสร้าง Test ขั้นพื้นฐาน:

```javascript
const { functionName } = require("../src/filename");

describe("Function Name", () => {
  it("should do something", () => {
    expect(functionName(input)).toBe(expectedOutput);
  });
});
```

### ตัวอย่าง:

```javascript
const { add } = require("../src/calculator");

describe("add() - Test ของฉัน", () => {
  it("should add 10 + 5 = 15", () => {
    expect(add(10, 5)).toBe(15);
  });

  it("should add 0 + 0 = 0", () => {
    expect(add(0, 0)).toBe(0);
  });

  it("should add large numbers", () => {
    expect(add(1000, 2000)).toBe(3000);
  });
});
```

---

## ขั้นตอนที่ 11: ดู Coverage Report

**Coverage** = เปอร์เซนต์ของโค้ดที่ถูกทดสอบแล้ว

### รันคำสั่ง:

```bash
npm run test:coverage
```

**ผลลัพธ์:**

```
-----------|---------|---------|---------|---------|
File       |  % Stmts | % Branch| % Funcs | % Lines |
-----------|---------|---------|---------|---------|
All files  |     100 |     100 |     100 |     100 |
-----------|---------|---------|---------|---------|
```

**ความหมาย:**

- 100% = โค้ด 100% ถูกทดสอบแล้ว ✅
- ถ้า 80% = ยังมี 20% ที่ไม่ได้ทดสอบ ⚠️

---

## โบนัส: ทำความเข้าใจ `module.exports`

**ในไฟล์ `src/calculator.js`:**

```javascript
module.exports = { add, subtract, multiply, divide };
//                 ↑ ส่งออกฟังก์ชันเหล่านี้ให้ใครอยากใช้เอา
```

**ในไฟล์ `__tests__/calculator.test.js`:**

```javascript
const { add, subtract, multiply, divide } = require("../src/calculator");
//                                           ↑ ไปเอาจากไฟล์นี้มา
```

**การทำงาน:**

```
src/calculator.js: exports ออก → test ไฟล์: imports เข้า
     (ให้เอา)                      (เอาไป)
```

---

## สรุป: ขั้นตอน 11 ขั้นเสร็จแล้ว!

ตอนนี้เราควรเข้าใจ:

- [ ] ว่า example-project มีไฟล์อะไรบ้าง
- [ ] ว่า `src/` คือโค้ดปกติ
- [ ] ว่า `__tests__/` คือโค้ดทดสอบ
- [ ] ว่า test ทำงานยังไง (`expect()` + `toBe()`)
- [ ] วิธีรัน test (`npm test`)
- [ ] วิธีแก้ไขโค้ดแล้อดู test ล้มหรือไม่
- [ ] ว่า `module.exports` และ `require()` ทำไม

---

## ขั้นตอนต่อไป

1. **ลองเขียน Test เอง:**
   - สร้างไฟล์ `__tests__/myTest.test.js`
   - เขียน test บางอย่าง
   - รัน `npm test`

2. **ลองแก้ไขโค้ด:**
   - เปลี่ยนตัวเลข
   - เพิ่มฟังก์ชันใหม่
   - ดู test ยังผ่านหรือไม่

3. **อ่านเอกสารประกอบ:**
   - อ่าน unit-testing-jest.md เพื่อเข้าใจแนวคิดลึก ๆ

---
