ศูนย์รวมความรู้วิศวกรรมซอฟต์แวร์

แบ่งปันประสบการณ์การออกแบบสถาปัตยกรรมระบบ, การเขียนโค้ดด้วย Laravel และการจัดการ Server ระดับ Production เพื่อยกระดับทักษะของนักพัฒนาทุกคน

เขียน Feature Test ใน Laravel สำหรับเชื่อมต่อ Oracle Database (บทที่ 3: Act — ลงมือยิง POST Request)

เขียน Feature Test ใน Laravel สำหรับเชื่อมต่อ Oracle Database


หลังจากที่เราจัดเตรียมข้อมูล (Arrange) ตัวละครอย่าง User และ Payload ฟอร์มเรียบร้อยแล้ว ในบทนี้เราจะเข้าสู่ขั้นตอน Act หรือการ "ลงมือกระทำ" โดยจำลองการกด Submit ฟอร์มเพื่อส่งข้อมูลเข้าระบบจริงกันครับ

เป้าหมายของบทนี้: ส่ง Payload เข้าไปใน Flow การทำงานจริงของระบบ แล้วเก็บผลลัพธ์ (Response) ไว้ เพื่อเตรียมนำไปตรวจเช็ค (Assert) ในบทถัดไป

1. คำสั่งหลักประจำบท (The Core Command)

นำคำสั่งนี้ไปวางต่อจากส่วน Arrange ใน Test Method ของคุณ:
// ----- [ Act ] -----
$response = $this->post('/stock/store', $payload);

แยกส่วนประกอบคำสั่ง:
  • $this->post(...) : คือการสั่งให้ Feature Test จำลองพฤติกรรมของ Browser ในการยิง HTTP POST Request
  • '/stock/store' : คือ URL Path ปลายทาง (อิงตามไฟล์ routes/rmqc.php) ข้อสังเกต: เราใส่ Path ตรงๆ เพราะ Route นี้อาจจะไม่ได้ตั้งชื่อ (ไม่มี ->name()) ไว้
  • $payload : ตัวแปร Array ข้อมูลฟอร์มที่เราเตรียมไว้จากบทที่ 2
  • $response : ตัวแปรสำหรับเก็บ "ผลลัพธ์ทั้งก้อน" ที่ระบบตอบกลับมา (เช่น Status Code, การ Redirect, ข้อมูลใน Session) เพื่อนำไปใช้ต่อ

2. เกิดอะไรขึ้น "หลังบ้าน" เมื่อรันคำสั่งนี้? (Laravel Lifecycle)

สิ่งสำคัญที่ต้องรู้คือ การทำ Act ไม่ใช่การที่เราไปเรียก new SampleController() ด้วยตัวเอง แต่ HTTP Helper ของ Laravel จะจำลองการวิ่งผ่านระบบเต็มรูปแบบตามลำดับดังนี้:

  1. 📍 หา Route ปลายทาง: ระบบวิ่งไปที่ SampleController@store

  2. 🛡️ ผ่านด่าน FormRequest (StoreStockRequest):

    • ถ้า Rules ผ่าน: ได้ไปต่อที่ Controller

    • ถ้า Rules ไม่ผ่าน: ระบบจะเตะ Redirect กลับทันทีพร้อมแนบ Errors (ยังไม่ทันเข้าฟังก์ชัน store)

  3. ⚙️ ประมวลผลใน store():

    • ดึง Auth::user()->id (จำได้ไหม? นี่คือผลลัพธ์จาก actingAs ในบทที่ 2)

    • แปลง Request เป็น Data Transfer Object ($request->toDto())

    • ส่งเข้า Action (createAction->execute()) เพื่อต่อ Oracle และ Insert ข้อมูลลงตาราง rmqc_inventory

  4. 🔄 ส่งผลลัพธ์กลับ: Controller สั่ง return redirect()->to(url('stock/edit', $id))->with('message', '...')

  5. 📦 เก็บลงกล่อง $response: นำทุกอย่างแพ็กรวมกันให้เราพร้อมนำไป Assert

3. อย่าลืมถอดเบรก! (ลบ markTestIncomplete)

หากในโค้ดเดิมคุณมีบรรทัด $this->markTestIncomplete('...'); ดักอยู่ ให้ลบออกได้เลย เพราะถ้าคุณวางคำสั่งนี้ไว้ ก่อน ขั้นตอน Act เทสจะหยุดทำงานและไม่ยอมยิง POST ให้คุณ

(คำแนะนำ: ระหว่างเรียนและเขียนเทส แนะนำให้ลบ Incomplete ออกไปเลยแล้วลุยเขียน Assert ในบทที่ 4 ต่อทันที)

4. โค้ดรวมหน้าตา Test Method หลังจบบทที่ 3
public function test_store_creates_inventory_and_redirects_to_edit(): void
{
    // ----- [ Arrange ] (จากบทที่ 2) -----
    $user = User::query()->first();
    if (!$user) {
        $this->markTestSkipped('ไม่มี user ใน rmqc_ap_users');
    }
    $this->actingAs($user);

    $scheme = Scheme::query()->where('status', 1)->first();
    if (!$scheme) {
        $this->markTestSkipped('ไม่มี scheme status=1');
    }

    $payload = [
        'scheme_id'   => $scheme->id,
        'fiscal_year' => 2569,
        'lot'         => 1,
        'exam_no'     => 1,
        'volumn'      => '100 ml',
        'qty'         => 10,
        'expire_date' => '2027-12-31',
        'status'      => 1,
    ];

    $expectedCode = "{$scheme->code}-{$payload['fiscal_year']}-{$payload['lot']}";

    // ----- [ Act ] (จากบทที่ 3) -----
    $response = $this->post('/stock/store', $payload);

    // 💡 ทริคสำหรับการ Debug (เปิดใช้ชั่วคราวเพื่อดูข้อมูล แล้วลบออกก่อน Commit):
    // $response->dump();
    // $response->dumpSession();
    // $this->withoutExceptionHandling(); 

    // ----- [ Assert ] (รอติดตามในบทที่ 4) -----
}

5. คู่มือแก้ปัญหาฉบับเร่งด่วน (Troubleshooting)

ถ้ารัน vendor/bin/phpunit --filter=SampleControllerStoreTest แล้วตัวหนังสือแดงแจ๋น อย่าเพิ่งตกใจ ลองเทียบอาการตามตารางนี้:
อาการที่พบ (Status Code)      | สาเหตุที่พบบ่อย                    | แนวทางแก้ไข & วิธีตรวจสอบ
404 Not Found
                  | Path ผิด หรือ Route โหลดไม่ขึ้น | เช็คว่าพิมพ์ '/stock/store' ถูกต้อง และมีอยู่ใน routes
302 Redirect ไปหน้า Login    | Middleware ยังบล็อกอยู่         | กลับไปเช็คฟังก์ชัน setUp() ว่ามี $this->withoutMiddleware() หรือยัง
302 Redirect กลับ + มี Errors | ส่ง Payload ไม่ผ่าน Validation  | ใส่ $response->dumpSession(); เพื่อดูว่าฟิลด์ไหนผิด Rule
500 Internal Server Error    | โค้ดพัง หรือ Database มีปัญหา  | ใส่ $this->withoutExceptionHandling(); ก่อนหน้า Act เพื่อให้โชว์ Error จริงๆ ออกมา
เทสค้าง ช้ามากจน Timeout     | เชื่อมต่อ Oracle ไม่ได้              | เช็คการเชื่อมต่อ VPN หรือค่า Host ในไฟล์ .env


laravel