<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Thanawat Wongchai</title>
    <description>The latest articles on Forem by Thanawat Wongchai (@thanawat_wonchai).</description>
    <link>https://forem.com/thanawat_wonchai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3821627%2F91172568-3a21-4fd5-9802-0ebae2983f0a.png</url>
      <title>Forem: Thanawat Wongchai</title>
      <link>https://forem.com/thanawat_wonchai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/thanawat_wonchai"/>
    <language>en</language>
    <item>
      <title>วิธีจำลอง API สำหรับการทดสอบ: คู่มือฉบับใช้งานจริง</title>
      <dc:creator>Thanawat Wongchai</dc:creator>
      <pubDate>Fri, 22 May 2026 07:37:00 +0000</pubDate>
      <link>https://forem.com/thanawat_wonchai/withiicchamlng-api-samhrabkaarthdsb-khuumuuechbabaichngaancchring-531j</link>
      <guid>https://forem.com/thanawat_wonchai/withiicchamlng-api-samhrabkaarthdsb-khuumuuechbabaichngaancchring-531j</guid>
      <description>&lt;p&gt;การทดสอบที่พึ่งพา API จริงมักล้มเหลวด้วยเหตุผลที่ไม่เกี่ยวกับโค้ดของคุณ เช่น staging ล่ม, third-party rate limit, หรือข้อมูลถูกเปลี่ยนโดยทีมอื่น การจำลอง API (API mocking) แก้ปัญหานี้โดยแทนที่ endpoint จริงด้วย endpoint ที่ควบคุมได้ และส่ง response ตามที่คุณกำหนดทุกครั้ง&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com/?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation" class="crayons-btn crayons-btn--primary"&gt;ลองใช้ Apidog วันนี้&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;คู่มือนี้แสดง workflow แบบลงมือทำสำหรับการจำลอง API เพื่อใช้ในการทดสอบ: กำหนด schema, สร้าง mock response, รัน mock server, ชี้ test ไปยัง mock URL และทดสอบ error path ที่ API จริงมักไม่สร้างให้ตามต้องการ ตัวอย่างใช้ API จัดการคำสั่งซื้อขนาดเล็ก แต่แนวทางเดียวกันใช้ได้กับ REST หรือ GraphQL API ส่วนใหญ่&lt;/p&gt;

&lt;h2&gt;
  
  
  เมื่อการจำลอง API เป็นทางเลือกที่ถูกต้อง
&lt;/h2&gt;

&lt;p&gt;ใช้ API mocking เมื่อเป้าหมายคือทดสอบโค้ดของคุณเอง ไม่ใช่ทดสอบเครือข่ายหรือ availability ของ service จริง เช่น unit test และ integration test ที่ต้องตรวจว่า client:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;parse response &lt;code&gt;200&lt;/code&gt; ได้ถูกต้อง&lt;/li&gt;
&lt;li&gt;retry เมื่อเจอ &lt;code&gt;503&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;แสดง error ที่เข้าใจได้เมื่อเจอ &lt;code&gt;404&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;handle timeout หรือ malformed response ได้ปลอดภัย&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แต่ไม่ควร mock ทุกอย่างเสมอไป ให้เก็บ test จำนวนเล็กน้อยสำหรับ contract test และ end-to-end test ที่เรียก API จริง เพื่อยืนยันว่า mock ยังตรงกับ production contract อยู่&lt;/p&gt;

&lt;p&gt;หลักคิดสั้นๆ คือ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mock&lt;/strong&gt; เพื่อความเร็ว ความเสถียร และ isolation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ใช้ API จริง&lt;/strong&gt; เพื่อยืนยัน contract&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;อ่านเพิ่มได้ที่ &lt;a href="http://apidog.com/blog/api-mocking-use-cases?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;สถานการณ์ที่การจำลอง API ได้ผล&lt;/a&gt; และ &lt;a href="http://apidog.com/blog/mock-server-vs-real-server?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;เซิร์ฟเวอร์จำลองกับเซิร์ฟเวอร์จริง&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflow 5 ขั้นตอนสำหรับ API Mocking
&lt;/h2&gt;

&lt;p&gt;ไม่ว่าจะใช้ภาษาใดหรือ test framework ใด ขั้นตอนหลักเหมือนกัน:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;กำหนด schema&lt;/strong&gt; เพื่อให้ mock response ตรงกับรูปแบบจริง&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;สร้าง mock response&lt;/strong&gt; แบบ static หรือ dynamic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;รัน mock server&lt;/strong&gt; เพื่อ expose response ผ่าน URL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ชี้ test ไปยัง mock server&lt;/strong&gt; โดยทำให้ base URL configurable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ทดสอบ error path&lt;/strong&gt; เช่น &lt;code&gt;404&lt;/code&gt;, &lt;code&gt;429&lt;/code&gt;, &lt;code&gt;500&lt;/code&gt;, timeout และ malformed JSON&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ตัวอย่างในบทความนี้ใช้ endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /orders/{id}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ขั้นตอนที่ 1: กำหนด Schema
&lt;/h2&gt;

&lt;p&gt;Mock จะมีประโยชน์ก็ต่อเมื่อ response shape ใกล้เคียงของจริง เริ่มจาก schema ก่อน หากมีเอกสาร &lt;a href="https://spec.openapis.org/oas/latest.html" rel="noopener noreferrer"&gt;OpenAPI&lt;/a&gt; อยู่แล้ว ให้ใช้ไฟล์นั้น ถ้ายังไม่มี ให้เขียนเฉพาะ endpoint ที่ต้องทดสอบก่อน&lt;/p&gt;

&lt;p&gt;ตัวอย่าง schema สำหรับ &lt;code&gt;GET /orders/{id}&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;/orders/{id}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;string&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;200'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
                &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;string&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
                  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;enum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pending&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;shipped&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;delivered&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
                  &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;number&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
                  &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;array&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;object&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;404'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Order not found&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schema ทำหน้าที่สองอย่าง:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;บอก mock server ว่าควรสร้าง field อะไร&lt;/li&gt;
&lt;li&gt;เป็น single source of truth ของ contract ระหว่าง frontend/client กับ backend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;เมื่อ backend เปลี่ยน contract เช่น เปลี่ยน &lt;code&gt;total&lt;/code&gt; เป็น &lt;code&gt;amount&lt;/code&gt; คุณต้อง update schema แล้ว regenerate หรือ update mock ให้ตรงกัน แนวทาง schema-first นี้ช่วยให้ &lt;a href="http://apidog.com/blog/api-contract-testing?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การทดสอบสัญญา API&lt;/a&gt; น่าเชื่อถือขึ้น&lt;/p&gt;

&lt;h2&gt;
  
  
  ขั้นตอนที่ 2: สร้าง Mock Response
&lt;/h2&gt;

&lt;p&gt;การสร้าง response มีสองแนวทางหลัก&lt;/p&gt;

&lt;h3&gt;
  
  
  Static response
&lt;/h3&gt;

&lt;p&gt;Static response คือ JSON ที่กำหนดตายตัว เหมาะกับ test ที่ต้องการ assert ค่าแน่นอน&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"order_8842"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shipped"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;149.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sku_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Keyboard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sku_002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mouse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เหมาะสำหรับ test เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;client แสดงสถานะ &lt;code&gt;shipped&lt;/code&gt; ถูกต้อง&lt;/li&gt;
&lt;li&gt;total ถูก format เป็นสกุลเงิน&lt;/li&gt;
&lt;li&gt;items ถูก render ครบ&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Dynamic response
&lt;/h3&gt;

&lt;p&gt;Dynamic response ถูกสร้างตาม request เช่น generate &lt;code&gt;id&lt;/code&gt;, random &lt;code&gt;status&lt;/code&gt; จาก enum หรือสร้าง &lt;code&gt;total&lt;/code&gt; ที่ดูสมจริง วิธีนี้ช่วยเจอ bug ที่ static payload อาจซ่อนไว้ เช่น parser พังเมื่อ string ยาว, field เป็น &lt;code&gt;null&lt;/code&gt;, หรือ array มีจำนวน item มากกว่าปกติ&lt;/p&gt;

&lt;p&gt;ทีมส่วนใหญ่มักใช้ทั้งสองแบบ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;static response สำหรับ assertion-heavy tests&lt;/li&gt;
&lt;li&gt;dynamic response สำหรับ fuzz-style coverage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ด้วย &lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; คุณสามารถให้เครื่องมืออ่าน schema แล้วสร้าง mock endpoint ให้อัตโนมัติ รวมถึง map field name เช่น &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;phone&lt;/code&gt;, &lt;code&gt;avatar&lt;/code&gt; ไปเป็นข้อมูลตัวอย่างที่เหมาะสม&lt;/p&gt;

&lt;p&gt;แนวทางสำคัญคืออย่าใช้ mock data ที่ง่ายเกินไป เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shipped"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;payload แบบนี้อาจทำให้ test ผ่าน แต่ไม่ได้สะท้อน production จริง ควรใช้ข้อมูลที่ใกล้เคียงของจริง เช่น มี item 2-3 รายการ, total สมจริง และ status อยู่ใน enum จริง&lt;/p&gt;

&lt;h2&gt;
  
  
  ขั้นตอนที่ 3: รัน Mock Server
&lt;/h2&gt;

&lt;p&gt;เมื่อมี schema และ response แล้ว ต้องมี mock server สำหรับให้ test เรียกใช้งาน&lt;/p&gt;

&lt;h3&gt;
  
  
  Local mock server
&lt;/h3&gt;

&lt;p&gt;เหมาะกับ unit test และ integration test เพราะเร็ว ทำงาน offline ได้ และไม่มี shared state ระหว่าง developer หรือ CI build&lt;/p&gt;

&lt;p&gt;ตัวอย่างใช้ &lt;a href="https://github.com/stoplightio/prism" rel="noopener noreferrer"&gt;Prism&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;prism mock openapi.yaml
&lt;span class="c"&gt;# Mock server listening on http://127.0.0.1:4010&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จากนั้นเรียก endpoint ได้ เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:4010/orders/order_8842
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cloud mock server
&lt;/h3&gt;

&lt;p&gt;Cloud mock server มี public URL เหมาะเมื่อ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mobile app ต้องเรียก mock API จาก device จริง&lt;/li&gt;
&lt;li&gt;CI runner เข้าไม่ถึงเครื่อง local&lt;/li&gt;
&lt;li&gt;external collaborator ต้องทดสอบ API เดียวกัน&lt;/li&gt;
&lt;li&gt;ต้อง demo API ก่อน backend พร้อม&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apidog มี Cloud Mock URL สำหรับ project ทำให้ทีมเรียก endpoint เดียวกันได้โดยไม่ต้องรัน server บนเครื่องตัวเอง&lt;/p&gt;

&lt;p&gt;สำหรับ automated test ให้เริ่มจาก local mock เป็นค่า default เพราะเร็วและ predictable กว่า ส่วน cloud mock เหมาะกับ demo, cross-device testing และ collaboration&lt;/p&gt;

&lt;h2&gt;
  
  
  ขั้นตอนที่ 4: ชี้ Test ไปยัง Mock Server
&lt;/h2&gt;

&lt;p&gt;อย่า hardcode production URL ใน test ให้ทำ base URL เป็น configuration ผ่าน environment variable แทน&lt;/p&gt;

&lt;p&gt;ตัวอย่าง JavaScript test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// orderClient.test.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getOrder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./orderClient.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;API_BASE_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://127.0.0.1:4010&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;parses a shipped order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order_8842&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shipped&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่าง client function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// orderClient.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/orders/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Order not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Request failed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ในเครื่อง local ใช้ค่า default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใน CI กำหนด &lt;code&gt;API_BASE_URL&lt;/code&gt; ก่อนรัน test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;API_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://127.0.0.1:4010 npm &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;pattern นี้ทำให้โค้ด production ไม่ต้องรู้เลยว่ากำลังเรียก mock หรือ API จริง และใช้แนวคิดเดียวกันได้เมื่อคุณ &lt;a href="http://apidog.com/blog/automate-api-tests-ci-cd?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ทำให้การทดสอบ API เป็นอัตโนมัติใน CI/CD&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ขั้นตอนที่ 5: ทดสอบ Error Path
&lt;/h2&gt;

&lt;p&gt;นี่คือจุดที่ mock API มีประโยชน์มาก เพราะ server จริงไม่ค่อยส่ง &lt;code&gt;500&lt;/code&gt;, &lt;code&gt;429&lt;/code&gt; หรือ response ช้าตามเวลาที่คุณต้องการ แต่ mock server ทำได้ทันที&lt;/p&gt;

&lt;p&gt;กำหนด scenario ที่ต้องทดสอบให้ชัดเจน:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;สถานการณ์&lt;/th&gt;
&lt;th&gt;Mock ส่งคืน&lt;/th&gt;
&lt;th&gt;สิ่งที่ควร assert&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ไม่พบ order&lt;/td&gt;
&lt;td&gt;&lt;code&gt;404&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;client ส่ง error “ไม่พบ” ที่ชัดเจน&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;server ล้มเหลว&lt;/td&gt;
&lt;td&gt;&lt;code&gt;500&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;client retry หรือแสดง fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rate limit&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;429&lt;/code&gt; พร้อม &lt;code&gt;Retry-After&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;client backoff ตามเวลาที่เหมาะสม&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;response ช้า&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;200&lt;/code&gt; หลัง delay 5 วินาที&lt;/td&gt;
&lt;td&gt;client timeout และ recover ได้&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON เสีย&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;200&lt;/code&gt; พร้อม malformed JSON&lt;/td&gt;
&lt;td&gt;client fail อย่างควบคุมได้&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ตัวอย่าง test สำหรับ &lt;code&gt;404&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;throws clear error when order is not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order_404&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rejects&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Order not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่าง test สำหรับ retry เมื่อเจอ &lt;code&gt;500&lt;/code&gt; อาจเขียน client ให้รับ retry policy แล้ว assert จำนวนครั้งที่เรียก:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;retries when server returns 500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getOrderWithRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order_retry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order_retry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;กฎ mock ขั้นสูงของ Apidog ช่วยให้ส่ง response ต่างกันตาม request ได้ เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /orders/order_404&lt;/code&gt; ส่ง &lt;code&gt;404&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /orders/order_rate_limited&lt;/code&gt; ส่ง &lt;code&gt;429&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /orders/order_8842&lt;/code&gt; ส่ง &lt;code&gt;200&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;วิธีนี้ทำให้ mock endpoint เดียวครอบคลุมทั้ง happy path และ error path ได้ เมื่อใช้ร่วมกับ &lt;a href="http://apidog.com/blog/api-assertions?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การยืนยัน API&lt;/a&gt; ที่ดี test จะตรวจพฤติกรรมจริง ไม่ใช่แค่ status code&lt;/p&gt;

&lt;h2&gt;
  
  
  การจัดระเบียบ Mock ใน Test Suite ที่โตขึ้น
&lt;/h2&gt;

&lt;p&gt;mock endpoint เดียวจัดการง่าย แต่เมื่อมีหลายสิบหรือหลายร้อยตัว ต้องมีโครงสร้างที่ชัดเจน&lt;/p&gt;

&lt;p&gt;แนวทางที่ควรใช้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;จัดกลุ่ม mock ตาม service จริง เช่น &lt;code&gt;orders&lt;/code&gt;, &lt;code&gt;payments&lt;/code&gt;, &lt;code&gt;users&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ตั้งชื่อ fixture ตาม scenario เช่น &lt;code&gt;order-shipped&lt;/code&gt;, &lt;code&gt;order-not-found&lt;/code&gt;, &lt;code&gt;order-rate-limited&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;เก็บ mock definition ไว้ใน version control คู่กับ test&lt;/li&gt;
&lt;li&gt;ใช้ base fixture แล้ว override เฉพาะ field ที่ test ต้องการ&lt;/li&gt;
&lt;li&gt;หลีกเลี่ยงการสร้าง payload เฉพาะใหม่สำหรับทุก test&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่างโครงสร้างไฟล์:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tests/
  fixtures/
    orders/
      order-shipped.json
      order-not-found.json
      order-rate-limited.json
    payments/
      payment-success.json
      payment-failed.json
  orderClient.test.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่าง base fixture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"order_8842"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shipped"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;149.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sku_001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Keyboard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หาก test ต้องการเปลี่ยนเฉพาะ status ให้ override เฉพาะ field นั้น แทนการ copy ทั้ง payload ไปทุกไฟล์ วิธีนี้ช่วยลดปัญหา contract เปลี่ยนแล้วต้องแก้หลายจุด&lt;/p&gt;

&lt;p&gt;แนวคิดเดียวกับการจัด &lt;a href="http://apidog.com/blog/test-suites-api-test-automation?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ชุดทดสอบสำหรับการทำงานอัตโนมัติของ API&lt;/a&gt; สามารถใช้กับ mock ได้โดยตรง&lt;/p&gt;

&lt;h2&gt;
  
  
  รักษาความถูกต้องของ Mock
&lt;/h2&gt;

&lt;p&gt;ปัญหาที่พบบ่อยที่สุดคือ mock drift: backend เปลี่ยน response แล้ว mock ยังส่งรูปแบบเก่า เช่น backend เปลี่ยนจาก:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;149.99&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เป็น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;149.99&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แต่ mock ยังใช้ &lt;code&gt;total&lt;/code&gt; อยู่ ผลคือ test ผ่าน แต่ production พัง&lt;/p&gt;

&lt;p&gt;ลดความเสี่ยงนี้ด้วย 3 วิธี:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;สร้าง mock จาก schema เดียวกับ backend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ถ้าใช้ OpenAPI ให้ mock server อ่านจากไฟล์เดียวกัน&lt;/li&gt;
&lt;li&gt;เมื่อ schema เปลี่ยน mock จะเปลี่ยนตาม&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;รัน contract test กับ API จริง&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ไม่ต้องเยอะ&lt;/li&gt;
&lt;li&gt;ให้มี job ที่ยืนยันว่า response จริงยังตรงกับ schema&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;review mock ใน pull request&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ถ้า PR เปลี่ยน API response ต้องเปลี่ยน schema และ mock ที่เกี่ยวข้องด้วย&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ตัวอย่าง workflow ใน CI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. start mock server from OpenAPI&lt;/span&gt;
prism mock openapi.yaml &amp;amp;

&lt;span class="c"&gt;# 2. run app/client tests against mock&lt;/span&gt;
&lt;span class="nv"&gt;API_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://127.0.0.1:4010 npm &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# 3. optionally run contract tests against real staging API&lt;/span&gt;
&lt;span class="nv"&gt;API_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://staging.example.com npm run &lt;span class="nb"&gt;test&lt;/span&gt;:contract
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หากต้องการสภาพแวดล้อมเดียวที่เก็บ schema, สร้าง mock และรัน test กับ mock ได้ในที่เดียว คุณสามารถ &lt;a href="https://apidog.com/download?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ดาวน์โหลด Apidog&lt;/a&gt; เพื่อจัดการ API design, mock server และ test suite ให้สอดคล้องกัน หรือดูตัวเลือกเพิ่มเติมใน &lt;a href="http://apidog.com/blog/rest-api-mocking-tools?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;เครื่องมือจำลอง REST API&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist สำหรับเริ่มใช้ API Mocking
&lt;/h2&gt;

&lt;p&gt;ใช้รายการนี้เป็นขั้นตอนเริ่มต้น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] มี OpenAPI schema หรือ schema เฉพาะ endpoint ที่จะ test&lt;/li&gt;
&lt;li&gt;[ ] มี mock response สำหรับ happy path&lt;/li&gt;
&lt;li&gt;[ ] มี mock response สำหรับ &lt;code&gt;404&lt;/code&gt;, &lt;code&gt;429&lt;/code&gt;, &lt;code&gt;500&lt;/code&gt;, timeout&lt;/li&gt;
&lt;li&gt;[ ] test ใช้ &lt;code&gt;API_BASE_URL&lt;/code&gt; แทนการ hardcode URL&lt;/li&gt;
&lt;li&gt;[ ] mock definition อยู่ใน version control&lt;/li&gt;
&lt;li&gt;[ ] มี contract test จำนวนเล็กน้อยที่เรียก API จริง&lt;/li&gt;
&lt;li&gt;[ ] CI รัน test กับ mock server ได้อัตโนมัติ&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  คำถามที่พบบ่อย
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ฉันควรจำลอง API สำหรับทุกการทดสอบหรือไม่?
&lt;/h3&gt;

&lt;p&gt;ไม่ควร ใช้ mock สำหรับ unit test และ integration test ที่ต้องการตรวจโค้ดของคุณเอง แต่ควรมี contract test และ end-to-end test จำนวนเล็กน้อยที่เรียก API จริง เพื่อยืนยันว่า mock ยังตรงกับ production contract&lt;/p&gt;

&lt;h3&gt;
  
  
  Static response กับ dynamic response ต่างกันอย่างไร?
&lt;/h3&gt;

&lt;p&gt;Static response คือ JSON ที่กำหนดตายตัว เหมาะกับ test ที่ต้องการ assert ค่าแน่นอน ส่วน dynamic response ถูกสร้างตาม request ด้วยค่าที่สมจริงกว่า ช่วยเจอ edge case ที่ static payload อาจไม่ครอบคลุม ทีมส่วนใหญ่ใช้ทั้งสองแบบร่วมกัน&lt;/p&gt;

&lt;h3&gt;
  
  
  จะทำให้ mock แม่นยำอยู่เสมอได้อย่างไร?
&lt;/h3&gt;

&lt;p&gt;สร้าง mock จาก schema เดียวกับ backend เช่น OpenAPI และรัน contract test กับ API จริงเป็นระยะ หาก contract test ล้มเหลว แปลว่า schema หรือ mock อาจล้าสมัยและต้องอัปเดต&lt;/p&gt;

&lt;h3&gt;
  
  
  Mock server จำลอง response ช้าหรือล้มเหลวได้หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ได้ และเป็นหนึ่งในเหตุผลหลักที่ควรใช้ mock คุณสามารถกำหนดให้ mock ส่ง &lt;code&gt;500&lt;/code&gt;, &lt;code&gt;429&lt;/code&gt; พร้อม header &lt;code&gt;Retry-After&lt;/code&gt;, response delay หรือ malformed JSON เพื่อทดสอบ retry, timeout และ error handling ได้&lt;/p&gt;

&lt;h3&gt;
  
  
  ควรใช้ local mock server หรือ cloud mock server สำหรับการทดสอบ?
&lt;/h3&gt;

&lt;p&gt;ใช้ local mock server สำหรับ automated test เพราะเร็ว ไม่มี network latency และไม่มี shared state ระหว่าง build ใช้ cloud mock server เมื่อ mobile device, CI runner หรือ collaborator ภายนอกต้องเข้าถึง mock API โดยไม่ต้องรัน server บนเครื่องของคุณเอง&lt;/p&gt;

</description>
    </item>
    <item>
      <title>วิธีใช้ Insomnia ทดสอบ API</title>
      <dc:creator>Thanawat Wongchai</dc:creator>
      <pubDate>Fri, 22 May 2026 07:36:16 +0000</pubDate>
      <link>https://forem.com/thanawat_wonchai/withiiaich-insomnia-thdsb-api-45pm</link>
      <guid>https://forem.com/thanawat_wonchai/withiiaich-insomnia-thdsb-api-45pm</guid>
      <description>&lt;p&gt;Insomnia คือไคลเอนต์ API จาก Kong สำหรับส่งคำขอ ตรวจสอบการตอบกลับ และทดสอบ API ในเครื่องมือเดียว รองรับ HTTP, REST, GraphQL, gRPC, SOAP และ WebSocket เหมาะกับนักพัฒนาที่ต้องการเครื่องมือเบากว่า IDE ขนาดใหญ่ แต่ยังทำงานทดสอบ API ได้จริง&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com/?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation" class="crayons-btn crayons-btn--primary"&gt;ลองใช้ Apidog วันนี้&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;บทความนี้สรุปขั้นตอนทดสอบ API ด้วย Insomnia แบบลงมือทำ: สร้างคอลเล็กชันคำขอ, ส่ง request, ตรวจ response, ตั้งค่า environment, ใช้ตัวแปรแทน URL และ token, เขียน assertion ด้วย test suite และรันผ่าน command line ด้วย Inso ตัวอย่างใช้ API สาธารณะเพื่อให้ทดลองตามได้ทันที&lt;/p&gt;

&lt;h2&gt;
  
  
  ติดตั้ง Insomnia และสร้างคำขอแรก
&lt;/h2&gt;

&lt;p&gt;ดาวน์โหลด Insomnia จาก &lt;a href="https://insomnia.rest/download" rel="noopener noreferrer"&gt;เว็บไซต์ทางการของ Kong&lt;/a&gt; แล้วติดตั้งตามระบบปฏิบัติการของคุณ เมื่อเปิดครั้งแรก Insomnia อาจถามให้ลงชื่อเข้าใช้ คุณสามารถเลือกทำงานแบบโลคอลได้หากไม่ต้องการซิงค์ข้อมูลขึ้นคลาวด์&lt;/p&gt;

&lt;p&gt;Insomnia จัดงานหลักเป็น &lt;strong&gt;Collection&lt;/strong&gt; และ &lt;strong&gt;Document&lt;/strong&gt; สำหรับการทดสอบ API ให้เริ่มจาก Collection:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;เปิด Dashboard&lt;/li&gt;
&lt;li&gt;คลิก &lt;strong&gt;Create&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;เลือก &lt;strong&gt;Request Collection&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ตั้งชื่อ เช่น &lt;code&gt;User API Tests&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;คลิกปุ่ม &lt;strong&gt;+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;เลือก &lt;strong&gt;HTTP Request&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;สร้างคำขอแรกด้วย API สาธารณะ JSONPlaceholder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://jsonplaceholder.typicode.com/users/1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;คลิก &lt;strong&gt;Send&lt;/strong&gt; แล้วตรวจสิ่งต่อไปนี้ในแผง response:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP status code เช่น &lt;code&gt;200&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;response body&lt;/li&gt;
&lt;li&gt;response time&lt;/li&gt;
&lt;li&gt;response size&lt;/li&gt;
&lt;li&gt;JSON ที่ถูก format ให้อ่านง่าย&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ถ้า response มีขนาดใหญ่ คุณสามารถใช้ JSONPath หรือ XPath เพื่อกรองข้อมูลเฉพาะส่วนที่ต้องการตรวจสอบได้&lt;/p&gt;

&lt;h2&gt;
  
  
  กำหนด Method, Query, Header, Body และ Auth
&lt;/h2&gt;

&lt;p&gt;สำหรับ API จริง คุณมักต้องตั้งค่ามากกว่า URL และ method ใน Insomnia ให้ใช้แท็บใต้แถบ URL เพื่อจัดการ request แต่ละส่วน&lt;/p&gt;

&lt;h3&gt;
  
  
  ส่ง JSON Body
&lt;/h3&gt;

&lt;p&gt;สำหรับ &lt;code&gt;POST&lt;/code&gt; ให้เลือก method เป็น &lt;code&gt;POST&lt;/code&gt; แล้วไปที่แท็บ &lt;strong&gt;Body&lt;/strong&gt; เลือกชนิดเป็น &lt;strong&gt;JSON&lt;/strong&gt; จากนั้นใส่ payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Daniel Okafor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"daniel.okafor@example.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เมื่อเลือก body เป็น JSON Insomnia จะตั้ง header &lt;code&gt;Content-Type: application/json&lt;/code&gt; ให้โดยอัตโนมัติ&lt;/p&gt;

&lt;h3&gt;
  
  
  เพิ่ม Query Parameter
&lt;/h3&gt;

&lt;p&gt;ใช้แท็บ &lt;strong&gt;Query&lt;/strong&gt; เพื่อเพิ่ม query string โดยไม่ต้องแก้ URL เอง เช่น:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;page&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;limit&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ข้อดีคือเปิด/ปิด parameter แต่ละตัวได้ และ URL ยังอ่านง่ายเมื่อมี parameter จำนวนมาก&lt;/p&gt;

&lt;h3&gt;
  
  
  เพิ่ม Header
&lt;/h3&gt;

&lt;p&gt;ใช้แท็บ &lt;strong&gt;Headers&lt;/strong&gt; สำหรับค่าที่ API ต้องการ เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Accept: application/json
X-Request-Id: test-001
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ตั้งค่า Authentication
&lt;/h3&gt;

&lt;p&gt;ไปที่แท็บ &lt;strong&gt;Auth&lt;/strong&gt; แล้วเลือก schema ที่ API ใช้ เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bearer Token&lt;/li&gt;
&lt;li&gt;Basic Auth&lt;/li&gt;
&lt;li&gt;API Key&lt;/li&gt;
&lt;li&gt;OAuth 1.0 / 2.0&lt;/li&gt;
&lt;li&gt;AWS IAM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;สำหรับ API ที่ใช้ bearer token ให้เลือก &lt;strong&gt;Bearer Token&lt;/strong&gt; แล้วใส่ token โดยตรง หรือดีกว่านั้นให้อ้างอิงจาก environment variable เพื่อไม่ hard-code ค่า secret ลงใน request&lt;/p&gt;

&lt;p&gt;ตัวอย่าง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{ _.auth_token }}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หากต้องการทบทวนว่า REST API ควรตอบ status code แบบใด ดูคู่มือเรื่อง &lt;a href="http://apidog.com/blog/which-http-status-codes-rest-apis-should-use?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;รหัสสถานะ HTTP ที่ REST API ควรใช้&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ตั้งค่า Environment และตัวแปร
&lt;/h2&gt;

&lt;p&gt;Environment ช่วยให้คุณไม่ต้องเขียนค่าเดิมซ้ำ เช่น base URL, token หรือค่า config ของแต่ละ environment&lt;/p&gt;

&lt;p&gt;ตัวอย่าง use case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;local&lt;/li&gt;
&lt;li&gt;staging&lt;/li&gt;
&lt;li&gt;production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ใน Insomnia ให้ทำดังนี้:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;คลิก dropdown ของ environment ใกล้ด้านบนของ sidebar&lt;/li&gt;
&lt;li&gt;เลือก &lt;strong&gt;Manage Environments&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;เพิ่มค่าใน &lt;strong&gt;Base Environment&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;สร้าง sub-environment สำหรับแต่ละเป้าหมาย&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ตัวอย่าง environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"base_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://jsonplaceholder.typicode.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"auth_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-token-here"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จากนั้นแก้ URL ของ request ให้ใช้ตัวแปร:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET {{ _.base_url }}/users/1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เมื่อสลับ environment จาก dropdown ทุก request ที่อ้างอิงตัวแปรจะเปลี่ยนค่าตาม environment นั้นทันที&lt;/p&gt;

&lt;h3&gt;
  
  
  ใช้ Template Tag เพื่อเชื่อม request
&lt;/h3&gt;

&lt;p&gt;Insomnia รองรับ &lt;strong&gt;template tags&lt;/strong&gt; สำหรับสร้างหรือดึงค่าระหว่าง request เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;timestamp&lt;/li&gt;
&lt;li&gt;UUID&lt;/li&gt;
&lt;li&gt;response body จาก request ก่อนหน้า&lt;/li&gt;
&lt;li&gt;token จาก login response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่าง flow ที่ใช้บ่อย:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ส่ง request login&lt;/li&gt;
&lt;li&gt;ดึงค่า token จาก response ด้วย JSONPath เช่น &lt;code&gt;$.token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ใส่ token นั้นใน header &lt;code&gt;Authorization&lt;/code&gt; ของ request ถัดไป&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;แนวทางนี้ช่วยให้ chain request ได้โดยไม่ต้องเขียน script เชื่อมเอง เหมาะกับกรณีที่ API ต้อง login ก่อนเรียก endpoint ที่มีการป้องกัน&lt;/p&gt;

&lt;p&gt;สำหรับการจัดกลุ่มกรณีทดสอบเพิ่มเติม ดูตัวอย่าง &lt;a href="http://apidog.com/blog/api-test-case-example?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;กรณีทดสอบ API&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  เขียน Test Suite และ Assertion
&lt;/h2&gt;

&lt;p&gt;การกด &lt;strong&gt;Send&lt;/strong&gt; ช่วยให้ดู response ได้ทันที แต่ถ้าต้องการตรวจสอบซ้ำแบบอัตโนมัติ ให้ใช้ &lt;strong&gt;Test Suite&lt;/strong&gt; หรือบางเวอร์ชันอาจแสดงเป็น &lt;strong&gt;Unit Tests&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ขั้นตอนพื้นฐาน:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;เปิด collection&lt;/li&gt;
&lt;li&gt;ไปที่มุมมอง &lt;strong&gt;Tests&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;สร้าง &lt;strong&gt;Test Suite&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;เพิ่ม test case&lt;/li&gt;
&lt;li&gt;เลือก request ที่ test case นั้นต้องรัน&lt;/li&gt;
&lt;li&gt;เขียน JavaScript assertion&lt;/li&gt;
&lt;li&gt;คลิก &lt;strong&gt;Run Tests&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Insomnia ใช้ Chai assertion และมี helper สำหรับส่ง request&lt;/p&gt;

&lt;p&gt;ตัวอย่างตรวจ status code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;insomnia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่างตรวจ response body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;insomnia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;daniel.okafor@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หาก response เป็น JSON array คุณสามารถตรวจจำนวนรายการได้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;insomnia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;an&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;greaterThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หรือถ้าต้องการตรวจ header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;insomnia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  จัดโครงสร้าง Test Suite ให้ดูแลง่าย
&lt;/h2&gt;

&lt;p&gt;เมื่อ test เพิ่มขึ้น ให้แบ่ง suite ตาม resource หรือ feature เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;User API Tests&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Post API Tests&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Auth API Tests&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ภายในแต่ละ suite ให้ test หนึ่งรายการตรวจพฤติกรรมเดียว เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;happy path&lt;/li&gt;
&lt;li&gt;not found&lt;/li&gt;
&lt;li&gt;validation error&lt;/li&gt;
&lt;li&gt;unauthorized&lt;/li&gt;
&lt;li&gt;forbidden&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่างชื่อ test ที่อ่านแล้วรู้ปัญหาทันที:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /users/1 returns 200 and user profile
GET /users/999999 returns 404
POST /users rejects invalid email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หลักสำคัญคือเมื่อ test fail ชื่อ test ควรบอกได้ทันทีว่า behavior ไหนเสีย โดยไม่ต้องอ่าน assertion ทั้งหมด&lt;/p&gt;

&lt;p&gt;อ่านเพิ่มเกี่ยวกับ &lt;a href="http://apidog.com/blog/api-assertions?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การยืนยัน API&lt;/a&gt; และการจัดโครงสร้าง &lt;a href="http://apidog.com/blog/test-suites-api-test-automation?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ชุดทดสอบสำหรับ API test automation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  รัน Test จาก Command Line ด้วย Inso
&lt;/h2&gt;

&lt;p&gt;GUI เหมาะกับการ debug ระหว่างพัฒนา แต่ถ้าต้องการรันใน CI/CD ให้ใช้ &lt;strong&gt;Inso&lt;/strong&gt; ซึ่งเป็น command line companion ของ Insomnia&lt;/p&gt;

&lt;p&gt;หลังจาก export หรือ sync collection แล้ว สามารถรัน test suite จาก terminal ได้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;inso run &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"User API tests"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้ามี test ใดล้มเหลว Inso จะคืนค่า exit code ที่ไม่เป็นศูนย์ ซึ่งเหมาะกับ CI pipeline เพราะระบบสามารถ mark build ว่าล้มเหลวได้ทันที&lt;/p&gt;

&lt;p&gt;ตัวอย่างการใช้งานใน pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;inso run &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"User API tests"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แนวทางนี้ช่วยให้ตรวจจับ endpoint ที่เสียก่อน merge หรือ deploy ไป production&lt;/p&gt;

&lt;p&gt;Inso ยังใช้ lint API specification และสร้าง test report ในรูปแบบมาตรฐานได้ จึงเหมาะกับ workflow ที่ต้องการตรวจทั้ง contract และ behavior ของ API&lt;/p&gt;

&lt;p&gt;ดูตัวอย่างรูปแบบ CI/CD เพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/automate-api-tests-ci-cd?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การทำให้การทดสอบ API เป็นไปโดยอัตโนมัติใน CI/CD&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  การเปลี่ยนแปลง Cloud ใน Insomnia 8 และทางเลือกอื่น
&lt;/h2&gt;

&lt;p&gt;Insomnia 8 เปลี่ยนไปใช้แนวทาง cloud-first มากขึ้น โดยค่าเริ่มต้นจะผลักดันให้ผู้ใช้สร้างบัญชี Kong และจัดเก็บโปรเจกต์ในคลาวด์ การเปลี่ยนแปลงนี้ทำให้ผู้ใช้บางกลุ่มไม่พอใจ เพราะเวอร์ชันก่อนหน้ารองรับการใช้งานแบบโลคอลและออฟไลน์ได้ชัดเจนกว่า&lt;/p&gt;

&lt;p&gt;เวอร์ชันต่อมาได้เพิ่มตัวเลือกแบบโลคอลหรือ “Scratch Pad” ให้ชัดเจนขึ้น แต่เหตุการณ์นี้ทำให้หลายทีมเริ่มประเมินเครื่องมืออื่น โดยเฉพาะทีมที่มีข้อกำหนดด้านข้อมูลว่า project หรือ test data ไม่ควรออกนอกองค์กร&lt;/p&gt;

&lt;p&gt;หากอยู่ในกรณีนี้ &lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; เป็นอีกทางเลือกหนึ่ง เป็นแพลตฟอร์ม API แบบ all-in-one สำหรับ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API design&lt;/li&gt;
&lt;li&gt;debugging&lt;/li&gt;
&lt;li&gt;mocking&lt;/li&gt;
&lt;li&gt;testing&lt;/li&gt;
&lt;li&gt;documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Apidog สามารถนำเข้าไฟล์ Insomnia ที่ export ออกมาได้ ช่วยให้ย้ายหรือทดลองเปรียบเทียบได้โดยไม่ต้องเริ่มใหม่ทั้งหมด นอกจากนี้ยังสร้าง assertion แบบ visual ได้โดยไม่ต้องเขียน JavaScript แต่ยังรองรับ script เมื่อจำเป็น&lt;/p&gt;

&lt;p&gt;คุณสามารถ &lt;a href="https://apidog.com/download?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ดาวน์โหลด Apidog&lt;/a&gt; แล้วนำเข้า collection จาก Insomnia เพื่อเปรียบเทียบ workflow ได้ หากต้องการดูตัวเลือกอื่นเพิ่มเติม บทความ &lt;a href="http://apidog.com/blog/online-api-testing-tools-free?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;เครื่องมือทดสอบ API ออนไลน์ฟรี&lt;/a&gt; รวบรวมเครื่องมือหลายประเภทไว้ให้เปรียบเทียบ&lt;/p&gt;

&lt;p&gt;Insomnia ยังเป็นไคลเอนต์ที่แข็งแรงสำหรับนักพัฒนาเดี่ยวและทีมขนาดเล็กที่ต้องการอินเทอร์เฟซเรียบง่าย เริ่มใช้งานเร็ว และไม่ซับซ้อน การเลือกเครื่องมือที่เหมาะสมขึ้นอยู่กับ workflow ของทีม และคุณต้องการรวมวงจรชีวิต API ไว้ในเครื่องมือเดียวมากน้อยแค่ไหน&lt;/p&gt;

&lt;h2&gt;
  
  
  คำถามที่พบบ่อย
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Insomnia ใช้งานฟรีหรือไม่?
&lt;/h3&gt;

&lt;p&gt;Insomnia มีแพ็กเกจฟรีที่ครอบคลุมการใช้งานส่วนบุคคล เช่น การส่ง request และรัน test suite ในเครื่อง แผนแบบชำระเงินเพิ่มความสามารถด้าน collaboration และ cloud sync สำหรับทีม คุณยังสามารถใช้ไคลเอนต์หลักได้โดยไม่เสียค่าใช้จ่าย และเวอร์ชันล่าสุดมีตัวเลือกสำหรับทำงานแบบโลคอลหากไม่ต้องการ cloud sync&lt;/p&gt;

&lt;h3&gt;
  
  
  Insomnia รองรับโปรโตคอลใดบ้าง?
&lt;/h3&gt;

&lt;p&gt;Insomnia รองรับ HTTP, REST, GraphQL, gRPC, SOAP และ WebSocket วิธีตั้งค่า request จะแตกต่างกันตามโปรโตคอล แต่การตรวจ response และการทดสอบ request ที่ใช้ HTTP สามารถทำได้ใน workflow เดียวกัน&lt;/p&gt;

&lt;h3&gt;
  
  
  เขียน assertion ใน Insomnia อย่างไร?
&lt;/h3&gt;

&lt;p&gt;ใช้ Test Suite ใน collection แล้วเพิ่ม test case ที่ผูกกับ request จากนั้นเขียน JavaScript โดยเรียก:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;insomnia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แล้วใช้ &lt;code&gt;expect&lt;/code&gt; แบบ Chai เพื่อตรวจ status, header หรือ body เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Insomnia 8 เปลี่ยนอะไรบ้าง?
&lt;/h3&gt;

&lt;p&gt;Insomnia 8 เปลี่ยนค่าเริ่มต้นไปทาง cloud-first มากขึ้น โดยกระตุ้นให้ผู้ใช้ลงชื่อเข้าใช้บัญชี Kong และ sync project ไปยังคลาวด์ ผู้ใช้บางส่วนไม่ชอบการเปลี่ยนจากแอปแบบโลคอลเป็นหลักไปสู่ flow ที่พึ่ง cloud มากขึ้น ภายหลังมีการเพิ่มตัวเลือกแบบโลคอลให้ชัดเจนขึ้น แต่การเปลี่ยนแปลงนี้ทำให้หลายทีมเริ่มประเมินทางเลือกอื่น&lt;/p&gt;

&lt;h3&gt;
  
  
  รัน Insomnia test ใน CI pipeline ได้หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ได้ ให้ใช้ Inso จาก command line หลังจาก export หรือ sync collection แล้วรัน:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;inso run &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;ชื่อชุดทดสอบ&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้า test fail Inso จะคืนค่า exit code ที่ไม่เป็นศูนย์ ทำให้ CI สามารถ fail build ได้อัตโนมัติเมื่อ API test มีปัญหา&lt;/p&gt;

</description>
    </item>
    <item>
      <title>ความแตกต่างระหว่าง Postman และ JMeter</title>
      <dc:creator>Thanawat Wongchai</dc:creator>
      <pubDate>Fri, 22 May 2026 07:34:54 +0000</pubDate>
      <link>https://forem.com/thanawat_wonchai/khwaamaetktaangrahwaang-postman-aela-jmeter-d81</link>
      <guid>https://forem.com/thanawat_wonchai/khwaamaetktaangrahwaang-postman-aela-jmeter-d81</guid>
      <description>&lt;p&gt;Postman และ JMeter ไม่ใช่คู่แข่งโดยตรง เพราะตอบคำถามคนละแบบ: Postman ใช้ตรวจว่า API “ทำงานถูกต้องหรือไม่” ส่วน JMeter ใช้ตรวจว่า API “ยังทำงานได้ดีหรือไม่เมื่อมีโหลดจำนวนมาก” ถ้าคุณกำลังเช็กว่า endpoint คืน JSON ถูก schema หรือไม่ ให้ใช้ Postman แต่ถ้าคุณต้องรู้ว่า endpoint เดิมรองรับผู้ใช้พร้อมกัน 2,000 คนได้หรือไม่ ให้ใช้ JMeter&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com/?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation" class="crayons-btn crayons-btn--primary"&gt;ลองใช้ Apidog วันนี้&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;ข้อผิดพลาดที่พบบ่อยคือทีมรัน Postman collection แล้วเห็นผลเขียวทั้งหมด จากนั้นสรุปว่า API พร้อมใช้งานจริง ทั้งที่ยังไม่เคยวัด latency, throughput หรือ error rate ภายใต้ concurrency จริง อีกด้านหนึ่ง บางทีมเริ่มด้วย JMeter แล้วคาดหวังให้มันตรวจจับ JSON field ที่ผิดรูปแบบได้เหมือนชุด functional test บทความนี้สรุปวิธีเลือกใช้เครื่องมือให้ตรงงาน พร้อมแนวทางนำไปใช้ใน workflow จริง&lt;/p&gt;

&lt;h2&gt;
  
  
  Postman สร้างขึ้นมาเพื่ออะไร
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; คือ API client และแพลตฟอร์มสำหรับทำงานร่วมกันกับ API คุณใช้มันเพื่อ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ส่ง request ไปยัง endpoint&lt;/li&gt;
&lt;li&gt;จัดกลุ่ม request เป็น collection&lt;/li&gt;
&lt;li&gt;ใช้ environment variables แยก dev/staging/prod&lt;/li&gt;
&lt;li&gt;เขียน test script ด้วย JavaScript&lt;/li&gt;
&lt;li&gt;ตรวจ status code, response body, headers และ schema&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่าง Postman test script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Status is 200&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Response has a user id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;นี่คือการทดสอบแบบ functional assertion: ส่ง request หนึ่งครั้ง ตรวจผลลัพธ์ แล้วรายงานผ่าน/ไม่ผ่าน&lt;/p&gt;

&lt;p&gt;Collection Runner สามารถวน collection ด้วย data file ได้ และ Postman CLI หรือ Newman สามารถรัน collection ใน CI pipeline ได้ แต่หลักการยังเหมือนเดิมคือยืนยันว่า API ทำงานตาม contract ที่กำหนดไว้ ไม่ใช่วัดความสามารถในการรับโหลด&lt;/p&gt;

&lt;p&gt;ถ้าต้องการลงลึกเรื่อง assertion ดูคู่มือ &lt;a href="http://apidog.com/blog/api-assertions?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การยืนยัน API&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ใช้ Postman ทำอะไรใน workflow จริง
&lt;/h3&gt;

&lt;p&gt;ใช้ Postman เมื่อคุณต้องการ feedback เร็วระหว่างพัฒนา เช่น:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;สร้าง request สำหรับ endpoint ใหม่&lt;/li&gt;
&lt;li&gt;ตั้ง environment เช่น &lt;code&gt;local&lt;/code&gt;, &lt;code&gt;staging&lt;/code&gt;, &lt;code&gt;production&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;เพิ่ม assertion ตรวจ response&lt;/li&gt;
&lt;li&gt;รวม request เป็น regression collection&lt;/li&gt;
&lt;li&gt;รัน collection ใน CI ทุกครั้งที่มี pull request&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ตัวอย่างแนวทาง assertion ที่ควรมี:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Status code is 201&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Response matches expected fields&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;createdAt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[^\s&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+@&lt;/span&gt;&lt;span class="se"&gt;[^\s&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\.[^\s&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+$/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type is JSON&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Postman เหมาะกับการตรวจความถูกต้องของ API แต่ไม่ได้ออกแบบมาเพื่อสร้างผู้ใช้จำลองจำนวนมากที่ยิง request พร้อมกัน&lt;/p&gt;

&lt;h2&gt;
  
  
  JMeter สร้างขึ้นมาเพื่ออะไร
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://jmeter.apache.org/" rel="noopener noreferrer"&gt;Apache JMeter&lt;/a&gt; คือเครื่องมือทดสอบโหลดและประสิทธิภาพ คุณใช้มันเพื่อจำลองผู้ใช้จำนวนมากผ่าน Thread Group แล้ววัด:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;latency&lt;/li&gt;
&lt;li&gt;throughput&lt;/li&gt;
&lt;li&gt;error rate&lt;/li&gt;
&lt;li&gt;percentile response time&lt;/li&gt;
&lt;li&gt;bottleneck ภายใต้โหลด&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;คำถามที่ JMeter ตอบได้ เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;p95 latency เป็นเท่าไรเมื่อมี concurrent users 500 คน&lt;/li&gt;
&lt;li&gt;request rate เท่าไรที่ทำให้ error rate เกิน 1%&lt;/li&gt;
&lt;li&gt;database connection เริ่มตันที่กี่ concurrent sessions&lt;/li&gt;
&lt;li&gt;autoscaling ทำงานทันหรือไม่เมื่อ traffic เพิ่มขึ้นต่อเนื่อง&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JMeter ไม่ได้จำกัดแค่ HTTP เท่านั้น แต่ยังรองรับ JDBC, JMS, FTP, SMTP, TCP และโปรโตคอลอื่นๆ จึงเหมาะกับการทดสอบระบบทั้งชุด ไม่ใช่แค่ REST endpoint เดียว&lt;/p&gt;

&lt;p&gt;ถ้าคุณยังใหม่กับ performance testing อ่านภาพรวม &lt;a href="http://apidog.com/blog/performance-testing?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การทดสอบประสิทธิภาพ&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  โครงสร้างพื้นฐานของ JMeter test plan
&lt;/h3&gt;

&lt;p&gt;JMeter test plan มักประกอบด้วย:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Thread Group&lt;/strong&gt;: จำนวนผู้ใช้จำลอง, ramp-up time, loop count&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sampler&lt;/strong&gt;: request ที่ต้องยิง เช่น HTTP Request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timer&lt;/strong&gt;: หน่วงเวลาเพื่อจำลองพฤติกรรมผู้ใช้จริง&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assertion&lt;/strong&gt;: ตรวจ response ขั้นพื้นฐาน&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Listener&lt;/strong&gt;: ดูผลลัพธ์ เช่น Summary Report หรือ HTML Dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่างการตั้งค่า test แบบง่าย:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread Group
- Number of Threads: 500
- Ramp-up Period: 300 seconds
- Loop Count: 10

HTTP Request
- Method: GET
- Path: /api/products/search?q=laptop

Assertions
- Response Code = 200
- Response contains "items"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สำหรับการรันจริง ควรรัน JMeter แบบ non-GUI เพื่อให้ผลแม่นยำกว่า:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jmeter &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; search-load-test.jmx &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-l&lt;/span&gt; results.jtl &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  การเปรียบเทียบแบบใช้งานจริง
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ด้าน&lt;/th&gt;
&lt;th&gt;Postman&lt;/th&gt;
&lt;th&gt;JMeter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;วัตถุประสงค์หลัก&lt;/td&gt;
&lt;td&gt;ทดสอบ API เชิงฟังก์ชันและ integration&lt;/td&gt;
&lt;td&gt;ทดสอบโหลด, stress และ performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;คำถามหลัก&lt;/td&gt;
&lt;td&gt;response ถูกต้องหรือไม่&lt;/td&gt;
&lt;td&gt;API รองรับโหลดได้หรือไม่&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;รูปแบบ concurrency&lt;/td&gt;
&lt;td&gt;ส่วนใหญ่เป็น request ตามลำดับ&lt;/td&gt;
&lt;td&gt;ผู้ใช้จำลองจำนวนมากทำงานพร้อมกัน&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;โปรโตคอล&lt;/td&gt;
&lt;td&gt;HTTP, HTTPS, GraphQL, WebSocket, gRPC&lt;/td&gt;
&lt;td&gt;HTTP, JDBC, JMS, FTP, SMTP, TCP และอื่นๆ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;การเขียนสคริปต์&lt;/td&gt;
&lt;td&gt;JavaScript test scripts&lt;/td&gt;
&lt;td&gt;Groovy, BeanShell, Java Sampler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ผลลัพธ์&lt;/td&gt;
&lt;td&gt;ผ่าน/ไม่ผ่านต่อ assertion&lt;/td&gt;
&lt;td&gt;latency percentile, throughput, error rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ความยากในการเรียนรู้&lt;/td&gt;
&lt;td&gt;เริ่มต้นง่าย&lt;/td&gt;
&lt;td&gt;ซับซ้อนกว่า เหมาะกับ performance testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ช่วงที่เหมาะที่สุด&lt;/td&gt;
&lt;td&gt;development, integration, regression&lt;/td&gt;
&lt;td&gt;pre-release capacity และ stress validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;การรายงาน&lt;/td&gt;
&lt;td&gt;test result, CLI report&lt;/td&gt;
&lt;td&gt;HTML dashboard, aggregate graph&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ความแตกต่างสำคัญที่สุดคือ &lt;strong&gt;concurrency model&lt;/strong&gt;: Postman เหมาะกับการตรวจ request/response ส่วน JMeter เหมาะกับการจำลองผู้ใช้พร้อมกันจำนวนมาก&lt;/p&gt;

&lt;h2&gt;
  
  
  ควรใช้ Postman เมื่อใด
&lt;/h2&gt;

&lt;p&gt;ใช้ Postman เมื่อคำถามหลักคือ “API ถูกต้องหรือไม่”&lt;/p&gt;

&lt;p&gt;ตัวอย่างสถานการณ์:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;กำลังพัฒนา endpoint ใหม่&lt;/li&gt;
&lt;li&gt;ต้องการตรวจ request/response format&lt;/li&gt;
&lt;li&gt;ต้องการ regression test ที่รันทุก pull request&lt;/li&gt;
&lt;li&gt;ต้องการตรวจ schema หรือ contract&lt;/li&gt;
&lt;li&gt;ต้องการให้ QA หรือทีมอื่นทดลอง API โดยไม่ต้องเขียนโค้ด&lt;/li&gt;
&lt;li&gt;ต้องการ mock หรือ document API ระหว่างพัฒนา&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;สำหรับ contract testing อ่านเพิ่มเติมที่ &lt;a href="http://apidog.com/blog/api-contract-testing?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การทดสอบ Contract Testing&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ตัวอย่าง CI workflow สำหรับ Postman
&lt;/h3&gt;

&lt;p&gt;แนวคิดคือรัน functional test ทุกครั้งที่มี commit หรือ pull request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;API Functional Tests&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postman-tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Newman&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install -g newman&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run API tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;newman run postman_collection.json \&lt;/span&gt;
            &lt;span class="s"&gt;-e staging_environment.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้า assertion ใด fail ให้ pipeline fail ทันที นี่คือ regression testing ไม่ใช่ load testing&lt;/p&gt;

&lt;h2&gt;
  
  
  การอ่านผลลัพธ์ของ JMeter
&lt;/h2&gt;

&lt;p&gt;JMeter สร้างตัวเลขจำนวนมาก แต่ตัวเลขที่ควรดูจริงๆ คือ:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Percentile latency
&lt;/h3&gt;

&lt;p&gt;ค่าเฉลี่ยมักทำให้เข้าใจผิด เพราะ request ที่เร็วมากอาจกลบ request ที่ช้ามากได้ ควรดู:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;p90&lt;/li&gt;
&lt;li&gt;p95&lt;/li&gt;
&lt;li&gt;p99&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่าง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p95 latency = 1.8s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หมายความว่า 95% ของ request ตอบกลับภายใน 1.8 วินาที และอีก 5% ช้ากว่านั้น ถ้าผู้ใช้จำนวนมากอยู่ในกลุ่ม 5% นี้ พวกเขาจะรู้สึกว่า API ช้า แม้ค่าเฉลี่ยจะดูดี&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Throughput
&lt;/h3&gt;

&lt;p&gt;Throughput คือจำนวน request ที่ระบบประมวลผลสำเร็จต่อวินาที เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Throughput = 850 requests/sec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ค่านี้บอก capacity ภายใต้โหลดที่ทดสอบ&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Error rate
&lt;/h3&gt;

&lt;p&gt;อย่าดู latency แยกจาก error rate ตัวอย่างเช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p95 latency = 300ms
error rate = 6%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ผลลัพธ์นี้ไม่ดี เพราะระบบเร็วเพราะทิ้ง request จำนวนมาก การทดสอบที่ใช้ได้ต้องมีทั้ง latency และ error rate อยู่ในเกณฑ์ที่ยอมรับได้&lt;/p&gt;

&lt;h2&gt;
  
  
  ควรใช้ JMeter เมื่อใด
&lt;/h2&gt;

&lt;p&gt;ใช้ JMeter เมื่อคำถามหลักคือ “API รับโหลดจริงได้หรือไม่”&lt;/p&gt;

&lt;p&gt;ตัวอย่างสถานการณ์:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ก่อนเปิดตัว feature สำคัญ&lt;/li&gt;
&lt;li&gt;ก่อนแคมเปญที่คาดว่า traffic จะเพิ่ม&lt;/li&gt;
&lt;li&gt;หลังเปลี่ยน database, cache, load balancer หรือ autoscaling rule&lt;/li&gt;
&lt;li&gt;ต้องทำ stress test เพื่อหาจุดพัง&lt;/li&gt;
&lt;li&gt;ต้องทำ soak test หลายชั่วโมงเพื่อหา memory leak หรือ connection exhaustion&lt;/li&gt;
&lt;li&gt;ต้องทำ spike test เช่น flash sale&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่างการตีความผล:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Concurrent users: 1,000
p95 latency: 380ms
error rate: 0.2%

Concurrent users: 1,500
p95 latency: 2.4s
error rate: 3.8%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จากผลนี้ คุณอาจสรุปได้ว่า capacity ที่ปลอดภัยอยู่ใกล้ 1,000 concurrent users และระบบเริ่มเสื่อมชัดเจนเมื่อเข้าใกล้ 1,500 users&lt;/p&gt;

&lt;p&gt;สำหรับ workflow แบบละเอียด อ่าน &lt;a href="http://apidog.com/blog/api-performance-testing-tutorial?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;บทช่วยสอนการทดสอบประสิทธิภาพ API&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ใช้ร่วมกัน ไม่ใช่เลือกอย่างใดอย่างหนึ่ง
&lt;/h2&gt;

&lt;p&gt;กลยุทธ์ที่ถูกต้องคือใช้ทั้งสองประเภทการทดสอบ:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Functional test&lt;/strong&gt;: ตรวจว่า API คืนข้อมูลถูกต้อง&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load test&lt;/strong&gt;: ตรวจว่า API ยังคืนข้อมูลถูกต้องได้เร็วพอภายใต้โหลดจริง&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ตัวอย่าง pipeline ที่ใช้งานได้จริง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Every commit
→ Run unit tests
→ Run Postman/Apidog functional API tests
→ Fail fast if API contract breaks

Before release
→ Run smoke tests
→ Run JMeter/Apidog performance tests
→ Review p95, p99, throughput, error rate
→ Release only if metrics pass threshold
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Postman หรือเครื่องมือ functional test ควรรันบ่อย เพราะเร็วและจับ regression ได้เร็ว ส่วน JMeter หรือ performance test ควรรันน้อยกว่า เช่น ก่อน release, หลัง infrastructure change หรือเป็น scheduled job รายสัปดาห์&lt;/p&gt;

&lt;h2&gt;
  
  
  ตัวอย่างปัญหาที่ต้องใช้ทั้งสองมุมมอง
&lt;/h2&gt;

&lt;p&gt;สมมติทีมสร้าง endpoint ค้นหา:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /api/search?q=wireless%20mouse&amp;amp;page=1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Postman test ตรวจว่า:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;status code เป็น 200&lt;/li&gt;
&lt;li&gt;response มี &lt;code&gt;items&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;pagination ถูกต้อง&lt;/li&gt;
&lt;li&gt;query ที่ผิดรูปแบบถูก reject&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ทุกอย่างผ่าน แต่เมื่อเปิดใช้งานจริง marketing ส่ง traffic เพิ่มขึ้นสิบเท่า แล้ว search latency พุ่งเป็น 8 วินาที เพราะ query ไปสแกน database table ที่ไม่มี index&lt;/p&gt;

&lt;p&gt;Postman ไม่ผิด เพราะมันตรวจความถูกต้องของ response ไม่ได้จำลอง concurrent traffic จำนวนมาก การรัน JMeter ด้วยโหลดสมจริงจะช่วยเจอ missing index ก่อน release&lt;/p&gt;

&lt;p&gt;ในทางกลับกัน ถ้าทีมใช้แต่ JMeter แล้วสนใจเฉพาะ throughput อาจพลาด bug เช่น API คืนราคาสินค้าผิดเพราะ cache เก่า Load test อาจบอกว่าเร็วมาก แต่ถ้าไม่มี functional assertion ที่ดี ระบบก็แค่ “ตอบผิดได้เร็ว”&lt;/p&gt;

&lt;h2&gt;
  
  
  Apidog เหมาะสมตรงไหน
&lt;/h2&gt;

&lt;p&gt;ถ้าการดูแล Postman และ JMeter แยกกันทำให้ workflow ซับซ้อน &lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; รวมงานออกแบบ API, debugging, functional test automation และ mock server ไว้ในแพลตฟอร์มเดียว&lt;/p&gt;

&lt;p&gt;คุณสามารถ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ออกแบบ API schema&lt;/li&gt;
&lt;li&gt;ส่ง request และ debug&lt;/li&gt;
&lt;li&gt;สร้าง test scenario พร้อม assertion แบบ visual&lt;/li&gt;
&lt;li&gt;เชื่อมหลายขั้นตอนเป็น automated test suite&lt;/li&gt;
&lt;li&gt;ใช้ mock server ระหว่างพัฒนา&lt;/li&gt;
&lt;li&gt;รัน performance test จาก API case ที่บันทึกไว้ ด้วย virtual users ที่กำหนดได้&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แนวทางนี้ช่วยลดงาน export, sync และ context switching ระหว่างหลายเครื่องมือ คุณสามารถ &lt;a href="https://apidog.com/download?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ดาวน์โหลด Apidog&lt;/a&gt; เพื่อทดลองใช้งานฟรี&lt;/p&gt;

&lt;p&gt;ถ้าต้องการเปรียบเทียบตัวเลือกอื่น ดูรายการ &lt;a href="http://apidog.com/blog/best-postman-alternatives-for-api-testing?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ทางเลือก Postman ที่ดีที่สุดสำหรับการทดสอบ API&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  คำถามที่พบบ่อย
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Postman สามารถทำ load testing ได้หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ไม่ได้ในความหมายของ load testing เต็มรูปแบบ Collection Runner วน request ตามลำดับ และแม้ Postman จะมีความสามารถด้าน performance testing บางส่วนในแอปเดสก์ท็อป แต่ยังไม่เทียบเท่าเครื่องมือที่สร้างมาเพื่อ concurrency, ramp-up control และ latency percentile โดยละเอียด&lt;/p&gt;

&lt;p&gt;ถ้าต้องการ load testing จริงจัง ให้ใช้ JMeter, k6, Gatling หรือ performance testing module ของ Apidog&lt;/p&gt;

&lt;h3&gt;
  
  
  JMeter สามารถทำ functional API testing ได้หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ทำได้ผ่าน Response Assertion เช่น ตรวจ status code หรือข้อความใน body แต่ JMeter ไม่ได้สะดวกเท่า Postman หรือ Apidog สำหรับ test ที่เน้น assertion, schema และ workflow ของ API&lt;/p&gt;

&lt;p&gt;แนวทางทั่วไปคือใช้ Postman หรือ Apidog สำหรับ functional test และใช้ JMeter สำหรับ load test&lt;/p&gt;

&lt;h3&gt;
  
  
  JMeter ยากกว่า Postman หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ใช่ Postman เริ่มส่ง request ได้ภายในไม่กี่นาที แต่ JMeter ต้องเข้าใจ Thread Group, Sampler, Timer, Listener, Assertion และแนวทางการรันแบบ non-GUI เพื่อให้ผลแม่นยำกว่า&lt;/p&gt;

&lt;p&gt;ถ้าทีมไม่เคยทำ performance testing มาก่อน ควรเริ่มจาก test case เล็กๆ แล้วเพิ่ม concurrency ทีละขั้น&lt;/p&gt;

&lt;h3&gt;
  
  
  จำเป็นต้องใช้ทั้ง Postman และ JMeter หรือไม่?
&lt;/h3&gt;

&lt;p&gt;จำเป็นต้องมีการทดสอบทั้งสองประเภท แต่ไม่จำเป็นต้องใช้สองผลิตภัณฑ์เสมอไป คุณอาจใช้ Postman คู่กับ JMeter หรือใช้แพลตฟอร์มอย่าง Apidog ที่รวม functional testing และ performance testing ไว้ในที่เดียว&lt;/p&gt;

&lt;h3&gt;
  
  
  เครื่องมือใดช่วยจับ database query ที่ช้าได้ดีกว่า?
&lt;/h3&gt;

&lt;p&gt;JMeter ภายใต้โหลดจริงเหมาะกว่า เพราะ query ที่ดูเร็วเมื่อยิง request เดียว อาจกลายเป็นคอขวดเมื่อมี concurrent users จำนวนมาก โดยเฉพาะเมื่อเกิด connection contention, lock contention หรือ missing index&lt;/p&gt;

&lt;h3&gt;
  
  
  k6, Gatling และ Locust อยู่ตรงไหน?
&lt;/h3&gt;

&lt;p&gt;k6, Gatling และ Locust เป็นทางเลือกของ JMeter ไม่ใช่ทางเลือกโดยตรงของ Postman เครื่องมือเหล่านี้เน้น load testing และมักเหมาะกับทีมที่ต้องการนิยาม test ด้วยโค้ดมากกว่า GUI&lt;/p&gt;

&lt;p&gt;ถึงใช้ k6, Gatling หรือ Locust คุณยังต้องมี functional API testing อยู่ดี&lt;/p&gt;

&lt;h3&gt;
  
  
  ควรทำ load testing บ่อยแค่ไหน?
&lt;/h3&gt;

&lt;p&gt;Functional test ควรรันทุก commit เพราะเร็วและจับ regression ได้ทันที ส่วน load test ใช้เวลานานและใช้ resource มากกว่า จึงมักรัน:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ก่อน release&lt;/li&gt;
&lt;li&gt;หลังเปลี่ยน infrastructure สำคัญ&lt;/li&gt;
&lt;li&gt;ก่อน event ที่คาดว่าจะมี traffic สูง&lt;/li&gt;
&lt;li&gt;ตามรอบ เช่น รายสัปดาห์หรือรายเดือน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;การรัน load test เต็มรูปแบบทุก commit มักไม่คุ้มทั้งเวลาและค่าใช้จ่าย&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Pytest API เฟรมเวิร์คทดสอบอัตโนมัติ: คู่มือฉบับใช้งานจริง</title>
      <dc:creator>Thanawat Wongchai</dc:creator>
      <pubDate>Fri, 22 May 2026 07:34:38 +0000</pubDate>
      <link>https://forem.com/thanawat_wonchai/pytest-api-efrmewirkhthdsbatonmati-khuumuuechbabaichngaancchring-140p</link>
      <guid>https://forem.com/thanawat_wonchai/pytest-api-efrmewirkhthdsbatonmati-khuumuuechbabaichngaancchring-140p</guid>
      <description>&lt;p&gt;นักพัฒนา Python เลือกใช้ pytest เพราะเริ่มต้นง่าย: การทดสอบคือฟังก์ชันที่ขึ้นต้นด้วย &lt;code&gt;test_&lt;/code&gt;, การตรวจสอบใช้ &lt;code&gt;assert&lt;/code&gt; ธรรมดา, และ pytest จัดการการค้นหา/รันให้เอง เมื่อใช้ร่วมกับ &lt;code&gt;requests&lt;/code&gt; คุณจะได้เฟรมเวิร์กแบบ code-first สำหรับทดสอบ API อัตโนมัติ โดยไม่ต้องมีโครงสร้างซับซ้อน&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com/?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation" class="crayons-btn crayons-btn--primary"&gt;ลองใช้ Apidog วันนี้&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;บทความนี้พาคุณสร้างชุดทดสอบ API ด้วย pytest แบบใช้งานจริง ตั้งแต่ตั้งค่าโปรเจกต์ เขียน test แรก แชร์ setup ด้วย fixtures รัน test เดียวกับหลาย input ด้วย &lt;code&gt;parametrize&lt;/code&gt; ไปจนถึงการตรวจสอบ status code, response body และ JSON Schema ตัวอย่างทั้งหมดใช้รูปแบบ API ที่พบได้บ่อย คุณจึงนำไปปรับกับโปรเจกต์ของตัวเองได้ทันที&lt;/p&gt;

&lt;h2&gt;
  
  
  การตั้งค่าโปรเจกต์
&lt;/h2&gt;

&lt;p&gt;เริ่มจากสร้าง virtual environment และติดตั้ง dependency ที่จำเป็น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install &lt;/span&gt;pytest requests jsonschema
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จัดโครงสร้างโปรเจกต์ให้ดูแลง่ายตั้งแต่แรก:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;api-tests/
  conftest.py        # shared fixtures
  test_users.py      # tests for the users endpoints
  test_orders.py     # tests for the orders endpoints
  pytest.ini         # configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;pytest จะค้นหา test อัตโนมัติเมื่อทำตาม convention เหล่านี้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ไฟล์ขึ้นต้นด้วย &lt;code&gt;test_&lt;/code&gt; หรือจบด้วย &lt;code&gt;_test.py&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ฟังก์ชันขึ้นต้นด้วย &lt;code&gt;test_&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;คลาส test ขึ้นต้นด้วย &lt;code&gt;Test&lt;/code&gt; และไม่มี &lt;code&gt;__init__&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่าง &lt;code&gt;pytest.ini&lt;/code&gt; สำหรับลงทะเบียน marks ที่ใช้บ่อย:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[pytest]&lt;/span&gt;
&lt;span class="py"&gt;markers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="err"&gt;smoke:&lt;/span&gt; &lt;span class="err"&gt;quick&lt;/span&gt; &lt;span class="err"&gt;checks&lt;/span&gt; &lt;span class="err"&gt;for&lt;/span&gt; &lt;span class="err"&gt;critical&lt;/span&gt; &lt;span class="err"&gt;API&lt;/span&gt; &lt;span class="err"&gt;flows&lt;/span&gt;
    &lt;span class="err"&gt;slow:&lt;/span&gt; &lt;span class="err"&gt;full&lt;/span&gt; &lt;span class="err"&gt;or&lt;/span&gt; &lt;span class="err"&gt;long-running&lt;/span&gt; &lt;span class="err"&gt;API&lt;/span&gt; &lt;span class="err"&gt;tests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้าคุณเพิ่งเริ่มกับ automation testing อ่านพื้นฐานเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/what-is-automated-testing?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การทดสอบอัตโนมัติคืออะไร&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เหตุผลที่ pytest เหมาะกับ API testing คือ &lt;code&gt;requests&lt;/code&gt; ดูแล HTTP ส่วน pytest ดูแลส่วนรอบ ๆ เช่น discovery, readable assertion failure, fixtures, &lt;code&gt;parametrize&lt;/code&gt; และ reporting คุณจึงสร้าง API test framework ได้จากไลบรารีเล็ก ๆ ที่ทีม Python คุ้นเคยอยู่แล้ว และสามารถเก็บ test ไว้ใน repository เดียวกับโค้ดแอปเพื่อให้เห็น failure ใน pull request เดียวกัน&lt;/p&gt;

&lt;h2&gt;
  
  
  การเขียนการทดสอบ API ครั้งแรก
&lt;/h2&gt;

&lt;p&gt;API test พื้นฐานมี 3 ขั้นตอน:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ส่ง request&lt;/li&gt;
&lt;li&gt;แปลง response&lt;/li&gt;
&lt;li&gt;assert สิ่งที่คาดหวัง&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ตัวอย่างสำหรับ endpoint ผู้ใช้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.example.com/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_user_returns_200&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/users/42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_user_returns_expected_fields&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/users/42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;รันด้วย:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pytest &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เมื่อ &lt;code&gt;assert&lt;/code&gt; ล้มเหลว pytest จะแสดงค่าจริงและค่าที่คาดหวังอย่างละเอียด โดยไม่ต้องใช้ assertion method พิเศษ สำหรับรายการ assertion ที่ควรตรวจใน API response ดูเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/api-assertions?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การยืนยัน API&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  การแชร์การตั้งค่าด้วย fixtures
&lt;/h2&gt;

&lt;p&gt;ถ้าทุก test ต้องเขียน base URL, headers หรือ login ซ้ำ ๆ โค้ดจะดูแลยาก ให้ย้าย setup ไปไว้ใน fixtures&lt;/p&gt;

&lt;p&gt;สร้าง &lt;code&gt;conftest.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# conftest.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.example.com/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;api_session&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Accept&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;auth_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_session&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/auth/login&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qa@example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test-pass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จุดสำคัญ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;scope="session"&lt;/code&gt; สร้าง session เพียงครั้งเดียวต่อการรัน test&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yield&lt;/code&gt; ใช้แยก setup และ teardown&lt;/li&gt;
&lt;li&gt;fixture ใน &lt;code&gt;conftest.py&lt;/code&gt; ใช้ได้ทุก test file โดยไม่ต้อง import&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;นำ fixture ไปใช้ใน test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auth_token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;auth_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pending&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;fixtures เป็นแนวทางที่ยืดหยุ่นกว่า &lt;code&gt;setup_function&lt;/code&gt; และ &lt;code&gt;teardown_function&lt;/code&gt; เพราะประกอบกันได้ รองรับ scope และทำให้ dependency ของ test ชัดเจน อ่านเพิ่มเติมได้จาก &lt;a href="https://docs.pytest.org/en/stable/how-to/fixtures.html" rel="noopener noreferrer"&gt;เอกสาร pytest fixtures&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  การรัน test เดียวกับ input หลายชุด
&lt;/h2&gt;

&lt;p&gt;API endpoint มักต้องตรวจหลายกรณี เช่น input ถูกต้อง, input ไม่ถูกต้อง และ edge case แทนที่จะเขียนหลายฟังก์ชัน ให้ใช้ &lt;code&gt;@pytest.mark.parametrize&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;

&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.example.com/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.mark.parametrize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id,expected_status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;99999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_user_status_codes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_status&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/users/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected_status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;โค้ดนี้สร้าง test case 4 รายการจากฟังก์ชันเดียว แต่ละ case ถูกรันและรายงานผลแยกกัน ทำให้เห็นชัดว่า input ไหนล้มเหลว&lt;/p&gt;

&lt;p&gt;เมื่อข้อมูล test มีจำนวนมาก ให้โหลดจากไฟล์ CSV หรือ JSON แทนการเขียน inline ดูแนวทางได้ใน &lt;a href="http://apidog.com/blog/data-driven-api-testing-tool-csv-json?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การทดสอบ API แบบขับเคลื่อนด้วยข้อมูลด้วย CSV และ JSON&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ถ้าไม่แน่ใจว่าแต่ละกรณีควรตอบ status code อะไร อ้างอิงได้จาก &lt;a href="http://apidog.com/blog/which-http-status-codes-rest-apis-should-use?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;รหัสสถานะ HTTP ที่ REST APIs ควรใช้&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  การยืนยัน response body และ schema
&lt;/h2&gt;

&lt;p&gt;Status code อย่างเดียวไม่พอ เพราะ &lt;code&gt;200 OK&lt;/code&gt; ที่มี body ผิดรูปแบบก็ยังเป็น bug ได้ ควรตรวจ response body ด้วย&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_order_response_shape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auth_token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;auth_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;total_seconds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สำหรับ contract ที่ชัดเจนขึ้น ให้ตรวจ JSON Schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;jsonschema&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt;

&lt;span class="n"&gt;order_schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;integer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;integer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;integer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;minimum&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_order_matches_schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auth_token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/orders&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;auth_token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quantity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order_schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schema validation ช่วยจับปัญหาเชิงโครงสร้าง เช่น field หาย, field เปลี่ยนชื่อ หรือ type เปลี่ยน โดยไม่ต้องเขียน assertion ทีละ field ไลบรารี &lt;code&gt;jsonschema&lt;/code&gt; เป็นตัวเลือกมาตรฐาน และมีรายละเอียดคีย์เวิร์ดใน &lt;a href="https://python-jsonschema.readthedocs.io/" rel="noopener noreferrer"&gt;เอกสารการตรวจสอบ&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  การรันชุดทดสอบใน CI
&lt;/h2&gt;

&lt;p&gt;pytest เหมาะกับ CI เพราะคืนค่า exit code ที่ไม่ใช่ศูนย์เมื่อ test fail ทำให้ build fail ได้ทันที&lt;/p&gt;

&lt;p&gt;รันพร้อมส่งออก JUnit report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pytest &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;--junitxml&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;results.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จากนั้นนำคำสั่งนี้ไปใส่ใน GitHub Actions หรือ pipeline อื่น ๆ เพื่อให้ API test ตรวจทุก commit คู่มือ &lt;a href="http://apidog.com/blog/automate-api-tests-ci-cd?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การทดสอบ API ใน CI/CD pipelines&lt;/a&gt; แสดงการตั้งค่าแบบครบถ้วน รวมถึง secrets และ environment selection&lt;/p&gt;

&lt;h3&gt;
  
  
  อ่านค่า config จาก environment
&lt;/h3&gt;

&lt;p&gt;อย่าฮาร์ดโค้ด secret หรือ URL ของ environment ลงใน test file ให้ใช้ environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API_BASE_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://staging.example.com/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่างการรัน:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;API_BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://staging.example.com/v1 pytest &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  รันแบบขนานเมื่อ test เป็นอิสระต่อกัน
&lt;/h3&gt;

&lt;p&gt;ติดตั้ง &lt;code&gt;pytest-xdist&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pytest-xdist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;รันแบบใช้ CPU core อัตโนมัติ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pytest &lt;span class="nt"&gt;-n&lt;/span&gt; auto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;การรันแบบขนานจะน่าเชื่อถือก็ต่อเมื่อ test ไม่แชร์ state และไม่ขึ้นกับลำดับการทำงาน ถ้า test ต้องสร้างข้อมูล ให้ใช้ fixture สร้างและ cleanup ข้อมูลของตัวเอง&lt;/p&gt;

&lt;h2&gt;
  
  
  การดูแลชุดทดสอบ pytest ให้บำรุงรักษาได้
&lt;/h2&gt;

&lt;p&gt;เมื่อจำนวน test เพิ่มจากหลักสิบเป็นหลักร้อย ให้เน้น 3 เรื่องนี้&lt;/p&gt;

&lt;h3&gt;
  
  
  1. แยก test ตาม domain
&lt;/h3&gt;

&lt;p&gt;ตัวอย่าง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test_users.py
test_orders.py
test_payments.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใช้ class เฉพาะเมื่อมี setup ร่วมกันจริง ๆ ไม่ใช่เพื่อจัดรูปแบบเท่านั้น ไฟล์ &lt;code&gt;test_orders.py&lt;/code&gt; ที่มีฟังก์ชันชัดเจนมักอ่านง่ายกว่าไฟล์ test ขนาดใหญ่ไฟล์เดียว&lt;/p&gt;

&lt;h3&gt;
  
  
  2. ใช้ marks เพื่อเลือก subset ของ test
&lt;/h3&gt;

&lt;p&gt;ตัวอย่าง smoke test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.mark.smoke&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_current_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_session&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/me&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;รันเฉพาะ smoke:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pytest &lt;span class="nt"&gt;-m&lt;/span&gt; smoke
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แนวทางทั่วไป:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;รัน &lt;code&gt;smoke&lt;/code&gt; ทุก commit&lt;/li&gt;
&lt;li&gt;รันชุดเต็มทุกคืน&lt;/li&gt;
&lt;li&gt;แยก &lt;code&gt;slow&lt;/code&gt; สำหรับ test ที่ใช้เวลานาน&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. รวม config และ helper ไว้จุดเดียว
&lt;/h3&gt;

&lt;p&gt;สิ่งที่ควรรวมศูนย์:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;base URL&lt;/li&gt;
&lt;li&gt;shared headers&lt;/li&gt;
&lt;li&gt;auth fixture&lt;/li&gt;
&lt;li&gt;schemas&lt;/li&gt;
&lt;li&gt;helper สำหรับสร้าง test data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;เมื่อ staging URL เปลี่ยน คุณควรแก้แค่จุดเดียว ไม่ใช่ไล่แก้หลายไฟล์ หลักการเดียวกับการออกแบบ test framework ทั่วไปใน &lt;a href="http://apidog.com/blog/how-to-write-automated-test-scripts?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การเขียนสคริปต์ทดสอบอัตโนมัติ&lt;/a&gt;: ถ้าเขียนซ้ำมากกว่าหนึ่งครั้ง ให้พิจารณาแยกเป็น fixture หรือ helper&lt;/p&gt;

&lt;h2&gt;
  
  
  เมื่อใดที่ควรใช้แพลตฟอร์มอื่นแทน
&lt;/h2&gt;

&lt;p&gt;pytest เหมาะมากเมื่อทีมเขียน Python และต้องการให้ test อยู่ใกล้กับ application code แต่จะไม่สะดวกเท่าไรเมื่อ QA หรือ product ต้องมีส่วนร่วม หรือเมื่อคุณต้องการออกแบบ API, mock, test และ run ในที่เดียวโดยไม่ต้องดูแลโค้ด fixture/assertion เอง&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; ช่วยเติมช่องว่างนี้ด้วย visual test builder, การตรวจ schema กับ OpenAPI spec, data-driven run จาก CSV/JSON และ CLI runner สำหรับ CI โดยไม่ต้องเขียน fixture และ assertion ด้วยตัวเอง หลายทีมใช้ทั้งสองแนวทางร่วมกัน: pytest สำหรับ scenario ที่มี logic ซับซ้อน และ &lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; สำหรับ coverage กว้าง ๆ รวมถึงการออกแบบและ mock API ที่ pytest ใช้ทดสอบ คุณสามารถ &lt;a href="https://apidog.com/download?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ดาวน์โหลด Apidog&lt;/a&gt; แล้วลองเทียบ workflow บน endpoint จริงได้ภายในช่วงบ่าย&lt;/p&gt;

&lt;h2&gt;
  
  
  คำถามที่พบบ่อย
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ทำไมถึงใช้ pytest แทน unittest ที่มาพร้อมกับ Python สำหรับการทดสอบ API?
&lt;/h3&gt;

&lt;p&gt;pytest ใช้ boilerplate น้อยกว่า test เป็นฟังก์ชันธรรมดา assertion ใช้ &lt;code&gt;assert&lt;/code&gt; ปกติแต่มี failure output ที่อ่านง่าย และ fixtures จัดการ setup ได้ยืดหยุ่นกว่า class-based setup ของ unittest นอกจากนี้ pytest ยังมี ecosystem plugin ขนาดใหญ่และมี &lt;code&gt;parametrize&lt;/code&gt; ในตัวสำหรับ data-driven testing&lt;/p&gt;

&lt;p&gt;pytest ยังสามารถรัน test แบบ unittest ที่มีอยู่แล้วได้ จึงลดความเสี่ยงในการย้ายระบบ&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixture กับ parametrize แตกต่างกันอย่างไร?
&lt;/h3&gt;

&lt;p&gt;Fixture ใช้จัดเตรียม resource ที่ test ต้องใช้ เช่น HTTP session, auth token หรือ test data&lt;/p&gt;

&lt;p&gt;&lt;code&gt;parametrize&lt;/code&gt; ใช้รัน test body เดียวกันหลายครั้งด้วย input ต่างกัน&lt;/p&gt;

&lt;p&gt;สรุปสั้น ๆ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fixture = แชร์ setup&lt;/li&gt;
&lt;li&gt;parametrize = เพิ่มจำนวน test case&lt;/li&gt;
&lt;li&gt;ใช้ร่วมกันได้ เช่น test แบบ parametrized ที่ใช้ &lt;code&gt;api_session&lt;/code&gt; fixture&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ควร assert response time ใน pytest API test หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ทำได้โดยใช้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;total_seconds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แต่ควรตั้ง threshold แบบหลวม ๆ เพื่อหลีกเลี่ยง flaky test จาก network jitter ปกติ pytest เหมาะกับ functional testing ไม่ใช่ load testing ถ้าต้องวัด performance จริง ควรใช้เครื่องมือเฉพาะทาง&lt;/p&gt;

&lt;h3&gt;
  
  
  จะรักษา API test ให้เป็นอิสระต่อกันใน pytest ได้อย่างไร?
&lt;/h3&gt;

&lt;p&gt;ใช้ fixtures เพื่อสร้างข้อมูลเฉพาะของแต่ละ test และ cleanup หลังใช้งาน หลีกเลี่ยงการให้ test หนึ่งพึ่งพาผลลัพธ์จากอีก test หนึ่ง แม้ pytest จะรันตามลำดับไฟล์โดยค่าเริ่มต้น แต่ test suite ที่ดีไม่ควรขึ้นกับลำดับนั้น&lt;/p&gt;

&lt;p&gt;test ที่เป็นอิสระจะรันแบบขนานได้ง่าย และ debug ง่ายกว่าเมื่อ fail&lt;/p&gt;

&lt;h3&gt;
  
  
  pytest สามารถตรวจ response เทียบกับ OpenAPI spec ได้หรือไม่?
&lt;/h3&gt;

&lt;p&gt;pytest เองไม่ได้ทำโดยตรง แต่คุณสามารถตรวจ JSON Schema ด้วย &lt;code&gt;jsonschema&lt;/code&gt; และมี plugin ที่ช่วยตรวจ response เทียบกับ OpenAPI document ได้ หาก schema validation เป็น workflow หลักของทีม แพลตฟอร์มอย่าง Apidog ที่ตรวจเทียบกับ OpenAPI spec อัตโนมัติอาจช่วยลดเวลาการตั้งค่า plugin และดูแล test infrastructure ได้มากขึ้น&lt;/p&gt;

</description>
    </item>
    <item>
      <title>วิธีทดสอบ API ด้วย Postman: คู่มือฉบับลงมือทำ</title>
      <dc:creator>Thanawat Wongchai</dc:creator>
      <pubDate>Fri, 22 May 2026 07:29:58 +0000</pubDate>
      <link>https://forem.com/thanawat_wonchai/withiithdsb-api-dwy-postman-khuumuuechbablngmuuetham-3pd6</link>
      <guid>https://forem.com/thanawat_wonchai/withiithdsb-api-dwy-postman-khuumuuechbablngmuuetham-3pd6</guid>
      <description>&lt;p&gt;Postman เป็นหนึ่งในเครื่องมือที่นักพัฒนาใช้บ่อยที่สุดสำหรับส่ง HTTP request และตรวจสอบพฤติกรรมของ API ตั้งแต่การลอง &lt;code&gt;GET&lt;/code&gt; แบบเร็ว ๆ ไปจนถึงการรัน test suite ใน CI ถ้าคุณสร้าง ดูแล หรือ consume API คุณมีโอกาสสูงที่จะต้องใช้ Postman ใน workflow ประจำวัน&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com/?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation" class="crayons-btn crayons-btn--primary"&gt;ลองใช้ Apidog วันนี้&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;คู่มือนี้สรุปวิธีทดสอบ API ใน Postman แบบลงมือทำจริง: ส่ง request, ตรวจ response, เขียน assertions ใน Tests, ตั้ง environments เพื่อสลับ staging/production และรันทั้ง collection ด้วย Collection Runner ตัวอย่างใช้ public API เพื่อให้ทำตามได้ทันทีโดยไม่ต้องตั้ง backend เอง&lt;/p&gt;

&lt;h2&gt;
  
  
  ตั้งค่า Postman และส่ง request แรก
&lt;/h2&gt;

&lt;p&gt;ดาวน์โหลด Postman จาก &lt;a href="https://www.postman.com/downloads/" rel="noopener noreferrer"&gt;เว็บไซต์ทางการ&lt;/a&gt; แล้วติดตั้งแอปเดสก์ท็อป คุณสามารถใช้งาน local ได้โดยไม่ต้องมีบัญชี แต่การลงชื่อเข้าใช้จะช่วย sync collections ระหว่างอุปกรณ์&lt;/p&gt;

&lt;p&gt;ขั้นตอนเริ่มต้น:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;เปิด Postman&lt;/li&gt;
&lt;li&gt;คลิก &lt;strong&gt;New&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;เลือก &lt;strong&gt;HTTP Request&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;เลือก method เป็น &lt;code&gt;GET&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ใส่ URL ต่อไปนี้
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET https://jsonplaceholder.typicode.com/users/1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;คลิก &lt;strong&gt;Send&lt;/strong&gt; แล้วดู response panel ซึ่งจะแสดง:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;response body&lt;/li&gt;
&lt;li&gt;status code เช่น &lt;code&gt;200 OK&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;response time&lt;/li&gt;
&lt;li&gt;response size&lt;/li&gt;
&lt;li&gt;มุมมอง body แบบ Pretty, Raw และ Preview&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;สำหรับ &lt;code&gt;POST&lt;/code&gt; ให้เปลี่ยน method เป็น &lt;code&gt;POST&lt;/code&gt; จากนั้นไปที่แท็บ &lt;strong&gt;Body&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;เลือก &lt;strong&gt;raw&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;เลือก format เป็น &lt;strong&gt;JSON&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ใส่ payload
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Maria Chen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"maria.chen@example.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เมื่อเลือก body เป็น JSON แล้ว Postman จะเพิ่ม header เช่น &lt;code&gt;Content-Type: application/json&lt;/code&gt; ให้โดยอัตโนมัติ ถ้า API ต้องการ header อื่น เช่น &lt;code&gt;Authorization&lt;/code&gt; ให้เพิ่มในแท็บ &lt;strong&gt;Headers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ถ้าคุณยังไม่แน่ใจว่าควรคาดหวัง status code แบบใดในแต่ละกรณี อ่านเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/which-http-status-codes-rest-apis-should-use?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;รหัสสถานะ HTTP ที่ REST API ควรใช้&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  จัด request เป็น collection
&lt;/h2&gt;

&lt;p&gt;request เดี่ยวเหมาะกับการตรวจแบบเร็ว ๆ แต่การทดสอบ API จริงมักเป็น flow หลายขั้นตอน เช่น:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;สร้างผู้ใช้&lt;/li&gt;
&lt;li&gt;ดึงข้อมูลผู้ใช้&lt;/li&gt;
&lt;li&gt;อัปเดตผู้ใช้&lt;/li&gt;
&lt;li&gt;ลบผู้ใช้&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ใน Postman ให้จัดกลุ่ม request เหล่านี้ด้วย &lt;strong&gt;Collection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;วิธีสร้าง collection:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;เปิดแถบ &lt;strong&gt;Collections&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;คลิกไอคอน &lt;strong&gt;+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ตั้งชื่อ เช่น &lt;code&gt;User API smoke tests&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;บันทึก request ด้วย &lt;strong&gt;Ctrl/Cmd + S&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ตั้งชื่อ request ให้สื่อความหมาย เช่น &lt;code&gt;Get user by ID&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;คุณยังสามารถสร้าง folder ภายใน collection เพื่อแยกประเภท request เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User API smoke tests
├── Auth
│   └── Login
├── Users
│   ├── Create user
│   ├── Get user
│   ├── Update user
│   └── Delete user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Collection ยังเป็นหน่วยที่ใช้แชร์และรันอัตโนมัติได้ คุณสามารถ export เป็น JSON หรือแชร์ผ่าน cloud workspace ได้ เมื่อเพื่อนร่วมทีมนำเข้า collection จะได้ request และ test เหมือนกัน&lt;/p&gt;

&lt;p&gt;Postman ยังรองรับ script ระดับ collection หรือ folder เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pre-request script&lt;/strong&gt;: รันก่อนทุก request เหมาะกับการ refresh token หรือสร้าง timestamp&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tests script&lt;/strong&gt;: รันหลังทุก request เหมาะกับ assertion กลาง เช่น ตรวจว่า response time ไม่เกิน threshold&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่าง test ระดับ collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Response time is acceptable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseTime&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;below&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;การใส่ logic ที่ใช้ซ้ำไว้ระดับ collection จะช่วยลด duplication ในแต่ละ request&lt;/p&gt;

&lt;h2&gt;
  
  
  เขียน test ในแท็บ Tests
&lt;/h2&gt;

&lt;p&gt;การส่ง request บอกว่า API ตอบอะไรกลับมา ส่วน test บอกว่า response นั้น “ถูกต้องหรือไม่” Postman รัน JavaScript หลังจากได้รับ response แล้ว โดยใช้ object หลักชื่อ &lt;code&gt;pm&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;รูปแบบพื้นฐานคือ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ชื่อ test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// assertion&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่าง assertions ที่ใช้บ่อย:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ตรวจ status code&lt;/span&gt;
&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Status code is 200&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ตรวจ response time&lt;/span&gt;
&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Response is under 500ms&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseTime&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;below&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ตรวจ field ใน JSON body&lt;/span&gt;
&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User has the expected email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;maria.chen@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// ตรวจ header&lt;/span&gt;
&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type is JSON&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Postman ใช้ assertion syntax จาก Chai ดังนั้น &lt;code&gt;pm.expect&lt;/code&gt; รองรับรูปแบบอย่างเช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;above&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;below&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หลังคลิก &lt;strong&gt;Send&lt;/strong&gt; ให้ดูผลที่แท็บ &lt;strong&gt;Test Results&lt;/strong&gt; แต่ละ test จะแสดงว่า pass หรือ fail&lt;/p&gt;

&lt;p&gt;แนวทางเขียน test ให้ดูแลต่อได้ง่าย:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ตั้งชื่อ &lt;code&gt;pm.test&lt;/code&gt; ตามพฤติกรรมที่ตรวจ เช่น &lt;code&gt;Status code is 200&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ตรวจสิ่งที่ client พึ่งพาจริง เช่น status code, schema, required fields&lt;/li&gt;
&lt;li&gt;หลีกเลี่ยงการ assert ค่าที่เปลี่ยนตลอด เช่น timestamp ที่ server สร้าง&lt;/li&gt;
&lt;li&gt;แยก assertion ให้ชัด เพื่อให้รู้ว่า fail เพราะอะไร&lt;/li&gt;
&lt;li&gt;ใช้แผง &lt;strong&gt;Snippets&lt;/strong&gt; ใน Postman เพื่อ insert assertion สำเร็จรูป&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ถ้าต้องการออกแบบ assertion ให้ดีขึ้น อ่านต่อได้ที่ &lt;a href="http://apidog.com/blog/api-assertions?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;คำยืนยัน API และวิธีเขียนให้ดี&lt;/a&gt; และ &lt;a href="http://apidog.com/blog/api-test-case-example?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ตัวอย่างกรณีทดสอบ API&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ใช้ environments และ variables
&lt;/h2&gt;

&lt;p&gt;อย่า hard-code base URL เช่น &lt;code&gt;https://api.staging.example.com&lt;/code&gt; ลงในทุก request เพราะเมื่อสลับไป production คุณจะต้องแก้หลายจุด Postman แก้ปัญหานี้ด้วย &lt;strong&gt;Environments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;สร้าง environment:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;เปิดเมนู environments&lt;/li&gt;
&lt;li&gt;สร้าง environment ชื่อ &lt;code&gt;Staging&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;เพิ่ม variable ชื่อ &lt;code&gt;base_url&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ตั้งค่าเป็น &lt;code&gt;https://jsonplaceholder.typicode.com&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;จากนั้นแก้ request เป็น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET {{base_url}}/users/1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เมื่อเลือก environment จาก dropdown มุมขวาบน ทุก request ที่ใช้ &lt;code&gt;{{base_url}}&lt;/code&gt; จะเปลี่ยนตาม environment ทันที&lt;/p&gt;

&lt;p&gt;ตัวแปรยังใช้ส่งข้อมูลระหว่าง request ได้ เช่น login request ดึง token จาก response แล้วเก็บไว้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Save the auth token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auth_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;request ถัดไปสามารถใช้ token ได้ใน header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Authorization: Bearer {{auth_token}}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;scope ของ variable ที่ควรรู้:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;ใช้เมื่อ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Environment variables&lt;/td&gt;
&lt;td&gt;ค่าที่เปลี่ยนตาม environment เช่น &lt;code&gt;base_url&lt;/code&gt;, &lt;code&gt;auth_token&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collection variables&lt;/td&gt;
&lt;td&gt;ค่าคงที่ของ collection เช่น API version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Global variables&lt;/td&gt;
&lt;td&gt;ค่าที่ใช้ได้ทุก collection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local variables&lt;/td&gt;
&lt;td&gt;ค่าชั่วคราวระหว่างการรัน&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;เลือก scope ให้เหมาะสมเพื่อลดปัญหา variable ชนกันหรือเปลี่ยนค่าผิด environment&lt;/p&gt;

&lt;h2&gt;
  
  
  รันทั้ง collection ด้วย Collection Runner
&lt;/h2&gt;

&lt;p&gt;การกด &lt;strong&gt;Send&lt;/strong&gt; ทีละ request ไม่เหมาะกับ regression test ให้ใช้ &lt;strong&gt;Collection Runner&lt;/strong&gt; เพื่อรันทุก request ตามลำดับและสรุปผล pass/fail&lt;/p&gt;

&lt;p&gt;ขั้นตอน:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;เปิด collection&lt;/li&gt;
&lt;li&gt;คลิก &lt;strong&gt;Run&lt;/strong&gt; หรือเลือกเมนูสามจุดแล้วคลิก &lt;strong&gt;Run collection&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;เลือก environment&lt;/li&gt;
&lt;li&gt;กำหนดจำนวน iterations&lt;/li&gt;
&lt;li&gt;แนบ data file ถ้าต้องการ&lt;/li&gt;
&lt;li&gt;คลิก &lt;strong&gt;Run&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Runner จะส่ง request จากบนลงล่างและรัน test ของแต่ละ request&lt;/p&gt;

&lt;p&gt;ลำดับ request สำคัญมาก เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Login
2. Create user
3. Get user
4. Update user
5. Delete user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้า request แรกเก็บ &lt;code&gt;auth_token&lt;/code&gt; ไว้ request ถัดไปก็สามารถใช้ &lt;code&gt;{{auth_token}}&lt;/code&gt; ได้ทันที&lt;/p&gt;

&lt;p&gt;หน้าผลลัพธ์จะแสดง:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;จำนวน tests ที่ผ่าน&lt;/li&gt;
&lt;li&gt;จำนวน tests ที่ไม่ผ่าน&lt;/li&gt;
&lt;li&gt;รายละเอียด assertion ของแต่ละ request&lt;/li&gt;
&lt;li&gt;ประวัติการรันก่อนหน้า&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ใช้ Runner หลัง deploy เพื่อเช็ค regression ได้เร็วขึ้น&lt;/p&gt;

&lt;h2&gt;
  
  
  ทำ data-driven testing ด้วย CSV หรือ JSON
&lt;/h2&gt;

&lt;p&gt;ถ้าต้องการทดสอบ input หลายชุด ให้แนบ CSV หรือ JSON ใน Collection Runner แต่ละ row จะกลายเป็นหนึ่ง iteration และเรียกใช้ variable ได้ เช่น &lt;code&gt;{{username}}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;ตัวอย่าง CSV:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;username,password,expected_status
valid_user,correct_password,200
wrong_user,bad_password,401
empty_user,,400
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใน request ใช้ variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{username}}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{password}}"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใน Tests ตรวจ expected status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Status code matches expected status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iterationData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;expected_status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;วิธีนี้ช่วยให้ request เดียวทดสอบหลายกรณีได้โดยไม่ต้อง copy request ซ้ำ อ่านเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/data-driven-api-testing-tool-csv-json?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การทดสอบ API ที่ขับเคลื่อนด้วยข้อมูลโดยใช้ CSV และ JSON&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ถ้าต้องการรัน collection เดียวกันใน CI โดยไม่มี GUI ให้ใช้ Newman ซึ่งเป็น CLI runner ของ Postman ดูรายละเอียดเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/what-is-the-difference-between-newman-and-postman?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Newman กับ Postman&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  รัน Postman test ใน CI ด้วย Newman
&lt;/h2&gt;

&lt;p&gt;Newman ใช้รัน Postman collection จาก command line ได้ เหมาะกับ GitHub Actions, GitLab CI, Jenkins หรือ pipeline อื่น ๆ&lt;/p&gt;

&lt;p&gt;ติดตั้ง Newman:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; newman
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;รัน collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newman run collection.json &lt;span class="nt"&gt;-e&lt;/span&gt; environment.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้ามี test fail Newman จะ exit ด้วย non-zero code ทำให้ CI job fail ได้โดยอัตโนมัติ&lt;/p&gt;

&lt;p&gt;ตัวอย่าง GitHub Actions แบบพื้นฐาน:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;API Tests&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postman-tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Newman&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install -g newman&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Postman collection&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;newman run collection.json -e environment.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;อ่านเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/automate-api-tests-ci-cd?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การทำให้การทดสอบ API เป็นอัตโนมัติใน CI/CD&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  จุดที่ Postman เริ่มมีข้อจำกัด
&lt;/h2&gt;

&lt;p&gt;Postman เหมาะกับ exploratory testing และ test suite ขนาดเล็กถึงกลาง แต่เมื่อโปรเจกต์โตขึ้นมักเจอข้อจำกัดหลัก ๆ สองเรื่อง:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Assertions ต้องเขียนด้วย JavaScript&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
ทีม QA หรือคนที่ไม่ถนัด code อาจต้องใช้เวลาเรียนรู้เพิ่ม&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;API design, testing, mocking และ docs อาจแยกกันอยู่&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
ถ้า spec, test และ docs ไม่ได้ใช้ source เดียวกัน อาจเกิด drift ได้ เช่น docs บอกอย่างหนึ่ง แต่ test เช็คอีกอย่าง&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; เป็นแพลตฟอร์ม API แบบครบวงจรที่ช่วยรวมการออกแบบ ดีบัก mock test และ documentation ไว้ใน workflow เดียวกัน และสามารถ import Postman collection ได้โดยตรง จึงไม่จำเป็นต้องเริ่มใหม่ทั้งหมด นอกจากนี้ยังรองรับการสร้าง assertion แบบ visual โดยไม่ต้องเขียน code ในกรณีที่ทีมต้องการ workflow ที่เข้าถึงง่ายขึ้น&lt;/p&gt;

&lt;p&gt;ถ้ากำลังเปรียบเทียบตัวเลือกอื่น อ่านต่อได้ที่ &lt;a href="http://apidog.com/blog/best-postman-alternatives-for-api-testing?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ทางเลือก Postman ที่ดีที่สุดสำหรับการทดสอบ API&lt;/a&gt; หรือ &lt;a href="https://apidog.com/download?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ดาวน์โหลด Apidog&lt;/a&gt; เพื่อนำเข้า collection ที่มีอยู่แล้วลองเทียบ workflow โดยตรง&lt;/p&gt;

&lt;p&gt;ไม่ได้หมายความว่าต้องเลิกใช้ Postman ทันที Postman ยังเป็นเครื่องมือที่แข็งแรงมาก โดยเฉพาะสำหรับการตรวจสอบอย่างรวดเร็ว ทีมที่ใช้ Postman อยู่แล้วสามารถเริ่มจากการจัด collection, เพิ่ม assertions และเชื่อม CI ก่อน จากนั้นค่อยพิจารณาเครื่องมืออื่นเมื่อ workflow เริ่มซับซ้อนขึ้น&lt;/p&gt;

&lt;h2&gt;
  
  
  คำถามที่พบบ่อย
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ฉันต้องเขียนโค้ดเพื่อทดสอบ API ใน Postman หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ถ้าแค่ส่ง request และดู response ไม่จำเป็นต้องเขียนโค้ด แต่ถ้าต้องการ automated assertions ต้องเขียน JavaScript ใน Tests อย่างน้อยเล็กน้อย โดยใช้ object &lt;code&gt;pm&lt;/code&gt; ของ Postman คุณสามารถเริ่มจาก Snippets ที่ Postman มีให้ แล้วปรับตาม endpoint ของคุณ&lt;/p&gt;

&lt;h3&gt;
  
  
  Collection และ Environment ใน Postman ต่างกันอย่างไร?
&lt;/h3&gt;

&lt;p&gt;Collection คือชุดของ request และ tests ที่จัดกลุ่มเข้าด้วยกัน ส่วน Environment คือชุดของ variables เช่น &lt;code&gt;base_url&lt;/code&gt; หรือ &lt;code&gt;auth_token&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;พูดสั้น ๆ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collection = สิ่งที่คุณจะส่ง&lt;/li&gt;
&lt;li&gt;Environment = ค่าที่เปลี่ยนตาม target เช่น staging, production หรือ local&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;คุณสามารถใช้ collection เดียวกันกับหลาย environment ได้&lt;/p&gt;

&lt;h3&gt;
  
  
  ฉันจะรัน Postman test อัตโนมัติใน CI ได้อย่างไร?
&lt;/h3&gt;

&lt;p&gt;ใช้ Newman โดย export collection และ environment เป็น JSON จากนั้นรัน:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newman run collection.json &lt;span class="nt"&gt;-e&lt;/span&gt; environment.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้า test ใด fail Newman จะส่ง exit code ที่ไม่ใช่ศูนย์ ทำให้ CI pipeline ตรวจจับความล้มเหลวได้&lt;/p&gt;

&lt;h3&gt;
  
  
  Postman ทดสอบ API ได้มากกว่า REST หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ได้ Postman เวอร์ชันใหม่รองรับ GraphQL, gRPC, WebSocket และ SOAP นอกเหนือจาก HTTP/REST ทั่วไป แต่รูปแบบการตั้งค่า request จะแตกต่างกันตาม protocol&lt;/p&gt;

&lt;h3&gt;
  
  
  request หนึ่งควรมี assertion กี่รายการ?
&lt;/h3&gt;

&lt;p&gt;ควรมีเท่าที่จำเป็นเพื่อยืนยันว่า response ถูกต้อง โดยทั่วไปเริ่มจาก:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;status code&lt;/li&gt;
&lt;li&gt;response time&lt;/li&gt;
&lt;li&gt;required fields&lt;/li&gt;
&lt;li&gt;ค่าที่ client ต้องใช้จริง&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;หลีกเลี่ยงการ assert ทุก field โดยไม่จำเป็น เพราะจะทำให้ test เปราะและ fail ง่ายเมื่อมีการเปลี่ยนแปลงเล็กน้อย เก็บแต่ละ &lt;code&gt;pm.test&lt;/code&gt; ให้ตรวจ expectation เดียวเพื่อให้ debug ได้ง่ายขึ้น&lt;/p&gt;

</description>
    </item>
    <item>
      <title>ทางเลือก Postman ที่ดีที่สุดสำหรับการทดสอบ API</title>
      <dc:creator>Thanawat Wongchai</dc:creator>
      <pubDate>Fri, 22 May 2026 07:29:21 +0000</pubDate>
      <link>https://forem.com/thanawat_wonchai/thaangeluuek-postman-thiidiithiisudsamhrabkaarthdsb-api-1ajk</link>
      <guid>https://forem.com/thanawat_wonchai/thaangeluuek-postman-thiidiithiisudsamhrabkaarthdsb-api-1ajk</guid>
      <description>&lt;p&gt;Postman เป็นไคลเอนต์ API ที่นักพัฒนาคุ้นเคยมานาน และยังคงเป็นเครื่องมือที่มีประสิทธิภาพ แต่วันนี้ไม่ใช่ตัวเลือกเดียวที่เหมาะกับทุกทีมอีกต่อไป โดยเฉพาะเมื่อทีมต้องการเครื่องมือที่เบากว่า ใช้งานออฟไลน์ได้ง่ายกว่า หรือควบคุมค่าใช้จ่ายต่อผู้ใช้ได้ดีกว่า&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com/?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation" class="crayons-btn crayons-btn--primary"&gt;ลองใช้ Apidog วันนี้&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;บทความนี้เปรียบเทียบทางเลือกของ Postman สำหรับการทดสอบ API แบบเน้นใช้งานจริง: เครื่องมือไหนเหมาะกับเวิร์กโฟลว์แบบเดสก์ท็อป ออฟไลน์ เบราว์เซอร์ Git หรือ VS Code และควรเลือกอย่างไรให้เข้ากับทีมของคุณ&lt;/p&gt;

&lt;h2&gt;
  
  
  ทำไมนักพัฒนาจึงมองหาทางเลือกอื่นนอกเหนือจาก Postman
&lt;/h2&gt;

&lt;p&gt;เหตุผลหลักไม่ได้แปลว่า Postman เป็นเครื่องมือที่ไม่ดี แต่สะท้อนว่าเวิร์กโฟลว์ของทีมต่างกัน&lt;/p&gt;

&lt;p&gt;ประเด็นที่พบบ่อยมี 3 เรื่อง:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ขนาดและความซับซ้อนของแอป&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
ถ้าคุณต้องการแค่ส่ง request, ตรวจ response และรัน test ง่าย ๆ Postman อาจรู้สึกใหญ่เกินความจำเป็น&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;บัญชีและ cloud sync&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
บางทีม โดยเฉพาะทีมที่อยู่ใน environment ที่มีข้อกำกับ ต้องการเก็บข้อมูลไว้ในเครื่องหรือ repository ของตัวเองมากกว่า&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ค่าใช้จ่ายเมื่อทีมโตขึ้น&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
ฟีเจอร์ collaboration มีประโยชน์ แต่ราคาต่อผู้ใช้อาจกลายเป็นต้นทุนสำคัญเมื่อทีมขยาย&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ถ้าเป้าหมายของคุณคือปรับโครงสร้างการทดสอบ API โดยไม่ยึดติดกับเครื่องมือเดียว อ่านเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/api-testing-without-postman-2026?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ทดสอบ API โดยไม่ต้องใช้ Postman&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ทางเลือกที่น่าพิจารณา
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Apidog
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; เป็นแพลตฟอร์ม API แบบครบวงจรที่รวมการออกแบบ API, debugging, automated testing, mocking และ documentation ไว้ในแอปเดียว&lt;/p&gt;

&lt;p&gt;เหมาะกับทีมที่ต้องการ workflow ต่อเนื่องตั้งแต่ design → test → mock → document โดยไม่ต้องต่อเครื่องมือหลายตัวเข้าด้วยกัน&lt;/p&gt;

&lt;p&gt;สิ่งที่ทำได้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;สร้างและส่ง request สำหรับ REST, GraphQL, SOAP และ WebSocket&lt;/li&gt;
&lt;li&gt;สร้าง test scenario จากหลาย request&lt;/li&gt;
&lt;li&gt;เพิ่ม assertion แบบ visual โดยไม่จำเป็นต้องเขียน script ทุกจุด&lt;/li&gt;
&lt;li&gt;mock endpoint ที่ยังพัฒนาไม่เสร็จ&lt;/li&gt;
&lt;li&gt;สร้างเอกสาร API จากข้อมูลเดียวกับที่ใช้ทดสอบ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่าง workflow ที่เหมาะกับ Apidog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. ออกแบบ endpoint /login
2. สร้าง mock response ให้ frontend ใช้ก่อน backend เสร็จ
3. สร้าง request สำหรับ login
4. ดึง token จาก response
5. ใช้ token เรียก endpoint ที่ต้อง authenticate
6. เพิ่ม assertion เช่น status = 200 และ response.user.id ต้องมีค่า
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://apidog.com/download?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ดาวน์โหลด Apidog&lt;/a&gt; หากต้องการทดลอง workflow เต็มรูปแบบตั้งแต่การออกแบบจนถึงการทดสอบ&lt;/p&gt;

&lt;h3&gt;
  
  
  Insomnia
&lt;/h3&gt;

&lt;p&gt;Insomnia เป็น desktop client ที่ UI สะอาด ใช้งานง่าย และรองรับ REST, GraphQL และ gRPC เหมาะกับนักพัฒนาที่ต้องการเครื่องมือเบากว่า Postman แต่ยังต้องการ GUI ที่ดี&lt;/p&gt;

&lt;p&gt;เหมาะกับงานประเภท:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ส่ง request และตรวจ response แบบ manual&lt;/li&gt;
&lt;li&gt;ทดสอบ GraphQL query/mutation&lt;/li&gt;
&lt;li&gt;ใช้งาน gRPC ใน desktop client&lt;/li&gt;
&lt;li&gt;จัดการ environment พื้นฐาน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ข้อจำกัดคือ automation และ test runner มีขอบเขตเล็กกว่าเครื่องมือที่เน้น automated testing โดยตรง&lt;/p&gt;

&lt;p&gt;ดูตัวอย่าง workflow ได้ที่ &lt;a href="http://apidog.com/blog/how-to-use-insomnia-test-api?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;วิธีการใช้ Insomnia เพื่อทดสอบ API&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Hoppscotch
&lt;/h3&gt;

&lt;p&gt;Hoppscotch เป็นเครื่องมือ open source ที่ทำงานใน browser ไม่ต้องติดตั้ง เหมาะสำหรับการทดสอบเร็ว ๆ หรือใช้งานส่วนตัว&lt;/p&gt;

&lt;p&gt;เหมาะกับกรณีเหล่านี้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ต้องการทดสอบ API จาก browser ทันที&lt;/li&gt;
&lt;li&gt;ไม่ต้องการติดตั้ง desktop app&lt;/li&gt;
&lt;li&gt;ใช้ REST, GraphQL หรือ WebSocket&lt;/li&gt;
&lt;li&gt;ต้องการแชร์ request แบบง่าย&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ข้อจำกัดคือ automation และ team features มีขอบเขตเบากว่าเครื่องมือทดสอบเฉพาะทาง และฟีเจอร์สำหรับทีมบางส่วนอยู่ในแผนแบบชำระเงิน&lt;/p&gt;

&lt;h3&gt;
  
  
  Bruno
&lt;/h3&gt;

&lt;p&gt;Bruno มีจุดเด่นคือเก็บ collection เป็นไฟล์ข้อความธรรมดาบน disk และสามารถ version control ด้วย Git ได้โดยตรง&lt;/p&gt;

&lt;p&gt;เหมาะกับทีมที่ต้องการให้ API tests อยู่ใน repository เดียวกับ code เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/api-tests
  /auth
    login.bru
    refresh-token.bru
  /users
    get-user.bru
    update-user.bru
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ข้อดีหลัก:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ไม่มี cloud บังคับ&lt;/li&gt;
&lt;li&gt;ไม่ต้องพึ่งบัญชีผู้ใช้สำหรับการเก็บ collection&lt;/li&gt;
&lt;li&gt;review การเปลี่ยนแปลง request/test ผ่าน pull request ได้&lt;/li&gt;
&lt;li&gt;เหมาะกับทีมที่ใช้ Git เป็นศูนย์กลางของ workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ข้อจำกัดคือ Bruno ยังใหม่กว่าเครื่องมืออื่น ฟีเจอร์ขั้นสูงบางส่วนจึงยังอยู่ระหว่างการพัฒนา&lt;/p&gt;

&lt;h3&gt;
  
  
  Thunder Client
&lt;/h3&gt;

&lt;p&gt;Thunder Client เป็น extension ของ VS Code เหมาะกับนักพัฒนาที่ต้องการทดสอบ API โดยไม่ออกจาก editor&lt;/p&gt;

&lt;p&gt;เหมาะกับกรณี:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เปิด VS Code อยู่ตลอดเวลา&lt;/li&gt;
&lt;li&gt;ต้องการส่ง request ระหว่างเขียน backend&lt;/li&gt;
&lt;li&gt;ต้องการ API client ที่เบาและไม่ซับซ้อน&lt;/li&gt;
&lt;li&gt;ใช้ REST หรือ GraphQL เป็นหลัก&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แผนฟรีเหมาะกับการใช้งานเดี่ยว ส่วน Git sync และฟีเจอร์สำหรับทีมมีค่าใช้จ่าย&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTPie
&lt;/h3&gt;

&lt;p&gt;HTTPie เป็น HTTP client แบบ command-line ที่ออกแบบให้อ่านง่าย เหมาะกับ developer ที่ทำงานผ่าน terminal&lt;/p&gt;

&lt;p&gt;ตัวอย่างการเรียก API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;http GET https://api.example.com/users Authorization:&lt;span class="s2"&gt;"Bearer &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่าง POST JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;http POST https://api.example.com/login &lt;span class="nv"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev@example.com &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เหมาะกับ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;manual check อย่างรวดเร็ว&lt;/li&gt;
&lt;li&gt;shell script&lt;/li&gt;
&lt;li&gt;CI step แบบง่าย&lt;/li&gt;
&lt;li&gt;developer ที่ชอบ command-line&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ข้อจำกัดคือ HTTPie ไม่ใช่ test platform เต็มรูปแบบที่มี collection, scenario runner และ visual assertion แบบเครื่องมือเฉพาะทาง&lt;/p&gt;

&lt;h2&gt;
  
  
  ตารางเปรียบเทียบ
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;เครื่องมือ&lt;/th&gt;
&lt;th&gt;ประเภท&lt;/th&gt;
&lt;th&gt;โปรโตคอล&lt;/th&gt;
&lt;th&gt;จุดเด่น&lt;/th&gt;
&lt;th&gt;ข้อเสียที่ตรงไปตรงมา&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Apidog&lt;/td&gt;
&lt;td&gt;แพลตฟอร์มเดสก์ท็อป&lt;/td&gt;
&lt;td&gt;REST, GraphQL, SOAP, WebSocket&lt;/td&gt;
&lt;td&gt;ออกแบบ, ทดสอบ, จำลอง, เอกสารรวมเป็นหนึ่งเดียว&lt;/td&gt;
&lt;td&gt;ทีมขนาดใหญ่ต้องจ่ายค่าที่นั่ง&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insomnia&lt;/td&gt;
&lt;td&gt;ไคลเอนต์เดสก์ท็อป&lt;/td&gt;
&lt;td&gt;REST, GraphQL, gRPC&lt;/td&gt;
&lt;td&gt;UI สะอาดและเน้นการใช้งาน&lt;/td&gt;
&lt;td&gt;ชุดฟีเจอร์อัตโนมัติเล็กกว่า&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hoppscotch&lt;/td&gt;
&lt;td&gt;เบราว์เซอร์, open source&lt;/td&gt;
&lt;td&gt;REST, GraphQL, WebSocket&lt;/td&gt;
&lt;td&gt;ไม่ต้องติดตั้ง, ใช้งานเร็ว&lt;/td&gt;
&lt;td&gt;automation เบากว่า, ฟีเจอร์ทีมต้องจ่ายเงิน&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bruno&lt;/td&gt;
&lt;td&gt;เดสก์ท็อป, อิงไฟล์&lt;/td&gt;
&lt;td&gt;REST, GraphQL&lt;/td&gt;
&lt;td&gt;collection เป็น text file และเข้ากับ Git&lt;/td&gt;
&lt;td&gt;ใหม่กว่า, ยังอยู่ระหว่างพัฒนา&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thunder Client&lt;/td&gt;
&lt;td&gt;VS Code extension&lt;/td&gt;
&lt;td&gt;REST, GraphQL&lt;/td&gt;
&lt;td&gt;ทดสอบ API ใน editor&lt;/td&gt;
&lt;td&gt;sync และฟีเจอร์ทีมต้องจ่ายเงิน&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTPie&lt;/td&gt;
&lt;td&gt;CLI และแอป&lt;/td&gt;
&lt;td&gt;REST&lt;/td&gt;
&lt;td&gt;เร็ว, script ได้, อ่านง่าย&lt;/td&gt;
&lt;td&gt;ไม่ใช่ runner สำหรับ test suite เต็มรูปแบบ&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  วิธีเลือกเครื่องมือที่เหมาะสม
&lt;/h2&gt;

&lt;p&gt;ให้เริ่มจากข้อจำกัดที่คุณยอมไม่ได้ก่อน แล้วค่อยเลือกเครื่องมือ&lt;/p&gt;

&lt;p&gt;ใช้แนวทางนี้ได้ทันที:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ต้องการ workflow ครบตั้งแต่ design, test, mock, document → เลือก &lt;strong&gt;Apidog&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ต้องการ desktop client ที่เรียบและเบา → เลือก &lt;strong&gt;Insomnia&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ไม่ต้องการติดตั้งอะไร → เลือก &lt;strong&gt;Hoppscotch&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ต้องการเก็บ API collection ใน Git และ review ผ่าน pull request → เลือก &lt;strong&gt;Bruno&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ต้องการทดสอบใน VS Code → เลือก &lt;strong&gt;Thunder Client&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;ทำงานผ่าน terminal เป็นหลัก → เลือก &lt;strong&gt;HTTPie&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;วิธีทดลองที่ยุติธรรมคือเลือก workflow จริงหนึ่งชุด แล้วทำซ้ำในแต่ละเครื่องมือ เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. POST /login
2. ตรวจว่า status code เป็น 200
3. ดึง access_token จาก response
4. GET /me พร้อม Authorization header
5. ตรวจว่า response มี user.id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่าง assertion ที่ควรมี:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status code = 200
response.body.access_token exists
response.body.user.id is not empty
response time &amp;lt; 1000ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เครื่องมือที่ทำ workflow นี้ได้ลื่นที่สุด และคุณคิดว่าจะใช้ทุกวันได้จริง คือคำตอบที่เหมาะกับทีมคุณ&lt;/p&gt;

&lt;p&gt;อ่านเพิ่มเติมเกี่ยวกับการจัดโครงสร้าง assertion ได้ที่ &lt;a href="http://apidog.com/blog/api-assertions?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;เขียน API assertion ที่มีประโยชน์&lt;/a&gt; และถ้าต้องการแยกขอบเขตการทดสอบให้ชัดขึ้น ดู &lt;a href="http://apidog.com/blog/test-scenario-vs-test-case?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Test Scenario กับ Test Case&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  การย้ายจาก Postman
&lt;/h2&gt;

&lt;p&gt;เครื่องมือส่วนใหญ่สามารถ import collection จาก Postman ได้โดยตรง เช่น Apidog, Insomnia, Hoppscotch และ Bruno&lt;/p&gt;

&lt;p&gt;ขั้นตอน migration ที่แนะนำ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Export Postman collection
2. Export environment ที่เกี่ยวข้อง
3. Import เข้าเครื่องมือใหม่
4. ตรวจ request สำคัญทีละชุด
5. ตรวจ environment variables
6. ป้อน secret ใหม่ด้วยตนเอง
7. รัน test และเทียบผลลัพธ์กับของเดิม
8. ตรวจ integration กับ CI/CD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สิ่งที่ควรระวัง:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript test script ของ Postman อาจต้องปรับ&lt;/li&gt;
&lt;li&gt;visual assertion ของบางเครื่องมืออาจใช้แนวคิดต่างจาก Postman script&lt;/li&gt;
&lt;li&gt;environment variables มักย้ายได้ แต่ secret ไม่ควรถูกส่งออกไปพร้อมไฟล์&lt;/li&gt;
&lt;li&gt;ถ้าเคยใช้ Newman ใน CI ต้องตรวจว่าเครื่องมือใหม่มี runner หรือ CLI แบบใด&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่างสิ่งที่ควรตรวจหลัง import:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- base_url ถูกต้องหรือไม่
- token ถูกส่งผ่าน header ถูกต้องหรือไม่
- pre-request script ยังจำเป็นอยู่หรือไม่
- assertion เดิมยังทำงานหรือไม่
- test data ถูกแยกจาก secret หรือไม่
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้าคุณต้องการวาง API tests ใน pipeline อ่านเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/automate-api-tests-ci-cd?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ทำให้การทดสอบ API เป็นไปโดยอัตโนมัติใน CI/CD&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ประเด็นสำคัญคือ การเปลี่ยนเครื่องมือไม่ได้ทำให้ test ดีขึ้นเอง Request ที่ไม่มี assertion ยังเป็น test ที่อ่อนแอไม่ว่าจะใช้ client ใดก็ตาม ใช้ช่วง migration เป็นโอกาสเพิ่ม assertion, ครอบคลุม error path และตรวจว่า API ใช้ &lt;a href="http://apidog.com/blog/which-http-status-codes-rest-apis-should-use?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;รหัสสถานะ HTTP ที่ REST API ควรใช้&lt;/a&gt; อย่างถูกต้อง&lt;/p&gt;

&lt;h2&gt;
  
  
  การเลือกเครื่องมือให้เหมาะสมกับขนาดทีม
&lt;/h2&gt;

&lt;h3&gt;
  
  
  นักพัฒนาเดี่ยว
&lt;/h3&gt;

&lt;p&gt;ให้เลือกจากความสะดวกและความเร็วในการใช้งาน&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ต้องการเริ่มเร็วที่สุด → Hoppscotch&lt;/li&gt;
&lt;li&gt;ใช้ VS Code เป็นหลัก → Thunder Client&lt;/li&gt;
&lt;li&gt;ใช้ terminal เป็นหลัก → HTTPie&lt;/li&gt;
&lt;li&gt;ต้องการเครื่องมือที่โตไปพร้อมโปรเจกต์ → Apidog&lt;/li&gt;
&lt;li&gt;ต้องการเก็บไฟล์ใน Git ตั้งแต่แรก → Bruno&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ทีมขนาดเล็ก 2–10 คน
&lt;/h3&gt;

&lt;p&gt;ปัญหาหลักคือการแชร์ collection และลดการส่งไฟล์ไปมา&lt;/p&gt;

&lt;p&gt;ทางเลือกที่เหมาะ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bruno&lt;/strong&gt;: เหมาะกับทีมที่ใช้ Git และ pull request เป็น workflow หลัก&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apidog&lt;/strong&gt;: เหมาะกับทีมที่ต้องการ shared project และ visual workflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Insomnia / Hoppscotch&lt;/strong&gt;: เหมาะกับทีมที่ต้องการ client เรียบง่ายและไม่ต้องการระบบ lifecycle เต็มรูปแบบ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;คำถามที่ควรถามก่อนเลือก:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ทีม review API changes ผ่าน Git หรือผ่าน UI?
ต้องแชร์ environment อย่างไร?
ใครมีสิทธิแก้ collection?
ต้องมี mock server หรือไม่?
ต้อง generate documentation หรือไม่?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  องค์กรขนาดใหญ่
&lt;/h3&gt;

&lt;p&gt;เมื่อทีมโตขึ้น สิ่งที่ต้องคิดเพิ่มคือ governance&lt;/p&gt;

&lt;p&gt;ตรวจประเด็นเหล่านี้ก่อนตัดสินใจ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;role และ permission&lt;/li&gt;
&lt;li&gt;การจัดการ environment&lt;/li&gt;
&lt;li&gt;การจัดการ secret&lt;/li&gt;
&lt;li&gt;audit และ review process&lt;/li&gt;
&lt;li&gt;การเชื่อมโยงระหว่าง API design, test และ documentation&lt;/li&gt;
&lt;li&gt;ค่าใช้จ่ายต่อ user เมื่อทีมขยายในอีก 12 เดือน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;สำหรับทีมขนาดใหญ่ เครื่องมือที่ครอบคลุม lifecycle ของ API ได้มากกว่า อาจช่วยลดจำนวน product ที่ต้องดูแลและลด fragmentation ของข้อมูล API&lt;/p&gt;

&lt;h2&gt;
  
  
  การรองรับโปรโตคอลควรเป็นปัจจัยในการตัดสินใจอย่างไร
&lt;/h2&gt;

&lt;p&gt;อย่าเลือกเครื่องมือจาก UI อย่างเดียว ให้เริ่มจาก protocol ที่ระบบของคุณใช้จริง&lt;/p&gt;

&lt;p&gt;แนวทางคัดกรอง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ถ้าใช้ REST เท่านั้น:
  เครื่องมือทั้งหมดในรายการนี้ใช้ได้

ถ้าใช้ GraphQL:
  เครื่องมือทั้งหมดในรายการนี้ยังเป็นตัวเลือกได้

ถ้าใช้ WebSocket:
  พิจารณา Apidog หรือ Hoppscotch

ถ้าใช้ gRPC:
  พิจารณา Insomnia หรือเครื่องมือที่รองรับ gRPC โดยตรง

ถ้าใช้ SOAP:
  เลือกเครื่องมือที่รองรับ SOAP โดยตรง เช่น Apidog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SOAP ยังพบได้ในองค์กรและระบบการเงินบางแห่ง การทดสอบ SOAP ด้วย XML ธรรมดาอาจทำได้ แต่ไม่สะดวกเท่าเครื่องมือที่รองรับโดยตรง ดูแนวทางเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/online-soap-api-tester?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ทดสอบ SOAP API ออนไลน์&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ก่อนเปรียบเทียบฟีเจอร์อื่น ให้ทำรายการ protocol ทั้งหมดที่ทีมใช้ แล้วตัดเครื่องมือที่ไม่รองรับออกก่อน&lt;/p&gt;

&lt;h2&gt;
  
  
  คำถามที่พบบ่อย
&lt;/h2&gt;

&lt;h3&gt;
  
  
  มีทางเลือกฟรีของ Postman ที่ทำทุกอย่างที่ Postman ทำได้หรือไม่?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; และ Hoppscotch มีแผนฟรีที่ครอบคลุมงานหลัก เช่น การสร้าง request, environment, assertion และ automated test บางรูปแบบ&lt;/p&gt;

&lt;p&gt;Apidog ยังรวม design, mocking และ documentation ไว้ใน workflow เดียวกัน ซึ่งเหมาะกับนักพัฒนาและทีมขนาดเล็กที่ต้องการมากกว่า API client ธรรมดา&lt;/p&gt;

&lt;h3&gt;
  
  
  ฉันสามารถนำเข้า collection Postman เข้าเครื่องมือเหล่านี้ได้หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ได้ Apidog, Insomnia, Hoppscotch และ Bruno รองรับการ import รูปแบบ export ของ Postman&lt;/p&gt;

&lt;p&gt;โดยทั่วไป request, folder และ environment variables จะย้ายได้ แต่ JavaScript test script และ secret ควรตรวจสอบหรือป้อนใหม่ด้วยตนเอง&lt;/p&gt;

&lt;h3&gt;
  
  
  ทางเลือก Postman ใดดีที่สุดสำหรับทีมที่ต้องการทำงานแบบออฟไลน์?
&lt;/h3&gt;

&lt;p&gt;Bruno เหมาะมากสำหรับ workflow แบบ offline และ file-based เพราะ collection ถูกเก็บเป็นไฟล์ข้อความธรรมดาบน disk และจัดการผ่าน Git ได้&lt;/p&gt;

&lt;p&gt;Apidog ก็เป็น desktop app เต็มรูปแบบและไม่จำเป็นต้องพึ่งการเชื่อมต่อ cloud ตลอดเวลา จึงเหมาะกับทีมที่ต้องการหลีกเลี่ยง workflow แบบ cloud-first บางส่วนของ Postman&lt;/p&gt;

&lt;h3&gt;
  
  
  ทางเลือก Postman ใดดีที่สุดสำหรับ Command-line และ CI?
&lt;/h3&gt;

&lt;p&gt;สำหรับ command-line แบบ manual และ script ง่าย ๆ HTTPie ใช้งานง่ายและอ่านคำสั่งได้ชัดเจน&lt;/p&gt;

&lt;p&gt;สำหรับ automated test suite ใน CI ให้พิจารณา Apidog, Hoppscotch หรือ Bruno เพราะมี runner หรือ CLI ของตัวเอง ขึ้นอยู่กับว่าคุณต้องการ scenario เต็มรูปแบบหรือแค่ scripted check อย่างรวดเร็ว&lt;/p&gt;

&lt;h3&gt;
  
  
  Postman แย่จริงหรือ?
&lt;/h3&gt;

&lt;p&gt;ไม่ Postman ยังเป็นเครื่องมือที่ดี มี ecosystem ใหญ่ และหลายทีมใช้งานได้อย่างมีประสิทธิภาพ&lt;/p&gt;

&lt;p&gt;เหตุผลที่นักพัฒนามองหาทางเลือกมักมาจากความต้องการเฉพาะ เช่น UI ที่เบากว่า, offline workflow, file-based collection, Git review หรือค่าใช้จ่ายต่อผู้ใช้ เลือกเครื่องมือจาก workflow ของทีม ไม่ใช่จากสมมติฐานว่า Postman เป็นตัวเลือกที่ผิดเสมอไป&lt;/p&gt;

</description>
    </item>
    <item>
      <title>การทดสอบประสิทธิภาพ: ประเภท, ตัวชี้วัด และวิธีการทำงาน</title>
      <dc:creator>Thanawat Wongchai</dc:creator>
      <pubDate>Fri, 22 May 2026 07:29:16 +0000</pubDate>
      <link>https://forem.com/thanawat_wonchai/kaarthdsbprasiththiphaaph-praephth-tawchiiwad-aelawithiikaarthamngaan-37gb</link>
      <guid>https://forem.com/thanawat_wonchai/kaarthdsbprasiththiphaaph-praephth-tawchiiwad-aelawithiikaarthamngaan-37gb</guid>
      <description>&lt;p&gt;ซอฟต์แวร์ที่ “ทำงานถูกต้อง” ไม่ได้แปลว่า “ทำงานได้ดีเมื่อมีผู้ใช้จริงจำนวนมาก” ฟีเจอร์อาจผ่าน functional test ทั้งหมด แต่ล้มเหลวทันทีเมื่อเจอโหลดจริง การทดสอบประสิทธิภาพจึงเป็นวิธีวัดว่าระบบตอบสนองอย่างไรภายใต้ภาระงาน ไม่ใช่แค่ว่าฟีเจอร์ทำงานถูกต้องหรือไม่&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com/?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation" class="crayons-btn crayons-btn--primary"&gt;ลองใช้ Apidog วันนี้&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;คู่มือนี้สรุปแนวทางการทำ performance testing แบบนำไปใช้ได้จริง: ควรทดสอบอะไร วัดเมตริกไหน วางไว้ตรงไหนใน CI/CD และควรหลีกเลี่ยงข้อผิดพลาดใดบ้าง&lt;/p&gt;

&lt;h2&gt;
  
  
  การทดสอบประสิทธิภาพคืออะไร
&lt;/h2&gt;

&lt;p&gt;การทดสอบประสิทธิภาพคือการประเมิน &lt;strong&gt;ความเร็ว ความเสถียร และความสามารถในการรองรับโหลด&lt;/strong&gt; ของระบบภายใต้เงื่อนไขที่กำหนดไว้&lt;/p&gt;

&lt;p&gt;แทนที่จะถามว่า:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ฟีเจอร์นี้ใช้งานได้หรือไม่?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ให้ถามว่า:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ฟีเจอร์นี้ตอบสนองเร็วแค่ไหน รองรับผู้ใช้ได้เท่าไร และล้มเหลวอย่างไรเมื่อถึงขีดจำกัด?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ตัวอย่างเช่น endpoint สำหรับ login อาจคืน token ถูกต้องทุกครั้ง แต่ใช้เวลา 4 วินาทีเมื่อมีผู้ใช้พร้อมกันจำนวนมาก Functional test จะผ่าน แต่ผู้ใช้จริงอาจออกจากระบบไปแล้ว Performance test คือสิ่งที่เปิดเผยปัญหาแบบนี้&lt;/p&gt;

&lt;p&gt;ผลลัพธ์ที่ดีไม่ควรเป็นแค่ “ผ่าน/ไม่ผ่าน” แต่ควรตอบได้ว่า:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ที่ concurrency ระดับหนึ่ง ระบบตอบสนองกี่มิลลิวินาที&lt;/li&gt;
&lt;li&gt;throughput สูงสุดอยู่ที่เท่าไร&lt;/li&gt;
&lt;li&gt;error rate เริ่มเพิ่มเมื่อใด&lt;/li&gt;
&lt;li&gt;คอขวดอยู่ที่ CPU, memory, database, network หรือ dependency ภายนอก&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ข้อมูลเหล่านี้ใช้สำหรับ capacity planning, ตั้ง SLO/SLA และจับ regression ก่อน release&lt;/p&gt;

&lt;h2&gt;
  
  
  ประเภทหลักของการทดสอบประสิทธิภาพ
&lt;/h2&gt;

&lt;p&gt;การทดสอบประสิทธิภาพไม่ใช่การทดสอบแบบเดียว แต่เป็นชุดของวิธีทดสอบที่ตอบคำถามต่างกัน&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Baseline testing
&lt;/h3&gt;

&lt;p&gt;Baseline testing คือการรันระบบภายใต้โหลดปกติที่คาดว่าจะเจอ แล้วบันทึกผลลัพธ์ไว้เป็นจุดอ้างอิง&lt;/p&gt;

&lt;p&gt;ใช้ตอบคำถาม:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ภายใต้โหลดปกติ ระบบควรเร็วแค่ไหน?&lt;/li&gt;
&lt;li&gt;release ใหม่ช้ากว่าเดิมหรือไม่?&lt;/li&gt;
&lt;li&gt;ตัวเลขที่เห็น “ดี” หรือ “แย่” เมื่อเทียบกับอดีต?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่าง baseline ที่ควรเก็บ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Endpoint: POST /login
Concurrency: 50 users
Duration: 10 minutes
p95 response time: 280 ms
p99 response time: 650 ms
Error rate: 0.02%
Throughput: 420 req/s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้าไม่มี baseline คุณจะไม่รู้ว่าตัวเลขใหม่คือปัญหาหรือเป็นพฤติกรรมปกติของระบบ&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Load testing
&lt;/h3&gt;

&lt;p&gt;Load testing คือการทดสอบด้วยปริมาณการใช้งานสูงสุดที่คาดว่าจะเกิดขึ้นจริง เช่น peak traffic ในวันทำงานหรือช่วง campaign&lt;/p&gt;

&lt;p&gt;ใช้ตรวจสอบว่า:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;response time ยังอยู่ใน budget หรือไม่&lt;/li&gt;
&lt;li&gt;error rate ยังใกล้ศูนย์หรือไม่&lt;/li&gt;
&lt;li&gt;throughput เพียงพอต่อ peak traffic หรือไม่&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่างเป้าหมาย:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Expected peak: 1,000 concurrent users
Target p95 response time: &amp;lt; 500 ms
Target error rate: &amp;lt; 0.1%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้าระบบผ่าน load test แปลว่าระบบน่าจะรองรับวันทำงานที่ยุ่งที่สุดได้&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Stress testing
&lt;/h3&gt;

&lt;p&gt;Stress testing คือการเพิ่มโหลดเกินกว่าที่คาดไว้จนระบบเริ่มเสื่อมถอยหรือล้มเหลว&lt;/p&gt;

&lt;p&gt;เป้าหมายไม่ใช่แค่ทำให้ระบบพัง แต่เพื่อดูว่า:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;จุดแตกหักอยู่ที่โหลดระดับใด&lt;/li&gt;
&lt;li&gt;ระบบ fail อย่างไร&lt;/li&gt;
&lt;li&gt;มี graceful degradation หรือไม่&lt;/li&gt;
&lt;li&gt;มี data loss หรือ cascading failure หรือไม่&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;พฤติกรรมที่ยอมรับได้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;โหลดสูงมาก → response time เพิ่มขึ้น → queue เต็ม → reject request บางส่วนด้วย 429
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;พฤติกรรมที่ไม่ควรเกิด:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;โหลดสูงมาก → database lock → service ทั้งหมด timeout → data corruption
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Spike testing
&lt;/h3&gt;

&lt;p&gt;Spike testing คือการเพิ่มโหลดขึ้นอย่างรวดเร็ว แล้วลดลงอีกครั้ง เพื่อจำลองเหตุการณ์ที่ traffic พุ่งทันที เช่น flash sale, ข่าว viral หรือ webhook burst&lt;/p&gt;

&lt;p&gt;ใช้ตอบคำถาม:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;autoscaling ตอบสนองเร็วพอหรือไม่&lt;/li&gt;
&lt;li&gt;connection pool รับ burst ได้หรือไม่&lt;/li&gt;
&lt;li&gt;queue/backpressure ทำงานถูกต้องหรือไม่&lt;/li&gt;
&lt;li&gt;cache warm-up ช้าเกินไปหรือไม่&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ระบบที่รองรับโหลดคงที่ได้ดี อาจล้มเหลวเมื่อเจอ spike เพราะ scale ไม่ทัน&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Capacity testing
&lt;/h3&gt;

&lt;p&gt;Capacity testing คือการหาจำนวนโหลดสูงสุดที่ระบบรองรับได้โดยยังอยู่ใน performance budget&lt;/p&gt;

&lt;p&gt;ผลลัพธ์ควรเป็นตัวเลขที่นำไปใช้ได้ เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ระบบรองรับได้สูงสุด 2,400 concurrent users
โดย p95 response time &amp;lt; 700 ms
และ error rate &amp;lt; 0.1%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวเลขนี้ใช้โดยตรงกับ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;capacity planning&lt;/li&gt;
&lt;li&gt;autoscaling threshold&lt;/li&gt;
&lt;li&gt;infrastructure sizing&lt;/li&gt;
&lt;li&gt;release readiness decision&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Soak testing
&lt;/h3&gt;

&lt;p&gt;Soak testing หรือ stability/endurance testing คือการรันโหลดระดับปานกลางเป็นเวลานาน เช่น หลายชั่วโมงหรือหลายวัน&lt;/p&gt;

&lt;p&gt;ใช้ตรวจหาปัญหาที่ไม่โผล่ในการทดสอบสั้น ๆ เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory leak&lt;/li&gt;
&lt;li&gt;connection leak&lt;/li&gt;
&lt;li&gt;file descriptor หมด&lt;/li&gt;
&lt;li&gt;queue ค่อย ๆ สะสม&lt;/li&gt;
&lt;li&gt;response time ค่อย ๆ แย่ลง&lt;/li&gt;
&lt;li&gt;cache หรือ session store โตไม่หยุด&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่าง soak test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Concurrency: 300 users
Duration: 12 hours
Target p95 response time: &amp;lt; 500 ms
Target error rate: &amp;lt; 0.1%
Monitor: memory, CPU, DB connections, GC, queue length
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;โดยทั่วไป ทีมควรเริ่มจาก &lt;strong&gt;baseline, load และ soak testing&lt;/strong&gt; แล้วเพิ่ม &lt;strong&gt;stress และ spike testing&lt;/strong&gt; สำหรับระบบที่มี traffic สูงหรือคาดเดายาก&lt;/p&gt;

&lt;h2&gt;
  
  
  เมตริกที่ใช้นิยามผลลัพธ์
&lt;/h2&gt;

&lt;p&gt;Performance test จะมีประโยชน์ก็ต่อเมื่อคุณวัดเมตริกที่ถูกต้อง&lt;/p&gt;

&lt;h3&gt;
  
  
  Response time
&lt;/h3&gt;

&lt;p&gt;Response time คือเวลาตั้งแต่ส่ง request จนได้รับ response&lt;/p&gt;

&lt;p&gt;อย่าดูแค่ค่าเฉลี่ย ให้ดู percentile เสมอ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;avg: 180 ms
p50: 120 ms
p95: 420 ms
p99: 2,800 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ค่าเฉลี่ยอาจดูดี แต่ p99 อาจแย่มาก ผู้ใช้จริงมักรู้สึกถึง tail latency โดยเฉพาะในระบบที่มีหลาย request ต่อหนึ่งหน้า&lt;/p&gt;

&lt;h3&gt;
  
  
  Throughput
&lt;/h3&gt;

&lt;p&gt;Throughput คือจำนวนงานที่ระบบทำเสร็จต่อหน่วยเวลา มักวัดเป็น requests per second&lt;/p&gt;

&lt;p&gt;สูตรพื้นฐาน:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;throughput = total completed requests / test duration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่าง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;120,000 requests / 300 seconds = 400 req/s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Throughput ช่วยบอกขีดความสามารถของระบบ แต่ต้องอ่านคู่กับ response time และ error rate เสมอ เพราะ throughput สูงแต่ error เยอะไม่ถือว่าดี&lt;/p&gt;

&lt;h3&gt;
  
  
  Concurrency
&lt;/h3&gt;

&lt;p&gt;Concurrency คือจำนวนผู้ใช้หรือ connection ที่ทำงานพร้อมกัน&lt;/p&gt;

&lt;p&gt;ตัวอย่างคำถามที่ควรตอบได้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ระบบเริ่มเกิน p95 500 ms ที่ concurrency เท่าไร?
ระบบเริ่ม error rate &amp;gt; 1% ที่ concurrency เท่าไร?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Concurrency ไม่เท่ากับ requests per second เสมอไป เพราะขึ้นกับพฤติกรรมผู้ใช้ ระยะเวลาคิดระหว่าง action และ latency ของแต่ละ request&lt;/p&gt;

&lt;h3&gt;
  
  
  Error rate
&lt;/h3&gt;

&lt;p&gt;Error rate คือเปอร์เซ็นต์ของ request ที่ล้มเหลวภายใต้โหลด&lt;/p&gt;

&lt;p&gt;ตัวอย่าง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;total requests: 100,000
failed requests: 75
error rate: 0.075%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ระบบที่ยังตอบเร็วแต่เริ่มคืน 5xx จำนวนมากไม่ถือว่าผ่าน เพราะ performance ต้องรวม reliability ด้วย&lt;/p&gt;

&lt;h3&gt;
  
  
  CPU และ memory utilization
&lt;/h3&gt;

&lt;p&gt;CPU และ memory ช่วยอธิบายว่าทำไมเมตริกอื่นเปลี่ยน&lt;/p&gt;

&lt;p&gt;ตัวอย่างการอ่านผล:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;p95 เพิ่มขึ้น + CPU 100%
→ อาจติด CPU-bound workload

p95 เพิ่มขึ้น + CPU ต่ำ
→ อาจติด database, lock, network หรือ external dependency

memory เพิ่มขึ้นเรื่อย ๆ ระหว่าง soak test
→ อาจมี memory leak หรือ cache eviction ไม่ทำงาน
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ผลลัพธ์ที่ดีควรเขียนได้เป็นประโยคเดียว เช่น:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ที่ 800 concurrent users ระบบทำ throughput ได้ 1,200 req/s, p95 อยู่ที่ 480 ms, error rate 0.05% และคอขวดหลักอยู่ที่ database connection pool&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  การทดสอบประสิทธิภาพเข้ากับกระบวนการอย่างไร
&lt;/h2&gt;

&lt;p&gt;ในอดีต performance testing มักถูกทำช่วงท้ายโครงการก่อน release ครั้งเดียว วิธีนี้ไม่เหมาะกับระบบที่ deploy ต่อเนื่อง เพราะ performance regression เกิดได้จากการเปลี่ยนแปลงเล็ก ๆ เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;query ใหม่ที่ไม่มี index&lt;/li&gt;
&lt;li&gt;integration ใหม่ที่ latency สูง&lt;/li&gt;
&lt;li&gt;payload ใหญ่ขึ้น&lt;/li&gt;
&lt;li&gt;cache key เปลี่ยน&lt;/li&gt;
&lt;li&gt;connection pool ไม่พอ&lt;/li&gt;
&lt;li&gt;N+1 query&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แนวทางที่ดีกว่าคือปฏิบัติกับ performance เหมือน correctness: &lt;strong&gt;ตรวจอย่างต่อเนื่องและมี budget ชัดเจน&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ขั้นตอนที่แนะนำ
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;เลือก critical path เช่น login, search, checkout, payment, create order&lt;/li&gt;
&lt;li&gt;กำหนด performance budget&lt;/li&gt;
&lt;li&gt;เขียน scenario test ที่จำลอง workflow จริง&lt;/li&gt;
&lt;li&gt;รัน lightweight load test ใน &lt;a href="http://apidog.com/blog/automate-api-tests-ci-cd?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;CI/CD&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ให้ build fail เมื่อ response time หรือ error rate เกิน budget&lt;/li&gt;
&lt;li&gt;รัน stress/soak test แบบ scheduled ก่อน release หรือเป็น nightly job&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ตัวอย่าง budget:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;performance_budget&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;p95_ms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
    &lt;span class="na"&gt;p99_ms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;
    &lt;span class="na"&gt;error_rate_percent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;
  &lt;span class="na"&gt;search&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;p95_ms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;
    &lt;span class="na"&gt;p99_ms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1500&lt;/span&gt;
    &lt;span class="na"&gt;error_rate_percent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;
  &lt;span class="na"&gt;checkout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;p95_ms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;800&lt;/span&gt;
    &lt;span class="na"&gt;p99_ms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2000&lt;/span&gt;
    &lt;span class="na"&gt;error_rate_percent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.05&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สำหรับระบบส่วนใหญ่ จุดที่คุ้มค่าที่สุดคือการทดสอบที่เลเยอร์ API เพราะ API มี business logic หลัก เรียกซ้ำได้ง่าย และไม่มี UI flakiness มารบกวน การทำ &lt;a href="http://apidog.com/blog/api-performance-testing-tutorial?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การทดสอบประสิทธิภาพ API&lt;/a&gt; ช่วยให้ได้ตัวเลขที่สม่ำเสมอและใช้ต้นทุนต่ำกว่า end-to-end UI test&lt;/p&gt;

&lt;p&gt;การเก็บ performance test ไว้ใกล้กับ &lt;a href="http://apidog.com/blog/api-assertions?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;API functional test&lt;/a&gt; ยังช่วยให้ทุก change ถูกตรวจทั้งความถูกต้องและความเร็วพร้อมกัน&lt;/p&gt;

&lt;h2&gt;
  
  
  ข้อผิดพลาดทั่วไปในการทดสอบประสิทธิภาพ
&lt;/h2&gt;

&lt;p&gt;Performance testing ทำผิดได้ง่าย และอาจให้ผลลัพธ์ที่ดูน่าเชื่อถือแต่ใช้ตัดสินใจไม่ได้ ข้อผิดพลาดที่พบบ่อยมีดังนี้&lt;/p&gt;

&lt;h3&gt;
  
  
  1. ทดสอบบน infrastructure ที่ไม่สมจริง
&lt;/h3&gt;

&lt;p&gt;การรัน load test บน laptop หรือ staging environment ที่เล็กกว่า production มาก จะให้ตัวเลขที่ใช้ไม่ได้&lt;/p&gt;

&lt;p&gt;ควรทำ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ใช้ environment ที่ใกล้ production ที่สุด&lt;/li&gt;
&lt;li&gt;ใช้ network path ที่ใกล้เคียงจริง&lt;/li&gt;
&lt;li&gt;ใช้ database size ใกล้เคียงจริง&lt;/li&gt;
&lt;li&gt;เปิด monitoring เหมือน production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ถ้าจำเป็นต้องใช้ staging ที่เล็กกว่า production ให้ระบุข้อจำกัดไว้ชัดเจน และใช้ผลลัพธ์เพื่อหา regression ไม่ใช่ capacity สุดท้าย&lt;/p&gt;

&lt;h3&gt;
  
  
  2. ไม่แยกช่วง warm-up
&lt;/h3&gt;

&lt;p&gt;ระบบจำนวนมากช้าในช่วงแรก เพราะ cache ยังไม่เต็ม connection pool ยังไม่พร้อม หรือ JIT/initialization ยังทำงานอยู่&lt;/p&gt;

&lt;p&gt;ควรแยกผลเป็น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Warm-up: 2 minutes
Measurement: 10 minutes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หรือรายงาน cold start และ steady state แยกกัน&lt;/p&gt;

&lt;h3&gt;
  
  
  3. อ่านค่าเฉลี่ยแทน percentile
&lt;/h3&gt;

&lt;p&gt;ค่าเฉลี่ยซ่อน tail latency ได้ง่ายมาก เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;avg: 200 ms
p99: 3,000 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ผู้ใช้ที่เจอ p99 จะรู้สึกว่าระบบช้า แม้ค่าเฉลี่ยจะดูดี&lt;/p&gt;

&lt;p&gt;ควรรายงานอย่างน้อย:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;p50&lt;/li&gt;
&lt;li&gt;p90&lt;/li&gt;
&lt;li&gt;p95&lt;/li&gt;
&lt;li&gt;p99&lt;/li&gt;
&lt;li&gt;max&lt;/li&gt;
&lt;li&gt;error rate&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. ใช้ข้อมูลทดสอบไม่สมจริง
&lt;/h3&gt;

&lt;p&gt;ถ้าทุก request ใช้ user id เดียวหรือ product id เดียว database อาจตอบจาก cache เกือบทั้งหมด ทำให้ผลดูดีกว่าความจริง&lt;/p&gt;

&lt;p&gt;ควรใช้ test data ที่กระจายเหมือน production เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"userIds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"u1001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"u1002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"u1003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"productIds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"p2001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"p2002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"p2003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"searchTerms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"laptop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"monitor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. ทดสอบ endpoint เดียวแยกจาก workflow จริง
&lt;/h3&gt;

&lt;p&gt;ผู้ใช้จริงไม่ได้เรียก endpoint เดียวซ้ำ ๆ แต่ทำ workflow เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;login → browse → search → add to cart → checkout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้าทดสอบแค่ endpoint เดียว คุณอาจพลาด resource contention ระหว่างหลาย service, database table หรือ connection pool&lt;/p&gt;

&lt;p&gt;ควรสร้าง scenario แบบ multi-step ที่ใกล้เคียง user journey จริง&lt;/p&gt;

&lt;h3&gt;
  
  
  6. ทำครั้งเดียวก่อน release
&lt;/h3&gt;

&lt;p&gt;Performance test ที่รันครั้งเดียวก่อน release จะล้าสมัยทันทีเมื่อมี change ถัดไป&lt;/p&gt;

&lt;p&gt;ควรทำให้เป็นกระบวนการต่อเนื่อง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Pull request → lightweight load test
Nightly → broader load test
Pre-release → stress/spike/soak test
Production → monitoring + alerting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;การหลีกเลี่ยงข้อผิดพลาดเหล่านี้ช่วยให้ performance test กลายเป็นข้อมูลที่ใช้ตัดสินใจได้จริง ไม่ใช่แค่เครื่องหมายถูกสีเขียวที่ทำให้รู้สึกปลอดภัย&lt;/p&gt;

&lt;h2&gt;
  
  
  การรันการทดสอบประสิทธิภาพด้วย Apidog
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; รวมการออกแบบ API, functional testing และ load testing ไว้ใน workspace เดียวกัน จึงไม่ต้องดูแล API definition หลายชุดหรือย้าย scenario ไปมาระหว่างหลายเครื่องมือ&lt;/p&gt;

&lt;p&gt;แนวทางใช้งานทั่วไปคือ:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;สร้าง endpoint หรือ &lt;a href="http://apidog.com/blog/test-scenario-vs-test-case?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;สถานการณ์ทดสอบ&lt;/a&gt; แบบหลายขั้นตอน&lt;/li&gt;
&lt;li&gt;ตรวจให้ผ่านใน functional test ก่อน&lt;/li&gt;
&lt;li&gt;กำหนดจำนวน virtual users และระยะเวลาทดสอบ&lt;/li&gt;
&lt;li&gt;รัน load test&lt;/li&gt;
&lt;li&gt;ดู response time percentile, throughput และ error rate แบบ real time&lt;/li&gt;
&lt;li&gt;ระบุ concurrency level ที่ performance เริ่มเปลี่ยน&lt;/li&gt;
&lt;li&gt;ถ้าต้องการโหลดระดับใหญ่กว่าเครื่องเดียว สามารถส่งออก scenario ไปยัง JMeter โดยคง definition เดิมไว้&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ตัวอย่าง workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create scenario: login → search → add to cart → checkout
Validate functional assertions
Run with 100 virtual users for 10 minutes
Check p95, p99, throughput, error rate
Compare with baseline
Fail the release if budget is exceeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ข้อดีคือ scenario เดียวกันใช้ได้ทั้ง functional run และ performance run ทำให้ดูแล artifact เดียว แทนที่จะต้องดูแล test script แยกหลายชุด&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com/download?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ดาวน์โหลด Apidog&lt;/a&gt; เพื่อเริ่ม profiling endpoint ที่คุณมีอยู่แล้ว&lt;/p&gt;

&lt;h2&gt;
  
  
  คำถามที่พบบ่อย
&lt;/h2&gt;

&lt;h3&gt;
  
  
  การทดสอบประสิทธิภาพกับการทดสอบฟังก์ชันแตกต่างกันอย่างไร?
&lt;/h3&gt;

&lt;p&gt;Functional testing ตรวจว่าซอฟต์แวร์ให้ผลลัพธ์ถูกต้องหรือไม่ ส่วน performance testing ตรวจว่าซอฟต์แวร์ทำงานได้เร็วและเสถียรเพียงใดภายใต้โหลดจริง ทั้งสองอย่างจำเป็นและทดแทนกันไม่ได้&lt;/p&gt;

&lt;h3&gt;
  
  
  ควรเริ่มรันการทดสอบประสิทธิภาพประเภทใดก่อน?
&lt;/h3&gt;

&lt;p&gt;เริ่มจาก baseline testing แล้วตามด้วย load testing&lt;/p&gt;

&lt;p&gt;Baseline ให้ค่ามาตรฐานสำหรับเปรียบเทียบ ส่วน load testing ยืนยันว่าระบบรองรับ peak traffic ที่คาดไว้ได้ จากนั้นค่อยเพิ่ม stress, spike และ soak testing ตามความเสี่ยงของระบบ&lt;/p&gt;

&lt;h3&gt;
  
  
  ทำไมต้องใช้ percentile แทน response time เฉลี่ย?
&lt;/h3&gt;

&lt;p&gt;ค่าเฉลี่ยซ่อนคำขอที่ช้าที่สุดได้ง่าย ในขณะที่ p95 และ p99 แสดงประสบการณ์ของผู้ใช้ที่เจอ latency แย่ที่สุด ซึ่งมักเป็นกลุ่มที่ร้องเรียนหรือออกจากระบบก่อน&lt;/p&gt;

&lt;h3&gt;
  
  
  การทดสอบประสิทธิภาพทำอัตโนมัติได้หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ได้ Lightweight load test สามารถรันใน CI ทุกครั้งที่มี pull request โดยกำหนด budget ให้ build fail เมื่อเกิด regression ส่วน stress และ soak test ที่ใช้เวลานานมักรันแบบ scheduled หรือก่อน release&lt;/p&gt;

&lt;h3&gt;
  
  
  ควรเริ่ม performance testing เมื่อใดในวงจรการพัฒนา?
&lt;/h3&gt;

&lt;p&gt;ควรเริ่มเร็วที่สุดเท่าที่ทำได้ แม้ยังไม่มี infrastructure สุดท้าย คุณสามารถกำหนด performance budget และเขียน scenario ได้ตั้งแต่ช่วงออกแบบ เมื่อ endpoint ใช้งานได้แล้วจึงเริ่มรัน baseline/load test เพื่อจับปัญหาตั้งแต่ยังแก้ได้ง่าย&lt;/p&gt;

&lt;h3&gt;
  
  
  ใครควรรับผิดชอบ performance testing?
&lt;/h3&gt;

&lt;p&gt;ในทีมสมัยใหม่ควรเป็นความรับผิดชอบร่วมกัน:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developer รัน lightweight load test กับ change ของตัวเอง&lt;/li&gt;
&lt;li&gt;QA ดูแล scenario และ performance budget&lt;/li&gt;
&lt;li&gt;Ops/SRE ดูแล environment, monitoring และ server-side metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;การมองว่า performance เป็นงานของผู้เชี่ยวชาญเพียงคนเดียวมักทำให้ปัญหาไปโผล่ใน production&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance test ควรรันนานแค่ไหน?
&lt;/h3&gt;

&lt;p&gt;ควรรันนานพอให้ผ่านช่วง warm-up และเข้าสู่ steady state&lt;/p&gt;

&lt;p&gt;โดยทั่วไป:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Load test: หลายนาทีถึงหลักสิบ分钟
Stress test: จนกว่าจะเห็นจุดเสื่อมถอยหรือจุดแตกหัก
Spike test: สั้นแต่เพิ่มโหลดเร็ว
Soak test: หลายชั่วโมงหรือหลายวัน
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Soak test ต้องรันนานเป็นพิเศษ เพราะเป้าหมายคือหา degradation ที่เกิดช้า เช่น memory leak หรือ resource exhaustion ที่การรันสั้น ๆ มองไม่เห็น&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Newman กับ Postman ต่างกันอย่างไร</title>
      <dc:creator>Thanawat Wongchai</dc:creator>
      <pubDate>Fri, 22 May 2026 07:28:48 +0000</pubDate>
      <link>https://forem.com/thanawat_wonchai/newman-kab-postman-taangkanyaangair-16ll</link>
      <guid>https://forem.com/thanawat_wonchai/newman-kab-postman-taangkanyaangair-16ll</guid>
      <description>&lt;p&gt;Newman และ Postman ไม่ใช่คู่แข่งกัน แต่เป็นสองส่วนของเวิร์กโฟลว์เดียวกัน: Postman ใช้สร้างและดีบั๊กคำขอ API แบบมี GUI ส่วน Newman ใช้รันคอลเลกชันเดียวกันจากบรรทัดคำสั่งโดยไม่มี GUI เหมาะสำหรับ CI/CD, scheduled jobs และ automation ที่ต้องการผลลัพธ์ผ่าน exit code&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com/?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation" class="crayons-btn crayons-btn--primary"&gt;ลองใช้ Apidog วันนี้&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;คำตอบสั้นๆ คือใช้ทั้งสองอย่างในคนละขั้นตอน: สร้างและตรวจสอบคอลเลกชันใน Postman จากนั้น export เป็น JSON แล้วให้ Newman รันใน pipeline บทความนี้สรุปวิธีใช้งานจริง ตั้งแต่การติดตั้ง การรันคอลเลกชัน การต่อกับ CI/CD ไปจนถึงข้อผิดพลาดที่ควรระวัง&lt;/p&gt;

&lt;h2&gt;
  
  
  Postman คืออะไร
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; เป็นแพลตฟอร์ม API แบบกราฟิกสำหรับสร้างคำขอ จัดกลุ่มเป็น collection/folder และผูก environment ที่เก็บตัวแปร เช่น base URL, token หรือค่า config อื่นๆ หลังจากได้รับ response แล้ว Postman สามารถรัน JavaScript test script ในแท็บ Tests ได้ทันที&lt;/p&gt;

&lt;p&gt;ตัวอย่าง test script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Status code is 200&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Order total is a positive number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;above&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Postman เหมาะกับงานแบบ interactive เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ดีบั๊ก endpoint ใหม่&lt;/li&gt;
&lt;li&gt;ทดลองเปลี่ยน header/body/query parameter&lt;/li&gt;
&lt;li&gt;ตรวจ response แบบ manual&lt;/li&gt;
&lt;li&gt;สร้าง regression test suite&lt;/li&gt;
&lt;li&gt;แชร์ collection ให้ทีมใช้ร่วมกัน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ถ้าต้องการเวิร์กโฟลว์การทดสอบ API ด้วย Postman แบบละเอียด ดูคู่มือ &lt;a href="http://apidog.com/blog/how-to-test-apis-with-postman?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ทดสอบ API ด้วย Postman&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ข้อจำกัดคือ Postman ไม่ได้ออกแบบมาเพื่อรันแบบ unattended ใน build server เพราะต้องเปิดแอปและใช้ Collection Runner ผ่าน GUI ซึ่งไม่เหมาะกับ pipeline&lt;/p&gt;

&lt;h2&gt;
  
  
  Newman คืออะไร
&lt;/h2&gt;

&lt;p&gt;Newman คือ official command-line collection runner ของ Postman เป็นแพ็กเกจ npm แบบโอเพ่นซอร์ส ใช้รันไฟล์ collection JSON ที่ export จาก Postman ได้โดยตรง&lt;/p&gt;

&lt;p&gt;ติดตั้ง Newman:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; newman
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;รัน collection พร้อม environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newman run orders-api.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--environment&lt;/span&gt; staging.postman_environment.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Newman จะรัน request ทั้งหมดใน collection, execute &lt;code&gt;pm.test&lt;/code&gt; เหมือน Postman และแสดงผลใน terminal จุดสำคัญคือ Newman ใช้ runtime เดียวกับ Postman ดังนั้น test ที่ผ่านใน Postman Collection Runner ควรให้ผลลัพธ์เดียวกันเมื่อรันด้วย Newman&lt;/p&gt;

&lt;p&gt;เมื่อ test ล้มเหลว Newman จะ exit ด้วย status code ที่ไม่ใช่ &lt;code&gt;0&lt;/code&gt; ทำให้ CI/CD ตรวจจับได้ทันทีและ mark build เป็น failed&lt;/p&gt;

&lt;p&gt;ตัวอย่างตรวจ exit code ใน shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newman run orders-api.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--environment&lt;/span&gt; staging.postman_environment.json

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้า exit code เป็น &lt;code&gt;0&lt;/code&gt; คือผ่าน ถ้าไม่ใช่ &lt;code&gt;0&lt;/code&gt; คือมี error หรือ assertion failed&lt;/p&gt;

&lt;h2&gt;
  
  
  เปรียบเทียบ Postman กับ Newman
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;แง่มุม&lt;/th&gt;
&lt;th&gt;Postman&lt;/th&gt;
&lt;th&gt;Newman&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;อินเทอร์เฟซ&lt;/td&gt;
&lt;td&gt;แอปเดสก์ท็อปแบบกราฟิก&lt;/td&gt;
&lt;td&gt;บรรทัดคำสั่ง ไม่มี UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;การใช้งานหลัก&lt;/td&gt;
&lt;td&gt;สร้าง ดีบั๊ก สำรวจ API&lt;/td&gt;
&lt;td&gt;รันอัตโนมัติใน script หรือ CI/CD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;รันที่ไหน&lt;/td&gt;
&lt;td&gt;เครื่องของนักพัฒนา&lt;/td&gt;
&lt;td&gt;CI server, terminal, scheduler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ค่าใช้จ่าย&lt;/td&gt;
&lt;td&gt;มีทั้งแผนฟรีและเสียเงิน&lt;/td&gt;
&lt;td&gt;โอเพ่นซอร์ส ฟรี&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;การติดตั้ง&lt;/td&gt;
&lt;td&gt;Desktop installer&lt;/td&gt;
&lt;td&gt;npm package หรือ Docker image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;สคริปต์ทดสอบ&lt;/td&gt;
&lt;td&gt;เขียนและรันในแอป&lt;/td&gt;
&lt;td&gt;รันสคริปต์เดียวกันแบบ headless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;การรายงานผล&lt;/td&gt;
&lt;td&gt;แสดงผลใน GUI&lt;/td&gt;
&lt;td&gt;CLI output และ reporter เช่น JUnit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;เหมาะที่สุดสำหรับ&lt;/td&gt;
&lt;td&gt;การทำงานแบบ interactive&lt;/td&gt;
&lt;td&gt;การรันซ้ำได้แบบอัตโนมัติ&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;สรุปคือ collection JSON คือสะพานระหว่างสองเครื่องมือ: ออกแบบใน Postman แล้วรันอัตโนมัติด้วย Newman&lt;/p&gt;

&lt;h2&gt;
  
  
  วิธีใช้ Newman ใน CI/CD
&lt;/h2&gt;

&lt;p&gt;รูปแบบทั่วไปมี 5 ขั้นตอน:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Export จาก Postman&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Export collection และ environment เป็นไฟล์ JSON&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Commit เข้า repository&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
เก็บไฟล์ไว้ใน repo เดียวกับโค้ด เช่น:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   api-tests/
     orders-api.postman_collection.json
     staging.postman_environment.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;ติดตั้ง Newman ใน CI job&lt;/strong&gt;
ใช้ npm:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; newman
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หรือใช้ Docker image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;:/etc/newman"&lt;/span&gt; postman/newman run /etc/newman/api-tests/orders-api.postman_collection.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;รัน collection&lt;/strong&gt;
ระบุ collection และ environment ให้ชัดเจน:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   newman run api-tests/orders-api.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;--environment&lt;/span&gt; api-tests/staging.postman_environment.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;ให้ exit code ตัดสินผล build&lt;/strong&gt;
ถ้ามี test fail Newman จะ exit non-zero และ CI จะ fail job&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ตัวอย่าง GitHub Actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;API Tests&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;newman&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Newman&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install -g newman&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run API tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;newman run api-tests/orders-api.postman_collection.json \&lt;/span&gt;
            &lt;span class="s"&gt;--environment api-tests/staging.postman_environment.json \&lt;/span&gt;
            &lt;span class="s"&gt;--reporters cli,junit \&lt;/span&gt;
            &lt;span class="s"&gt;--reporter-junit-export results.xml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แฟล็ก &lt;code&gt;--reporters cli,junit&lt;/code&gt; ช่วยให้ได้ทั้ง output ใน terminal และไฟล์ JUnit XML สำหรับแสดงผลใน CI dashboard&lt;/p&gt;

&lt;p&gt;อ่านต่อได้ที่:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://apidog.com/blog/automate-api-tests-ci-cd?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ทำให้การทดสอบ API เป็นอัตโนมัติใน CI/CD&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://apidog.com/blog/api-test-automation-github-actions?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ทำ API test automation ด้วย GitHub Actions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ตัวเลือกบรรทัดคำสั่ง Newman ที่ควรรู้
&lt;/h2&gt;

&lt;p&gt;Newman มี flag หลายตัวที่ช่วยให้ pipeline เสถียรขึ้น&lt;/p&gt;

&lt;h3&gt;
  
  
  รันด้วยข้อมูลหลายชุด
&lt;/h3&gt;

&lt;p&gt;ใช้ &lt;code&gt;--iteration-data&lt;/code&gt; เพื่อรัน collection ซ้ำตามข้อมูลใน CSV หรือ JSON&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newman run orders-api.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--environment&lt;/span&gt; staging.postman_environment.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--iteration-data&lt;/span&gt; test-data/orders.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่าง &lt;code&gt;orders.csv&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;productId,quantity
p-1001,1
p-1002,3
p-1003,5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใน Postman script หรือ request สามารถเรียกใช้ตัวแปรเป็น &lt;code&gt;{{productId}}&lt;/code&gt; และ &lt;code&gt;{{quantity}}&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  จำกัดจำนวนรอบ
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newman run orders-api.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--iteration-count&lt;/span&gt; 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เหมาะกับการทดสอบซ้ำเพื่อดูความเสถียรของ endpoint&lt;/p&gt;

&lt;h3&gt;
  
  
  หยุดทันทีเมื่อเจอ failure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newman run orders-api.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bail&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เหมาะกับ pull request pipeline ที่ต้องการ feedback เร็ว&lt;/p&gt;

&lt;h3&gt;
  
  
  ตั้ง timeout ต่อ request
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newman run orders-api.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--timeout-request&lt;/span&gt; 10000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่างนี้กำหนด timeout request ละ 10 วินาที ลดความเสี่ยงที่ CI job จะค้างเพราะ service ไม่ตอบ&lt;/p&gt;

&lt;h3&gt;
  
  
  หน่วงเวลาระหว่าง request
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newman run orders-api.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--delay-request&lt;/span&gt; 500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เหมาะกับ API ที่มี rate limit หรือระบบ staging ที่รับโหลดได้จำกัด&lt;/p&gt;

&lt;h3&gt;
  
  
  รันเฉพาะ folder
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newman run orders-api.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--folder&lt;/span&gt; &lt;span class="s2"&gt;"Smoke Tests"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ใช้แยก pipeline ได้ เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pull request: รันเฉพาะ &lt;code&gt;Smoke Tests&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nightly build: รัน regression ทั้ง collection&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ข้อผิดพลาดทั่วไปเมื่อย้ายจาก Postman ไป Newman
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. ลืมส่ง environment file
&lt;/h3&gt;

&lt;p&gt;Request ที่รันผ่านใน Postman อาจ fail ใน Newman ถ้าตัวแปรอยู่ใน active environment แต่ไม่ได้ส่งไฟล์ environment ให้ Newman&lt;/p&gt;

&lt;p&gt;ควรระบุเสมอ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newman run orders-api.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--environment&lt;/span&gt; staging.postman_environment.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Hardcode ค่าเฉพาะเครื่อง
&lt;/h3&gt;

&lt;p&gt;หลีกเลี่ยงการ hardcode เช่น token, base URL หรือ user ID ใน request โดยตรง ให้ใช้ environment variable แทน:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{baseUrl}}/orders/{{orderId}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แล้วจัดการค่าผ่าน environment file หรือ CI secrets&lt;/p&gt;

&lt;h3&gt;
  
  
  3. ใช้ token ที่หมดอายุ
&lt;/h3&gt;

&lt;p&gt;ถ้า environment file มี token แบบ static pipeline อาจ fail เมื่อ token หมดอายุ ทางแก้คือเพิ่ม request สำหรับ login/auth แล้วเก็บ token ด้วย script&lt;/p&gt;

&lt;p&gt;ตัวอย่าง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;pm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accessToken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จากนั้นใช้ใน header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Authorization: Bearer {{accessToken}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Export collection แล้วลืมอัปเดต repo
&lt;/h3&gt;

&lt;p&gt;ไฟล์ collection ใน repository เป็น snapshot ถ้าแก้ collection ใน Postman แล้วไม่ export ใหม่ CI จะยังรันเวอร์ชันเก่า&lt;/p&gt;

&lt;p&gt;แนวทางที่ควรทำ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ทุกการแก้ test ต้อง export ใหม่&lt;/li&gt;
&lt;li&gt;commit collection และ environment พร้อมโค้ด&lt;/li&gt;
&lt;li&gt;review diff ของ JSON ใน pull request&lt;/li&gt;
&lt;li&gt;ทดสอบด้วย Newman local ก่อน push
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newman run api-tests/orders-api.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--environment&lt;/span&gt; api-tests/staging.postman_environment.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. พึ่งพา Postman cloud/session มากเกินไป
&lt;/h3&gt;

&lt;p&gt;คอลเลกชันที่อ้างอิง state จาก cloud sync หรือ login session อาจไม่ทำงานเหมือนเดิมเมื่อรันจากไฟล์ JSON ใน CI ให้ทดสอบไฟล์ export ด้วย Newman ในเครื่องก่อนเสมอ&lt;/p&gt;

&lt;h2&gt;
  
  
  ควรใช้ Postman หรือ Newman เมื่อไหร่
&lt;/h2&gt;

&lt;p&gt;ใช้ &lt;strong&gt;Postman&lt;/strong&gt; เมื่อมีคนกำลังทำงานกับ API โดยตรง:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ออกแบบ request ใหม่&lt;/li&gt;
&lt;li&gt;ดีบั๊ก response&lt;/li&gt;
&lt;li&gt;ทดลอง endpoint ของ third-party service&lt;/li&gt;
&lt;li&gt;เขียนและปรับ test script&lt;/li&gt;
&lt;li&gt;จัดระเบียบ collection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ใช้ &lt;strong&gt;Newman&lt;/strong&gt; เมื่อต้องการให้ระบบรันเอง:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;รัน test ทุก pull request&lt;/li&gt;
&lt;li&gt;รัน nightly regression&lt;/li&gt;
&lt;li&gt;รัน smoke test หลัง deploy&lt;/li&gt;
&lt;li&gt;สร้าง API monitoring job แบบ scheduled&lt;/li&gt;
&lt;li&gt;ส่งผลลัพธ์เข้า CI dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ขอบเขตที่จำง่ายคือ:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Postman ใช้สร้างงาน ส่วน Newman ใช้รันงานอัตโนมัติ&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ถ้าไม่ต้องการใช้ Newman เป็นตัวรันแยก ดูตัวเลือกเพิ่มเติมในคู่มือ &lt;a href="http://apidog.com/blog/run-postman-collections-ci-without-newman?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;รันคอลเลกชัน Postman ใน CI โดยไม่มี Newman&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ทางเลือกแบบครบวงจร: Apidog
&lt;/h2&gt;

&lt;p&gt;การใช้ Postman ร่วมกับ Newman ต้องดูแลหลายส่วน: export collection, sync ไฟล์ JSON, จัดการ environment และตั้งค่าตัวรันใน CI/CD&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; รวมงานออกแบบ API, debug request, automated testing และ CI/CD runner ไว้ในแพลตฟอร์มเดียว คุณสามารถสร้าง test scenario พร้อม assertion ในแอป แล้วนำไปรันใน pipeline ด้วย command-line runner โดยไม่ต้อง export/sync collection แยกเหมือน workflow แบบ Postman + Newman&lt;/p&gt;

&lt;p&gt;Apidog ยังรองรับ API design, mock servers และ performance testing ใน workspace เดียวกัน คุณสามารถ &lt;a href="https://apidog.com/download?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ดาวน์โหลด Apidog&lt;/a&gt; และใช้ฟีเจอร์ทดสอบได้ฟรี&lt;/p&gt;

&lt;p&gt;สำหรับการเปรียบเทียบเครื่องมือเพิ่มเติม ดูรายการ &lt;a href="http://apidog.com/blog/best-postman-alternatives-for-api-testing?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ทางเลือกที่ดีที่สุดของ Postman สำหรับการทดสอบ API&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  คำถามที่พบบ่อย
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Newman เป็นตัวแทนของ Postman ใช่หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ไม่ใช่ Newman สร้างหรือแก้ไข collection ไม่ได้ ทำได้เพียงรัน collection ที่สร้างไว้แล้ว คุณยังต้องใช้ Postman หรือเครื่องมืออื่นเพื่อสร้าง request และเขียน test script&lt;/p&gt;

&lt;h3&gt;
  
  
  Newman มีค่าใช้จ่ายหรือไม่?
&lt;/h3&gt;

&lt;p&gt;ไม่มี Newman เป็นโอเพ่นซอร์สและใช้ฟรี แจกจ่ายผ่าน npm package ส่วน Postman มีทั้งแผนฟรีและแผนเสียเงิน แต่ Newman เองไม่มีค่าใช้จ่าย&lt;/p&gt;

&lt;h3&gt;
  
  
  Test script จาก Postman จะทำงานเหมือนกันใน Newman หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ใช่ Newman ใช้ execution engine เดียวกับ Postman ดังนั้น &lt;code&gt;pm.test&lt;/code&gt;, assertion และ request logic จะทำงานเหมือนกันเมื่อ export collection และ environment อย่างถูกต้อง&lt;/p&gt;

&lt;h3&gt;
  
  
  Newman รายงาน test failure อย่างไร?
&lt;/h3&gt;

&lt;p&gt;Newman แสดงผลใน terminal และ exit ด้วย status code ที่ไม่ใช่ &lt;code&gt;0&lt;/code&gt; เมื่อมี test fail CI/CD จึงตรวจจับ failure ได้ทันที นอกจากนี้ยังรองรับ reporter เช่น JUnit XML และ HTML สำหรับนำผลลัพธ์ไปแสดงใน dashboard&lt;/p&gt;

&lt;h3&gt;
  
  
  รัน Newman โดยไม่ติดตั้ง Node.js ได้ไหม?
&lt;/h3&gt;

&lt;p&gt;ได้ ถ้าไม่ต้องการติดตั้ง Node.js โดยตรง ให้ใช้ Docker image &lt;code&gt;postman/newman&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;:/etc/newman"&lt;/span&gt; postman/newman run &lt;span class="se"&gt;\&lt;/span&gt;
  /etc/newman/api-tests/orders-api.postman_collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--environment&lt;/span&gt; /etc/newman/api-tests/staging.postman_environment.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;วิธีนี้เหมาะกับ CI environment ที่ไม่ต้องการจัดการ Node.js runtime เอง&lt;/p&gt;

</description>
    </item>
    <item>
      <title>เครื่องมือทดสอบ API ออนไลน์ฟรี: สรุปฉบับใช้งานจริง</title>
      <dc:creator>Thanawat Wongchai</dc:creator>
      <pubDate>Fri, 22 May 2026 07:28:45 +0000</pubDate>
      <link>https://forem.com/thanawat_wonchai/ekhruuengmuuethdsb-api-nailnfrii-srupchbabaichngaancchring-d6d</link>
      <guid>https://forem.com/thanawat_wonchai/ekhruuengmuuethdsb-api-nailnfrii-srupchbabaichngaancchring-d6d</guid>
      <description>&lt;p&gt;คุณไม่จำเป็นต้องมีใบอนุญาตแบบเสียเงินเพื่อทดสอบ API อย่างจริงจัง เครื่องมือฟรีจำนวนมากสามารถส่ง request, ตรวจ status code, ตรวจ response body, จัดการ environment และรันชุดทดสอบขนาดเล็กก่อน deploy ได้ สิ่งสำคัญคือเลือกเครื่องมือที่ไม่จำกัดฟีเจอร์หลักเมื่อ workflow ของคุณเริ่มจริงจังขึ้น&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com/?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation" class="crayons-btn crayons-btn--primary"&gt;ลองใช้ Apidog วันนี้&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;บทความนี้สรุปเครื่องมือทดสอบ API ออนไลน์ฟรีที่ใช้งานได้จริง พร้อมจุดแข็ง ข้อจำกัด และแนวทางเลือกใช้งานแบบลงมือทำ เพื่อให้คุณเริ่มจาก request แรก ไปจนถึง automated test และ CI ได้โดยไม่ต้องเปลี่ยนเครื่องมือเร็วเกินไป&lt;/p&gt;

&lt;h2&gt;
  
  
  “ออนไลน์ฟรี” หมายถึงอะไร
&lt;/h2&gt;

&lt;p&gt;คำว่า “ออนไลน์” ในบริบทของ API testing มักหมายถึงหนึ่งในสามรูปแบบนี้:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;ทำงานบนเบราว์เซอร์&lt;/strong&gt;: ไม่ต้องติดตั้ง เช่น Hoppscotch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;เดสก์ท็อป + ซิงค์คลาวด์&lt;/strong&gt;: ติดตั้งแอป แต่แชร์/ซิงค์ผ่านเว็บได้ เช่น Apidog, Postman&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;โอเพนซอร์ส/self-hosted&lt;/strong&gt;: ฟรีระยะยาว แต่คุณต้องดูแลการรันเอง เช่น SoapUI หรือ Hoppscotch แบบ self-host&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ก่อนเลือก ให้ตรวจ 3 ข้อจำกัดหลักของแพลนฟรี:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Collaboration&lt;/strong&gt;: ใช้คนเดียวฟรี แต่เพิ่มทีมแล้วคิดตาม seat&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run history / monitoring&lt;/strong&gt;: เก็บผลลัพธ์ย้อนหลังได้จำกัด&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation quota&lt;/strong&gt;: scheduled run หรือ CI run อาจนับเป็นหน่วยใช้งาน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ถ้าคุณยังแยกไม่ชัดว่าควรทดสอบระดับ “scenario” หรือ “case” อย่างไร อ่านเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/test-scenario-vs-test-case?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;สถานการณ์การทดสอบและกรณีทดสอบ&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  วิธีประเมินเครื่องมือแบบเร็ว
&lt;/h2&gt;

&lt;p&gt;ใช้ test flow เดียวกันกับทุกเครื่องมือ เพื่อเปรียบเทียบอย่างยุติธรรม:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;สร้าง environment เช่น &lt;code&gt;baseUrl&lt;/code&gt;, &lt;code&gt;token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ส่ง request แรก เช่น login หรือ create resource&lt;/li&gt;
&lt;li&gt;assert อย่างน้อย:

&lt;ul&gt;
&lt;li&gt;status code&lt;/li&gt;
&lt;li&gt;field สำคัญใน response&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ดึงค่าจาก response ไปใช้ใน request ที่สอง&lt;/li&gt;
&lt;li&gt;รันเป็น collection หรือ scenario&lt;/li&gt;
&lt;li&gt;export เก็บไว้ใน version control ถ้าทำได้&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ตัวอย่าง assertion ขั้นต่ำที่ควรมี:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Status code = 200 หรือ 201
Response มี field ที่ต้องใช้ต่อ เช่น id, token, status
Response time ไม่เกิน threshold ที่ทีมยอมรับ
Business value ถูกต้อง เช่น currency, role, order status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  เครื่องมือที่คุ้มค่ากับเวลาของคุณ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Apidog
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; เป็นแพลตฟอร์ม API แบบครบวงจรสำหรับออกแบบ ดีบัก ทดสอบอัตโนมัติ ทำ mock และสร้างเอกสาร API ในที่เดียว แพลนฟรีรองรับ REST, GraphQL, SOAP และ WebSocket และใช้สร้าง test scenario ที่ chain request ต่อกันได้โดยไม่ต้องใช้บัตรเครดิต&lt;/p&gt;

&lt;p&gt;จุดที่เหมาะกับ workflow จริง:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ใช้ environment variables แยก &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;staging&lt;/code&gt;, &lt;code&gt;production&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;สร้าง request chain เช่น login → create order → get order&lt;/li&gt;
&lt;li&gt;ใช้ visual assertions เพื่อตรวจ response&lt;/li&gt;
&lt;li&gt;ใช้ built-in mock server เพื่อทดสอบ endpoint ที่ยังไม่พร้อม&lt;/li&gt;
&lt;li&gt;ใช้เดสก์ท็อปบน Windows, macOS และ Linux พร้อม cloud sync&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่าง flow ที่ควรลองใน Apidog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /auth/login
  -&amp;gt; เก็บ token

POST /orders
  -&amp;gt; ใช้ token
  -&amp;gt; assert status = 201
  -&amp;gt; เก็บ orderId

GET /orders/{orderId}
  -&amp;gt; assert response.id = orderId
  -&amp;gt; assert response.status มีค่าตามที่คาดหวัง
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เริ่มใช้งานได้จาก &lt;a href="https://apidog.com/download?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ดาวน์โหลด Apidog&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Hoppscotch
&lt;/h3&gt;

&lt;p&gt;Hoppscotch ทำงานบนเบราว์เซอร์และเป็นโอเพนซอร์ส เหมาะเมื่อคุณต้องการส่ง request อย่างรวดเร็วโดยไม่ติดตั้งแอป รองรับ REST, GraphQL และ WebSocket รวมถึง environment และ collection&lt;/p&gt;

&lt;p&gt;เหมาะกับ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ทดสอบ endpoint แบบเร็ว&lt;/li&gt;
&lt;li&gt;ใช้งานคนเดียว&lt;/li&gt;
&lt;li&gt;ใช้บนเครื่องที่ไม่ต้องการติดตั้งเครื่องมือเพิ่ม&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ข้อจำกัดที่ควรรู้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automation ขั้นสูงไม่เด่นเท่าเครื่องมือทดสอบเฉพาะทาง&lt;/li&gt;
&lt;li&gt;collaboration และ history ขั้นสูงอยู่ในแผนทีมแบบเสียเงิน&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Postman แพลนฟรี
&lt;/h3&gt;

&lt;p&gt;Postman เป็นเครื่องมือที่นักพัฒนาจำนวนมากคุ้นเคย แพลนฟรีครอบคลุม manual request, collections, environments และจำนวน automated run รายเดือนแบบจำกัด&lt;/p&gt;

&lt;p&gt;เหมาะกับ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ทีมที่มี collection เดิมอยู่แล้ว&lt;/li&gt;
&lt;li&gt;developer onboarding เพราะหลายคนใช้งานเป็น&lt;/li&gt;
&lt;li&gt;export collection ไปใช้กับ Newman ใน CI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่างรัน Postman collection ด้วย Newman:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newman run collection.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--environment&lt;/span&gt; staging.postman_environment.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ข้อจำกัดหลักคือ collaboration seat และ automation quota หากคุณกำลังเทียบ workflow กับเครื่องมืออื่น อ่าน &lt;a href="http://apidog.com/blog/how-to-test-apis-with-postman?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;วิธีการทดสอบ API ด้วย Postman&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Insomnia
&lt;/h3&gt;

&lt;p&gt;Insomnia เป็นเดสก์ท็อปไคลเอ็นต์สำหรับ REST, GraphQL และ gRPC จุดเด่นคือ UI ที่สะอาดและเหมาะกับการดีบัก API แบบโฟกัส&lt;/p&gt;

&lt;p&gt;เหมาะกับ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;developer ที่ต้องการ client เบาและไม่ซับซ้อน&lt;/li&gt;
&lt;li&gt;การทดสอบส่วนบุคคล&lt;/li&gt;
&lt;li&gt;scripted test ขนาดเล็ก&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ดูขั้นตอนใช้งานเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/how-to-use-insomnia-test-api?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การใช้ Insomnia เพื่อทดสอบ API&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  SoapUI โอเพนซอร์ส
&lt;/h3&gt;

&lt;p&gt;SoapUI เป็นตัวเลือกที่ใช้มายาวนานสำหรับ SOAP และยังรองรับ REST เวอร์ชันโอเพนซอร์สเหมาะกับ functional test และ data-driven test โดยเฉพาะกับระบบ legacy&lt;/p&gt;

&lt;p&gt;เหมาะกับ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SOAP API&lt;/li&gt;
&lt;li&gt;WSDL-based testing&lt;/li&gt;
&lt;li&gt;data-driven test ที่ต้องวนหลายชุดข้อมูล&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ข้อจำกัด:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;แอป Java ค่อนข้างหนัก&lt;/li&gt;
&lt;li&gt;reporting ที่สวยและฟีเจอร์ขั้นสูงอยู่ใน ReadyAPI แบบเสียเงิน&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Thunder Client
&lt;/h3&gt;

&lt;p&gt;Thunder Client เป็น extension ใน VS Code เหมาะถ้าคุณใช้ VS Code เป็นหลักและต้องการทดสอบ API โดยไม่เปลี่ยนหน้าต่าง&lt;/p&gt;

&lt;p&gt;เหมาะกับ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST/GraphQL request จากใน editor&lt;/li&gt;
&lt;li&gt;collection ขนาดเล็ก&lt;/li&gt;
&lt;li&gt;การทดสอบแบบไม่ต้องเขียนสคริปต์มาก&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ข้อจำกัดคือ Git sync และ team features ต้องใช้แผนเสียเงิน&lt;/p&gt;

&lt;h2&gt;
  
  
  ตารางเปรียบเทียบ
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;เครื่องมือ&lt;/th&gt;
&lt;th&gt;ประเภท&lt;/th&gt;
&lt;th&gt;โปรโตคอล&lt;/th&gt;
&lt;th&gt;จุดแข็งของแพลนฟรี&lt;/th&gt;
&lt;th&gt;ข้อจำกัดหลัก&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Apidog&lt;/td&gt;
&lt;td&gt;เดสก์ท็อป + ซิงค์คลาวด์&lt;/td&gt;
&lt;td&gt;REST, GraphQL, SOAP, WebSocket&lt;/td&gt;
&lt;td&gt;ออกแบบ, ทดสอบ, จำลอง, เอกสารครบวงจร&lt;/td&gt;
&lt;td&gt;ทีมขนาดใหญ่ต้องการที่นั่งแบบเสียเงิน&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hoppscotch&lt;/td&gt;
&lt;td&gt;เบราว์เซอร์, โอเพนซอร์ส&lt;/td&gt;
&lt;td&gt;REST, GraphQL, WebSocket&lt;/td&gt;
&lt;td&gt;ไม่ต้องติดตั้ง, รวดเร็ว&lt;/td&gt;
&lt;td&gt;ระบบอัตโนมัติเบากว่า&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Postman&lt;/td&gt;
&lt;td&gt;เดสก์ท็อป + คลาวด์&lt;/td&gt;
&lt;td&gt;REST, GraphQL, gRPC&lt;/td&gt;
&lt;td&gt;คุ้นเคย, มีเอกสารประกอบดี&lt;/td&gt;
&lt;td&gt;การรันแบบจำกัด, ที่นั่งแบบเสียเงิน&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insomnia&lt;/td&gt;
&lt;td&gt;เดสก์ท็อป&lt;/td&gt;
&lt;td&gt;REST, GraphQL, gRPC&lt;/td&gt;
&lt;td&gt;ประสบการณ์ผู้ใช้ดีบักที่สะอาดตา&lt;/td&gt;
&lt;td&gt;ชุดคุณสมบัติการทดสอบเล็กกว่า&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SoapUI&lt;/td&gt;
&lt;td&gt;เดสก์ท็อป, โอเพนซอร์ส&lt;/td&gt;
&lt;td&gt;SOAP, REST&lt;/td&gt;
&lt;td&gt;ทดสอบ SOAP และขับเคลื่อนด้วยข้อมูลได้ลึกซึ้ง&lt;/td&gt;
&lt;td&gt;แอปพลิเคชันหนัก, การรายงานเสียเงิน&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thunder Client&lt;/td&gt;
&lt;td&gt;ส่วนขยาย VS Code&lt;/td&gt;
&lt;td&gt;REST, GraphQL&lt;/td&gt;
&lt;td&gt;สะดวกในตัวแก้ไข&lt;/td&gt;
&lt;td&gt;ซิงค์และคุณสมบัติทีมเสียเงิน&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  วิธีเลือกเครื่องมือ
&lt;/h2&gt;

&lt;p&gt;เริ่มจาก protocol ที่คุณใช้งานจริง:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ใช้ &lt;strong&gt;REST / GraphQL&lt;/strong&gt;: เครื่องมือส่วนใหญ่ในบทความนี้เพียงพอ&lt;/li&gt;
&lt;li&gt;ใช้ &lt;strong&gt;SOAP&lt;/strong&gt;: เลือกเครื่องมือที่รองรับ SOAP โดยตรง เช่น Apidog หรือ SoapUI อ่านเพิ่มได้ที่ &lt;a href="http://apidog.com/blog/online-soap-api-tester?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;เครื่องมือทดสอบ SOAP API ออนไลน์&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ใช้ &lt;strong&gt;WebSocket&lt;/strong&gt;: จำกัดตัวเลือกไปที่ Apidog, Hoppscotch หรือ WebSocket client เฉพาะทาง&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;จากนั้นเลือกตามสภาพแวดล้อมการทำงาน:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;เงื่อนไข&lt;/th&gt;
&lt;th&gt;ควรเลือก&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ไม่อยากติดตั้งอะไร&lt;/td&gt;
&lt;td&gt;Browser tool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ต้องยิง &lt;code&gt;localhost&lt;/code&gt; หรือ internal network&lt;/td&gt;
&lt;td&gt;Desktop app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ต้องทำงาน offline&lt;/td&gt;
&lt;td&gt;Desktop app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ต้องแชร์ collection กับทีม&lt;/td&gt;
&lt;td&gt;Desktop + cloud sync หรือ hosted workspace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ต้องออกแบบ + mock + test ในที่เดียว&lt;/td&gt;
&lt;td&gt;All-in-one platform&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;แนะนำให้ลองด้วย flow เดียวกัน เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. POST /login
2. เก็บ access_token
3. GET /profile ด้วย token
4. assert status code
5. assert response.email หรือ response.id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เครื่องมือที่ทำขั้นตอนนี้ได้ง่ายที่สุดสำหรับทีมคุณคือจุดเริ่มต้นที่ดี สำหรับแนวทางเขียน assertion อ่าน &lt;a href="http://apidog.com/blog/api-assertions?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การเขียนการยืนยัน API ที่มีประโยชน์&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  เครื่องมือฟรีกับ CI Pipelines
&lt;/h2&gt;

&lt;p&gt;เครื่องมือฟรีจำนวนมากสามารถใช้กับ CI ได้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Postman export collection แล้วรันด้วย Newman&lt;/li&gt;
&lt;li&gt;Hoppscotch มี CLI&lt;/li&gt;
&lt;li&gt;Apidog รัน scenario จาก runner ของตัวเองและทำงานร่วมกับ pipeline ได้&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ข้อจำกัดของแพลนฟรีมักเป็น “จำนวนรัน” มากกว่า “ทำ CI ไม่ได้” ดังนั้น nightly test suite มักเพียงพอ แต่ถ้ารันทุก commit ใน repo ที่ activity สูง อาจชน quota เร็ว&lt;/p&gt;

&lt;p&gt;ตัวอย่าง GitHub Actions สำหรับ Newman:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;API Tests&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api-test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Newman&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install -g newman&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run API collection&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;newman run collection.json --environment staging.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้า CI/CD เป็นเป้าหมายหลัก อ่าน &lt;a href="http://apidog.com/blog/automate-api-tests-ci-cd?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ทำให้การทดสอบ API เป็นอัตโนมัติใน CI/CD&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  อย่าทดสอบแค่ “200 OK”
&lt;/h2&gt;

&lt;p&gt;การทดสอบที่ตรวจแค่ status code มักพลาด bug สำคัญ ควรตรวจอย่างน้อย:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;status code ถูกต้องตามกรณี เช่น &lt;code&gt;200&lt;/code&gt;, &lt;code&gt;201&lt;/code&gt;, &lt;code&gt;400&lt;/code&gt;, &lt;code&gt;401&lt;/code&gt;, &lt;code&gt;404&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;response schema มี field ที่จำเป็น&lt;/li&gt;
&lt;li&gt;business value ถูกต้อง&lt;/li&gt;
&lt;li&gt;error response มี message/code ที่ client ใช้ได้&lt;/li&gt;
&lt;li&gt;response time อยู่ในช่วงที่ยอมรับได้&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่าง checklist สำหรับ REST endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /users/{id}

[ ] 200 เมื่อ id มีอยู่จริง
[ ] 404 เมื่อ id ไม่มีอยู่
[ ] response.id ตรงกับ path parameter
[ ] response.email เป็น string
[ ] ไม่มี field ลับ เช่น password_hash
[ ] latency ไม่เกิน threshold
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;อ่านพื้นฐานเพิ่มเติมที่ &lt;a href="http://apidog.com/blog/which-http-status-codes-rest-apis-should-use?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;รหัสสถานะ HTTP ที่ REST API ควรใช้&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ข้อผิดพลาดที่พบบ่อยกับเครื่องมือฟรี
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. เลือกเหมือนเป็น trial ชั่วคราว
&lt;/h3&gt;

&lt;p&gt;อย่าเลือกเครื่องมือที่คุณรู้อยู่แล้วว่าจะต้องย้ายออกในหนึ่งเดือน เลือกเครื่องมือที่แพลนฟรีรองรับงานหลักได้อย่างน้อยตลอดช่วงเริ่มต้นของโปรเจกต์&lt;/p&gt;

&lt;h3&gt;
  
  
  2. ไม่ใช้ environment variables
&lt;/h3&gt;

&lt;p&gt;หลีกเลี่ยงการ hard-code base URL หรือ token ในทุก request&lt;/p&gt;

&lt;p&gt;ควรใช้แบบนี้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{{baseUrl}}/users
Authorization: Bearer {{token}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ไม่ควรใช้แบบนี้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://staging.example.com/users
Authorization: Bearer eyJhbGciOi...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. ไม่สนใจ response time
&lt;/h3&gt;

&lt;p&gt;ถ้า endpoint ที่ควรตอบใน 100 ms ใช้เวลา 800 ms นั่นคือสัญญาณที่ควรตรวจ แม้ยังไม่ได้ทำ load testing ก็ตาม&lt;/p&gt;

&lt;p&gt;สำหรับงาน performance โดยตรง อ่าน &lt;a href="http://apidog.com/blog/api-performance-testing-tutorial?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;บทช่วยสอนการทดสอบประสิทธิภาพ API&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. ไม่ export งานออกมาเก็บ
&lt;/h3&gt;

&lt;p&gt;แพลนฟรีของ hosted tool อาจเปลี่ยนเงื่อนไขได้ ควร export collection/spec แล้วเก็บใน Git เป็นระยะ&lt;/p&gt;

&lt;p&gt;ตัวอย่างโครงสร้าง repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;api-tests/
  collections/
    users.collection.json
    orders.collection.json
  environments/
    staging.json
    production.example.json
  README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Browser tool vs Desktop app
&lt;/h2&gt;

&lt;p&gt;เครื่องมือบนเบราว์เซอร์เหมาะกับการเริ่มเร็ว แต่ทำงานภายใต้ browser sandbox ซึ่งอาจจำกัดบางกรณี เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เรียก &lt;code&gt;localhost&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;เรียก private network&lt;/li&gt;
&lt;li&gt;อัปโหลดไฟล์ใหญ่&lt;/li&gt;
&lt;li&gt;payload binary บางประเภท&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Desktop app เหมาะเมื่อคุณต้องการ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เข้าถึง local service โดยตรง&lt;/li&gt;
&lt;li&gt;ทดสอบ API ใน internal network&lt;/li&gt;
&lt;li&gt;ทำงาน offline&lt;/li&gt;
&lt;li&gt;จัดการ payload ขนาดใหญ่&lt;/li&gt;
&lt;li&gt;ใช้ native network stack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แนวทางที่ทีมจำนวนมากเลือกคือ desktop app ที่ซิงค์คลาวด์ได้ เพราะได้ทั้ง native access และการแชร์/ซิงค์ collection ระหว่างเครื่อง Apidog ทำงานในรูปแบบนี้ จึงเหมาะกับทีมที่ต้องการทั้งการดีบักในเครื่องและ collaboration&lt;/p&gt;

&lt;h2&gt;
  
  
  ดูแลชุดทดสอบให้ไม่เสื่อม
&lt;/h2&gt;

&lt;p&gt;ชุดทดสอบ API เสื่อมได้เหมือน code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;endpoint เปลี่ยน&lt;/li&gt;
&lt;li&gt;field ถูก rename&lt;/li&gt;
&lt;li&gt;business rule เปลี่ยน&lt;/li&gt;
&lt;li&gt;test ยังผ่าน แต่ตรวจสิ่งที่ไม่สำคัญแล้ว&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตั้งรอบ review ทุก 2-4 สัปดาห์:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] ลบ request ที่ endpoint ไม่มีแล้ว
[ ] อัปเดต assertion ที่ตรวจ field เก่า
[ ] ตรวจว่า environment ยังถูกต้อง
[ ] เช็ก test ที่ flaky
[ ] export collection ล่าสุดเข้า Git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตั้งชื่อ request ให้บอกพฤติกรรม ไม่ใช่ลำดับ:&lt;/p&gt;

&lt;p&gt;ไม่ดี:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test 1
test 2
test 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ดี:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create order with valid payload
create order with invalid currency
get order by existing order id
reject unauthenticated request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จัดกลุ่มตาม user flow เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auth
  - login with valid credentials
  - reject invalid password

Orders
  - create order
  - get order
  - cancel order
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หลักการตั้งชื่อเดียวกับที่ทำให้ &lt;a href="http://apidog.com/blog/api-test-case-example?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;กรณีทดสอบ&lt;/a&gt; อ่านง่าย ก็ใช้กับ API collection ได้เช่นกัน&lt;/p&gt;

&lt;h2&gt;
  
  
  คำถามที่พบบ่อย
&lt;/h2&gt;

&lt;h3&gt;
  
  
  เครื่องมือทดสอบ API ฟรีดีพอสำหรับงานจริงหรือไม่
&lt;/h3&gt;

&lt;p&gt;ดีพอสำหรับทีมส่วนใหญ่ในช่วงเริ่มต้นและงานประจำวัน แพลนฟรีมักครอบคลุม request, assertions, environments และ automation พื้นฐาน เหตุผลหลักที่ต้องอัปเกรดมักเป็น team seats, run history หรือ CI quota ไม่ใช่เพราะทดสอบไม่ได้&lt;/p&gt;

&lt;h3&gt;
  
  
  ฉันทดสอบ SOAP APIs ด้วยเครื่องมือออนไลน์ฟรีได้หรือไม่
&lt;/h3&gt;

&lt;p&gt;ได้ Apidog รองรับ SOAP ในแพลนฟรี และ SoapUI โอเพนซอร์สก็เหมาะกับ SOAP โดยเฉพาะ SOAP ต้องใช้ XML envelope และมักเกี่ยวข้องกับ WSDL ดังนั้นควรใช้เครื่องมือที่รองรับ SOAP โดยตรง ดูรายละเอียด protocol ได้จาก &lt;a href="https://www.w3.org/TR/soap12/" rel="noopener noreferrer"&gt;ข้อกำหนด SOAP อย่างเป็นทางการ&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  เครื่องมือเบราว์เซอร์กับเดสก์ท็อปต่างกันอย่างไร
&lt;/h3&gt;

&lt;p&gt;เครื่องมือเบราว์เซอร์ไม่ต้องติดตั้งและใช้งานข้ามเครื่องได้ง่าย แต่ browser security อาจจำกัดการเรียก local/private network ส่วนเดสก์ท็อปเข้าถึง local service และ payload ใหญ่ได้ดีกว่า และมักทำงาน offline ได้&lt;/p&gt;

&lt;h3&gt;
  
  
  เครื่องมือฟรีรองรับ automated test suite หรือไม่
&lt;/h3&gt;

&lt;p&gt;ส่วนใหญ่รองรับ คุณสามารถ chain request, เพิ่ม assertion และรันเป็นชุดได้ Postman ใช้ร่วมกับ &lt;a href="https://github.com/postman-tools/newman" rel="noopener noreferrer"&gt;Newman&lt;/a&gt; ได้ ส่วน Hoppscotch และ Apidog มี runner ของตัวเอง ข้อจำกัดหลักมักเป็นจำนวน automated run ต่อเดือน&lt;/p&gt;

&lt;h3&gt;
  
  
  ทีมขนาดเล็กควรเริ่มด้วยเครื่องมือใด
&lt;/h3&gt;

&lt;p&gt;ถ้าต้องการแค่ยิง request เร็ว ๆ Hoppscotch หรือ Thunder Client เริ่มได้ง่าย ถ้าต้องการออกแบบ ทดสอบ mock และเอกสารใน workflow เดียว Apidog เป็นตัวเลือกที่เหมาะกว่า สำหรับทีมขนาดเล็ก ให้รัน flow เดียวกันในแต่ละเครื่องมือ เช่น login → create resource → get resource พร้อม assertion แล้วเลือกเครื่องมือที่ใช้งานได้ลื่นที่สุดกับ stack ของคุณ&lt;/p&gt;

</description>
    </item>
    <item>
      <title>ประเภทของ Automation Testing Framework และวิธีเลือกให้เหมาะกับทีม</title>
      <dc:creator>Thanawat Wongchai</dc:creator>
      <pubDate>Fri, 22 May 2026 07:27:58 +0000</pubDate>
      <link>https://forem.com/thanawat_wonchai/praephthkhng-automation-testing-framework-aelawithiieluuekaihehmaaakabthiim-5dec</link>
      <guid>https://forem.com/thanawat_wonchai/praephthkhng-automation-testing-framework-aelawithiieluuekaihehmaaakabthiim-5dec</guid>
      <description>&lt;p&gt;ทีมมักบอกว่า “ใช้ระบบอัตโนมัติแล้ว” เหมือนปัญหาการจัดระเบียบการทดสอบจะจบลงทันที แต่ความจริงคือชุดทดสอบอัตโนมัติสองชุดอาจมีโครงสร้างต่างกันมาก และมีต้นทุนบำรุงรักษาต่างกันมากด้วย โครงสร้างนั้นคือ “เฟรมเวิร์ก” และประเภทเฟรมเวิร์กที่เลือกจะกำหนดว่าการทดสอบจะขยายได้เร็วแค่ไหน คนที่ไม่ใช่โปรแกรมเมอร์มีส่วนร่วมได้แค่ไหน และการเปลี่ยน UI เล็กน้อยจะทำให้โค้ดทดสอบพังมากเพียงใด&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com/?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation" class="crayons-btn crayons-btn--primary"&gt;ลองใช้ Apidog วันนี้&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;บทความนี้สรุปเฟรมเวิร์กการทดสอบอัตโนมัติ 6 ประเภทที่ใช้บ่อย ได้แก่ linear, modular, data-driven, keyword-driven, hybrid และ behavior-driven หรือ BDD พร้อมวิธีคิดเชิงปฏิบัติว่าแต่ละแบบเหมาะกับสถานการณ์ใด ควรเริ่มอย่างไร และต้องระวังอะไร แนวคิดเหล่านี้ใช้ได้กับ UI testing, API testing และ unit testing&lt;/p&gt;

&lt;h2&gt;
  
  
  เฟรมเวิร์กการทดสอบระบบอัตโนมัติคืออะไร
&lt;/h2&gt;

&lt;p&gt;เฟรมเวิร์กการทดสอบระบบอัตโนมัติคือชุดของแนวทาง ข้อตกลง และคอมโพเนนต์ที่นำกลับมาใช้ซ้ำได้ ซึ่งกำหนดวิธีเขียน จัดเก็บ รัน และรายงานผลการทดสอบอัตโนมัติ มันไม่ใช่แค่เครื่องมือที่ติดตั้ง แต่คือสถาปัตยกรรมของชุดทดสอบ&lt;/p&gt;

&lt;p&gt;เฟรมเวิร์กควรตอบคำถามเหล่านี้ก่อนเริ่มเขียนเทสต์:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ข้อมูลทดสอบเก็บไว้ที่ไหน&lt;/li&gt;
&lt;li&gt;ขั้นตอนที่ใช้ซ้ำ เช่น login หรือ create user แชร์กันอย่างไร&lt;/li&gt;
&lt;li&gt;ใครเขียนเทสต์ได้บ้าง เฉพาะวิศวกรหรือรวม QA/business analyst&lt;/li&gt;
&lt;li&gt;จะรันเทสต์อย่างไรใน CI&lt;/li&gt;
&lt;li&gt;รายงานผลและ debug failure อย่างไร&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ถ้าคุณยังใหม่กับภาพรวมของ automation testing อ่านพื้นฐานเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/what-is-automated-testing?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การทดสอบอัตโนมัติคืออะไร&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เหตุผลที่เรื่องนี้สำคัญคือ “การบำรุงรักษา” การเขียนเทสต์อัตโนมัติ 100 รายการแรกมักไม่ยาก แต่การดูแลเทสต์ 1,000 รายการในระบบที่เปลี่ยนทุกสัปดาห์คือปัญหาจริง ประเภทเฟรมเวิร์กเป็นปัจจัยหลักที่กำหนดต้นทุนนี้&lt;/p&gt;

&lt;p&gt;ตัวอย่างง่าย ๆ: ถ้าหน้า login เปลี่ยนชื่อ field จาก &lt;code&gt;username&lt;/code&gt; เป็น &lt;code&gt;email&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เฟรมเวิร์กที่ดี: แก้ selector หรือ helper เดียว เทสต์ทั้งหมดกลับมาผ่าน&lt;/li&gt;
&lt;li&gt;เฟรมเวิร์กที่ไม่ดี: ต้องแก้สคริปต์ 200 ไฟล์ที่ copy-paste ขั้นตอน login ไว้&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แอปพลิเคชันเดียวกัน แต่ต้นทุนต่างกันเพราะโครงสร้างเฟรมเวิร์กต่างกัน&lt;/p&gt;

&lt;h2&gt;
  
  
  หกประเภทของเฟรมเวิร์ก
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1. Linear หรือเชิงเส้น
&lt;/h2&gt;

&lt;p&gt;Linear framework คือการบันทึกและเล่นซ้ำขั้นตอนแบบตรงไปตรงมา เทสต์แต่ละรายการเป็นสคริปต์เดี่ยว ไม่มี shared function ไม่มี test data แยกออกจาก logic และมักเกิดจาก record-and-playback tool&lt;/p&gt;

&lt;p&gt;ตัวอย่างลักษณะสคริปต์:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้าต้องทดสอบอีก workflow หนึ่ง ก็มัก copy ขั้นตอนเดิมไปวางใหม่&lt;/p&gt;

&lt;h3&gt;
  
  
  ใช้เมื่อไหร่
&lt;/h3&gt;

&lt;p&gt;เหมาะกับ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;demo สั้น ๆ&lt;/li&gt;
&lt;li&gt;proof of concept&lt;/li&gt;
&lt;li&gt;script ที่ใช้ครั้งเดียว&lt;/li&gt;
&lt;li&gt;โปรเจกต์เล็กมากและอายุสั้น&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ข้อดี
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;เริ่มเร็ว&lt;/li&gt;
&lt;li&gt;ไม่ต้องออกแบบโครงสร้างมาก&lt;/li&gt;
&lt;li&gt;เหมาะกับผู้เริ่มต้นหรือการทดลอง tool&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ข้อเสีย
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;reuse แทบไม่มี&lt;/li&gt;
&lt;li&gt;แก้ UI หนึ่งจุดอาจต้องแก้หลายไฟล์&lt;/li&gt;
&lt;li&gt;scale ไม่ดี&lt;/li&gt;
&lt;li&gt;test suite โตแล้ว maintenance cost สูงมาก&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แนวทางปฏิบัติ: ถ้าเทสต์เริ่มมีขั้นตอนซ้ำเกิน 3–5 จุด เช่น login, search, checkout ให้เริ่มย้ายไป modular framework&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Modular หรือแบบโมดูล
&lt;/h2&gt;

&lt;p&gt;Modular framework แบ่งแอปออกเป็น module หรือ reusable function เช่น login, search product, add to cart, checkout แล้วให้เทสต์ประกอบจากฟังก์ชันเหล่านั้น&lt;/p&gt;

&lt;p&gt;ตัวอย่าง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;searchProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#search-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เทสต์จริงจะอ่านง่ายขึ้น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user can search product after login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;searchProduct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keyboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.product-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เมื่อหน้า login เปลี่ยน คุณแก้ที่ &lt;code&gt;login()&lt;/code&gt; ที่เดียว&lt;/p&gt;

&lt;h3&gt;
  
  
  ใช้เมื่อไหร่
&lt;/h3&gt;

&lt;p&gt;เหมาะกับทีมวิศวกรส่วนใหญ่ โดยเฉพาะเมื่อ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เทสต์ต้องอยู่มากกว่าหนึ่งไตรมาส&lt;/li&gt;
&lt;li&gt;มี workflow ซ้ำหลายจุด&lt;/li&gt;
&lt;li&gt;ต้องการลด copy-paste&lt;/li&gt;
&lt;li&gt;ทีมมีคนเขียนโค้ดดูแลเทสต์ได้&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ข้อดี
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;reuse สูง&lt;/li&gt;
&lt;li&gt;maintain ง่ายกว่า linear&lt;/li&gt;
&lt;li&gt;เป็นฐานที่ดีสำหรับ hybrid framework&lt;/li&gt;
&lt;li&gt;debug ง่ายถ้าออกแบบ module ดี&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ข้อเสีย
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ต้องออกแบบ abstraction ตั้งแต่ต้น&lt;/li&gt;
&lt;li&gt;ต้องตกลง naming และ module boundary&lt;/li&gt;
&lt;li&gt;ถ้าแยก module ผิด อาจกลายเป็น abstraction ที่สับสน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;สำหรับแนวทางเขียนสคริปต์ให้ maintain ได้ อ่านเพิ่มที่ &lt;a href="http://apidog.com/blog/how-to-write-automated-test-scripts?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;วิธีการเขียนสคริปต์ทดสอบอัตโนมัติ&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Data-driven หรือขับเคลื่อนด้วยข้อมูล
&lt;/h2&gt;

&lt;p&gt;Data-driven framework แยก test logic ออกจาก test data เทสต์หนึ่งชุดสามารถรันซ้ำกับข้อมูลหลายแถวจาก CSV, JSON, spreadsheet หรือ database&lt;/p&gt;

&lt;p&gt;ตัวอย่างข้อมูล &lt;code&gt;login-cases.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"valid@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"correct-password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expected"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"invalid@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wrong-password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expected"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่าง test logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cases&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./login-cases.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;cases&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`login case: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ใช้เมื่อไหร่
&lt;/h3&gt;

&lt;p&gt;เหมาะกับกรณีที่ workflow เดิมต้องทดสอบกับ input หลายแบบ เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;valid/invalid login&lt;/li&gt;
&lt;li&gt;boundary values&lt;/li&gt;
&lt;li&gt;หลายภาษา หลาย locale&lt;/li&gt;
&lt;li&gt;API request เดียวแต่ payload หลายชุด&lt;/li&gt;
&lt;li&gt;form validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;สำหรับ API testing โดยเฉพาะ แนวทาง &lt;a href="http://apidog.com/blog/data-driven-api-testing-tool-csv-json?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การทดสอบที่ขับเคลื่อนด้วยข้อมูลโดยใช้ CSV และ JSON&lt;/a&gt; ช่วยเปลี่ยน request เดียวให้กลายเป็น test case จำนวนมากได้&lt;/p&gt;

&lt;h3&gt;
  
  
  ข้อดี
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;เพิ่ม coverage ด้วยการเพิ่มข้อมูล ไม่ใช่เพิ่มโค้ด&lt;/li&gt;
&lt;li&gt;ลด duplication&lt;/li&gt;
&lt;li&gt;เหมาะกับ API และ validation-heavy workflow&lt;/li&gt;
&lt;li&gt;QA ที่ไม่ถนัดโค้ดอาจช่วยเพิ่ม test data ได้&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ข้อเสีย
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;test logic มักค่อนข้างคงที่&lt;/li&gt;
&lt;li&gt;ไม่เหมาะกับ workflow ที่แตกต่างกันมาก&lt;/li&gt;
&lt;li&gt;test data ต้องมี schema และ naming ที่ชัดเจน&lt;/li&gt;
&lt;li&gt;ข้อมูลจำนวนมากอาจทำให้ debug ยากถ้าไม่มี report ที่ดี&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แนวทางปฏิบัติ: เริ่มจาก data-driven เฉพาะจุดที่ input หลากหลายจริง ๆ อย่าเปลี่ยนทุกเทสต์ให้เป็น data table โดยไม่จำเป็น&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Keyword-driven หรือขับเคลื่อนด้วยคีย์เวิร์ด
&lt;/h2&gt;

&lt;p&gt;Keyword-driven framework แปลง action เป็น keyword เช่น &lt;code&gt;Login&lt;/code&gt;, &lt;code&gt;SearchProduct&lt;/code&gt;, &lt;code&gt;VerifyTotal&lt;/code&gt; แล้วให้ผู้ใช้สร้าง test case ด้วยตาราง keyword พร้อม argument&lt;/p&gt;

&lt;p&gt;ตัวอย่างตาราง:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Keyword&lt;/th&gt;
&lt;th&gt;Argument 1&lt;/th&gt;
&lt;th&gt;Argument 2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Login&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:user@example.com"&gt;user@example.com&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;SearchProduct&lt;/td&gt;
&lt;td&gt;keyboard&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;AddToCart&lt;/td&gt;
&lt;td&gt;SKU-001&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;VerifyTotal&lt;/td&gt;
&lt;td&gt;1200&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;เบื้องหลัง วิศวกรต้อง implement keyword เหล่านี้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keywords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#password&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;SearchProduct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#search&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#search-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ใช้เมื่อไหร่
&lt;/h3&gt;

&lt;p&gt;เหมาะกับทีมที่:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;QA analyst หรือ business user ต้องช่วยสร้างเทสต์&lt;/li&gt;
&lt;li&gt;ต้องการให้ test case อ่านเป็นขั้นตอนเชิงธุรกิจ&lt;/li&gt;
&lt;li&gt;มีวิศวกรคอยดูแล keyword library&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ข้อดี
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;คนที่ไม่ใช่โปรแกรมเมอร์อ่านและสร้างเทสต์ได้ง่ายขึ้น&lt;/li&gt;
&lt;li&gt;standardize ขั้นตอนซ้ำ ๆ ได้ดี&lt;/li&gt;
&lt;li&gt;เหมาะกับองค์กรที่แยกบทบาท test design กับ test implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ข้อเสีย
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ต้องสร้างและดูแล keyword library&lt;/li&gt;
&lt;li&gt;keyword ที่ออกแบบไม่ดีจะกลายเป็นภาระ&lt;/li&gt;
&lt;li&gt;ถ้ามี keyword เยอะเกินไป จะค้นหาและ reuse ยาก&lt;/li&gt;
&lt;li&gt;debug failure อาจต้องไล่จากตารางไป implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แนวทางปฏิบัติ: จำกัด keyword ให้เป็น business-level action เช่น &lt;code&gt;CreateOrder&lt;/code&gt; แทน keyword ระดับเล็กเกินไปอย่าง &lt;code&gt;ClickButton&lt;/code&gt; หรือ &lt;code&gt;TypeText&lt;/code&gt; เพราะจะทำให้ตารางยาวและ maintain ยาก&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Hybrid หรือแบบไฮบริด
&lt;/h2&gt;

&lt;p&gt;Hybrid framework คือการผสมหลายแนวทางเข้าด้วยกัน เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;modular เป็นแกนหลัก&lt;/li&gt;
&lt;li&gt;data-driven สำหรับ input หลายชุด&lt;/li&gt;
&lt;li&gt;keyword-driven สำหรับบาง workflow ที่ QA ต้องเขียนเอง&lt;/li&gt;
&lt;li&gt;BDD สำหรับ requirement ที่ต้องให้ product และ engineering เข้าใจร่วมกัน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่างโครงสร้าง hybrid สำหรับ API testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tests/
  auth/
    login.spec.js
  orders/
    create-order.spec.js
data/
  login-cases.json
  order-cases.csv
helpers/
  auth.js
  orders.js
reports/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ใช้เมื่อไหร่
&lt;/h3&gt;

&lt;p&gt;เหมาะกับ test suite ที่โตแล้ว มีหลายทีม หรือมีหลายประเภทการทดสอบ เช่น UI, API, integration และ regression&lt;/p&gt;

&lt;h3&gt;
  
  
  ข้อดี
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ยืดหยุ่นที่สุด&lt;/li&gt;
&lt;li&gt;เลือก pattern ให้เหมาะกับแต่ละปัญหา&lt;/li&gt;
&lt;li&gt;scale ได้ดีเมื่อมี convention ชัดเจน&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ข้อเสีย
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ถ้าไม่มี design guideline จะไม่สอดคล้องกัน&lt;/li&gt;
&lt;li&gt;อาจแบกต้นทุนของทุกแบบพร้อมกัน&lt;/li&gt;
&lt;li&gt;onboarding ยากถ้าไม่มีเอกสาร&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แนวทางปฏิบัติ: เขียน &lt;code&gt;testing-guidelines.md&lt;/code&gt; ระบุให้ชัดว่าเมื่อใดควรใช้ modular, data-driven, keyword หรือ BDD เพื่อป้องกันแต่ละทีมออกแบบคนละทาง&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Behavior-driven หรือ BDD
&lt;/h2&gt;

&lt;p&gt;BDD framework แสดง test scenario ด้วยภาษาที่ใกล้เคียงภาษาธรรมชาติ โดยใช้รูปแบบ Given-When-Then มักเขียนด้วย Gherkin และรันด้วยเครื่องมือเช่น Cucumber&lt;/p&gt;

&lt;p&gt;ตัวอย่าง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gherkin"&gt;&lt;code&gt;&lt;span class="kd"&gt;Feature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Login

  &lt;span class="kn"&gt;Scenario&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; Registered user logs in successfully
    &lt;span class="nf"&gt;Given &lt;/span&gt;a registered user exists
    &lt;span class="nf"&gt;When &lt;/span&gt;the user submits valid credentials
    &lt;span class="nf"&gt;Then &lt;/span&gt;the user should see the dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เบื้องหลังยังต้องมี step definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nc"&gt;Given&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a registered user exists&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// create or prepare user&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nc"&gt;When&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;the user submits valid credentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// submit login form or API request&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nc"&gt;Then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;the user should see the dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// assertion&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ใช้เมื่อไหร่
&lt;/h3&gt;

&lt;p&gt;เหมาะเมื่อ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;product, QA และ engineering ต้องใช้ requirement ร่วมกัน&lt;/li&gt;
&lt;li&gt;มี misunderstanding ระหว่างสิ่งที่ขอและสิ่งที่สร้างบ่อย&lt;/li&gt;
&lt;li&gt;scenario ต้องอ่านได้โดยคนที่ไม่ใช่วิศวกร&lt;/li&gt;
&lt;li&gt;acceptance criteria สำคัญมาก&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;อ่านตัวอย่างกับ API ได้ที่ &lt;a href="http://apidog.com/blog/gherkin-guide-bdd-api-testing?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;คู่มือ Gherkin สำหรับการทดสอบ API แบบ BDD&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ข้อดี
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ช่วยสร้าง shared understanding&lt;/li&gt;
&lt;li&gt;scenario อ่านง่าย&lt;/li&gt;
&lt;li&gt;เชื่อม requirement กับ automation ได้ดี&lt;/li&gt;
&lt;li&gt;เหมาะกับ acceptance test&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ข้อเสีย
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;มีสองชั้นที่ต้องดูแล: Gherkin และ step definition&lt;/li&gt;
&lt;li&gt;ถ้า product หรือ QA ไม่อ่าน scenario จริง BDD จะไม่คุ้ม&lt;/li&gt;
&lt;li&gt;step definition ซ้ำหรือคลุมเครือทำให้ maintain ยาก&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แนวทางปฏิบัติ: ใช้ BDD เฉพาะ workflow ที่ต้องการ alignment ระหว่างหลายบทบาท ไม่จำเป็นต้องเขียนทุก unit test หรือทุก API test เป็น Gherkin&lt;/p&gt;

&lt;h2&gt;
  
  
  การเปรียบเทียบประเภทของเฟรมเวิร์ก
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ประเภทเฟรมเวิร์ก&lt;/th&gt;
&lt;th&gt;การนำกลับมาใช้ซ้ำ&lt;/th&gt;
&lt;th&gt;ผู้ที่ไม่ใช่โปรแกรมเมอร์สร้างได้&lt;/th&gt;
&lt;th&gt;ค่าใช้จ่ายในการติดตั้ง&lt;/th&gt;
&lt;th&gt;การปรับขนาด&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Linear&lt;/td&gt;
&lt;td&gt;ต่ำมาก&lt;/td&gt;
&lt;td&gt;ได้ ผ่าน record/playback&lt;/td&gt;
&lt;td&gt;ต่ำมาก&lt;/td&gt;
&lt;td&gt;ไม่ดี&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modular&lt;/td&gt;
&lt;td&gt;สูง&lt;/td&gt;
&lt;td&gt;โดยทั่วไปไม่ได้&lt;/td&gt;
&lt;td&gt;ปานกลาง&lt;/td&gt;
&lt;td&gt;ดี&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data-driven&lt;/td&gt;
&lt;td&gt;ปานกลาง&lt;/td&gt;
&lt;td&gt;บางส่วน ผ่าน test data&lt;/td&gt;
&lt;td&gt;ปานกลาง&lt;/td&gt;
&lt;td&gt;ดีสำหรับ input หลากหลาย&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keyword-driven&lt;/td&gt;
&lt;td&gt;สูง&lt;/td&gt;
&lt;td&gt;ได้&lt;/td&gt;
&lt;td&gt;สูง&lt;/td&gt;
&lt;td&gt;ดี&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid&lt;/td&gt;
&lt;td&gt;สูง&lt;/td&gt;
&lt;td&gt;แล้วแต่การออกแบบ&lt;/td&gt;
&lt;td&gt;สูง&lt;/td&gt;
&lt;td&gt;ดีมาก&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BDD&lt;/td&gt;
&lt;td&gt;สูง&lt;/td&gt;
&lt;td&gt;ได้ สำหรับอ่านและเขียน scenario&lt;/td&gt;
&lt;td&gt;สูง&lt;/td&gt;
&lt;td&gt;ดี&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;รูปแบบที่เห็นได้ชัดคือ ยิ่งเริ่มง่าย ต้นทุนระยะยาวมักยิ่งสูง และยิ่งต้องการให้คนที่ไม่ใช่วิศวกรมีส่วนร่วม ก็ยิ่งต้องลงทุนด้านโครงสร้างเบื้องหลังมากขึ้น ไม่มีตัวเลือกที่ฟรี มีแต่ตัวเลือกที่เหมาะกับข้อจำกัดของทีมคุณ&lt;/p&gt;

&lt;h2&gt;
  
  
  ข้อผิดพลาดทั่วไปของเฟรมเวิร์ก
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1. สร้าง abstraction เร็วเกินไป
&lt;/h2&gt;

&lt;p&gt;บางทีมเริ่มด้วย keyword-driven หรือ hybrid ตั้งแต่มีเทสต์แค่ 10–15 รายการ ทำให้ต้นทุนการสร้าง abstraction สูงกว่า benefit ที่ได้&lt;/p&gt;

&lt;p&gt;แนวทางแก้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เริ่มจาก modular แบบเบา ๆ&lt;/li&gt;
&lt;li&gt;รอให้มี duplication จริงก่อนดึงเป็น reusable layer&lt;/li&gt;
&lt;li&gt;ใช้กฎง่าย ๆ: ถ้าขั้นตอนซ้ำ 3 ครั้งขึ้นไป ค่อยพิจารณาแยก helper&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. ไม่ยอมพัฒนาออกจาก linear
&lt;/h2&gt;

&lt;p&gt;Linear script ที่เคยดีตอนมี 20 รายการจะกลายเป็นภาระเมื่อโตเป็น 400 รายการ การเปลี่ยน product เล็กน้อยอาจทำให้ต้องแก้ script ซ้ำ ๆ ทั้งวัน&lt;/p&gt;

&lt;p&gt;สัญญาณที่ควร refactor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;แก้ product หนึ่งจุด แต่เทสต์พังหลายสิบไฟล์&lt;/li&gt;
&lt;li&gt;login, setup, teardown ถูก copy-paste ทุกที่&lt;/li&gt;
&lt;li&gt;failure debug ยากเพราะแต่ละ script ทำคล้ายกันแต่ไม่เหมือนกัน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แนวทางแก้คือย้ายขั้นตอนซ้ำไป helper/module ก่อนที่ suite จะใหญ่เกินไป&lt;/p&gt;

&lt;h2&gt;
  
  
  3. เลือก framework เพื่อ audience ที่ไม่มีอยู่จริง
&lt;/h2&gt;

&lt;p&gt;BDD จะคุ้มเมื่อ product หรือ QA อ่าน Gherkin จริง Keyword-driven จะคุ้มเมื่อ analyst สร้าง test case จริง ถ้าคนเหล่านั้นไม่แตะ test suite เลย คุณจะได้แต่ต้นทุนของ layer เพิ่มเติมโดยไม่มีประโยชน์&lt;/p&gt;

&lt;p&gt;ก่อนเลือก ให้ถามตรง ๆ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ใครจะเขียนเทสต์จริง&lt;/li&gt;
&lt;li&gt;ใครจะอ่านผลลัพธ์จริง&lt;/li&gt;
&lt;li&gt;ใครจะ maintain เมื่อ failure&lt;/li&gt;
&lt;li&gt;ใครจะเพิ่ม test data หรือ scenario&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;เลือก framework ให้ตรงกับคนที่มีส่วนร่วมจริง ไม่ใช่คนที่ “อาจจะ” มีส่วนร่วมในอนาคต&lt;/p&gt;

&lt;h2&gt;
  
  
  วิธีเลือกประเภทเฟรมเวิร์ก
&lt;/h2&gt;

&lt;p&gt;ใช้ checklist นี้ก่อนเริ่มออกแบบ:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ขนาดและอายุของโปรเจกต์&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
ถ้าเป็น script ใช้ครั้งเดียว Linear พอได้ แต่ถ้าต้องดูแลนานกว่าหนึ่งไตรมาส ควรเริ่มอย่างน้อยที่ Modular&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ใครเป็นคนเขียนเทสต์&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
ถ้าเป็นวิศวกรทั้งหมด Modular หรือ Hybrid มักเหมาะกว่า ถ้ามี QA analyst หรือ business user ร่วมเขียน ให้พิจารณา Keyword-driven หรือ BDD&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;input หลากหลายแค่ไหน&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
ถ้า workflow เดิมต้องรันกับข้อมูลหลายสิบหรือหลายร้อยชุด ให้ใช้ Data-driven&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;มีช่องว่างการสื่อสารหรือไม่&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
ถ้า product และ engineering เข้าใจ requirement ไม่ตรงกันบ่อย BDD อาจช่วยได้ เพราะ scenario กลายเป็นภาษากลาง&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;ทีม maintain อะไรได้จริง&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Framework ที่ดีที่สุดคือ framework ที่ทีมเข้าใจและดูแลได้ โครงสร้างสวยแต่ไม่มีใคร debug ได้จะล้มเหลวเร็วกว่าโครงสร้างธรรมดาที่ทุกคนเข้าใจ&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ทีมจำนวนมากมักจบที่ Hybrid โดยมี Modular เป็นแกน ใช้ Data-driven กับกรณี input เยอะ และใช้ BDD เฉพาะจุดที่ต้องการความเข้าใจร่วมกันสูง เริ่มเรียบง่าย แล้วเพิ่มโครงสร้างจาก pain point จริง ไม่ใช่จากทฤษฎี&lt;/p&gt;

&lt;p&gt;ถ้าต้องการวางขอบเขตการทดสอบให้ชัดขึ้น อ่านเพิ่มเรื่อง &lt;a href="http://apidog.com/blog/test-scenario-vs-test-case?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;สถานการณ์ทดสอบเทียบกับกรณีทดสอบ&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  แพลตฟอร์มช่วยได้อย่างไร
&lt;/h2&gt;

&lt;p&gt;การเลือกประเภทเฟรมเวิร์กคือการตัดสินใจด้านสถาปัตยกรรม แต่การทำให้ใช้งานได้จริงต้องมีเครื่องมือที่ช่วยให้ทีมทำตามโครงสร้างได้สม่ำเสมอ โดยเฉพาะ API testing&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; ช่วยรองรับแนวทางเหล่านี้ เช่น การนำ request ที่ใช้ร่วมกันกลับมาใช้ซ้ำ การใส่ parameter การรันแบบ data-driven จาก CSV และ JSON รวมถึงตัวสร้างการทดสอบแบบ visual ที่ช่วยให้ผู้ที่ไม่ใช่โปรแกรมเมอร์ประกอบ test flow ได้ง่ายขึ้น&lt;/p&gt;

&lt;p&gt;สิ่งนี้สำคัญเพราะ framework จะดีได้ก็ต่อเมื่อทีมใช้มันได้อย่างสม่ำเสมอ เมื่อ suite โตขึ้นหรือมีคนเข้าออก เครื่องมือที่มีโครงสร้างในตัวจะช่วยลดความแตกต่างของ style และลดงานที่ต้องเขียน framework เองซ้ำ ๆ&lt;/p&gt;

&lt;p&gt;คุณสามารถ &lt;a href="https://apidog.com/download?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ดาวน์โหลด Apidog&lt;/a&gt; เพื่อดูว่ารูปแบบ modular, data-driven และ visual test building ทำงานจริงอย่างไร จากนั้นค่อยตัดสินใจว่าทีมยังต้องเขียน custom framework เพิ่มมากน้อยแค่ไหน ในหลายกรณี &lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; จัดการ layer อย่าง request, data และ report ให้แล้ว ทำให้ทีมโฟกัสกับ test coverage และ quality ได้มากขึ้น&lt;/p&gt;

&lt;h2&gt;
  
  
  คำถามที่พบบ่อย
&lt;/h2&gt;

&lt;h2&gt;
  
  
  เครื่องมืออัตโนมัติกับเฟรมเวิร์กการทดสอบอัตโนมัติต่างกันอย่างไร
&lt;/h2&gt;

&lt;p&gt;เครื่องมืออัตโนมัติคือสิ่งที่ใช้รันการทดสอบ เช่น browser driver หรือ HTTP client ส่วนเฟรมเวิร์กคือโครงสร้างรอบเครื่องมือนั้น เช่น วิธีจัดไฟล์ วิธีแชร์ logic วิธีจัดการข้อมูล และวิธีรายงานผล คุณมีเครื่องมือได้โดยไม่มีเฟรมเวิร์ก แต่เมื่อเทสต์โตขึ้นจะ maintain ยากมาก&lt;/p&gt;

&lt;h2&gt;
  
  
  เฟรมเวิร์กการทดสอบอัตโนมัติประเภทใดดีที่สุด
&lt;/h2&gt;

&lt;p&gt;ไม่มีประเภทที่ดีที่สุดสำหรับทุกกรณี Linear เหมาะกับ script ใช้ครั้งเดียว Modular เหมาะกับทีมวิศวกรส่วนใหญ่ Data-driven เหมาะกับ input หลากหลาย Keyword-driven และ BDD เหมาะกับทีมที่มีหลายบทบาท ส่วน Hybrid เหมาะกับ suite ขนาดใหญ่ที่โตแล้ว&lt;/p&gt;

&lt;h2&gt;
  
  
  BDD ใช้ได้เฉพาะ API หรือ UI testing หรือไม่
&lt;/h2&gt;

&lt;p&gt;ไม่ใช่ BDD เป็นรูปแบบการจัดโครงสร้าง scenario ไม่ใช่ test layer รูปแบบ Given-When-Then ใช้ได้กับ unit, API และ UI test สิ่งสำคัญคือมีคนที่ไม่ใช่วิศวกรอ่านหรือเขียน scenario จริงหรือไม่&lt;/p&gt;

&lt;h2&gt;
  
  
  เปลี่ยนประเภทเฟรมเวิร์กหลังสร้างชุดทดสอบแล้วได้ไหม
&lt;/h2&gt;

&lt;p&gt;ได้ แต่มีต้นทุนตามขนาดของ test suite การย้ายจาก Linear ไป Modular หมายถึงต้องดึง logic ซ้ำออกจาก script ที่ copy-paste ไว้จำนวนมาก ยิ่งเริ่ม refactor เร็ว ต้นทุนยิ่งต่ำ&lt;/p&gt;

&lt;h2&gt;
  
  
  โปรเจกต์ขนาดเล็กจำเป็นต้องมีเฟรมเวิร์กไหม
&lt;/h2&gt;

&lt;p&gt;ถ้าเป็นโปรเจกต์สั้นมากและใช้ script เพียงไม่กี่ครั้ง Linear อาจพอได้ แต่โปรเจกต์ขนาดเล็กมักโตขึ้นเสมอ ถ้า test suite จะอยู่เกินสองสามสัปดาห์ หรือมีมากกว่าหนึ่งคนดูแล โครงสร้างแบบ Modular ที่เรียบง่ายมักคุ้มค่าตั้งแต่แรก&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Test Scenario เทียบกับ Test Case: อธิบายความแตกต่างที่สำคัญ</title>
      <dc:creator>Thanawat Wongchai</dc:creator>
      <pubDate>Fri, 22 May 2026 07:27:50 +0000</pubDate>
      <link>https://forem.com/thanawat_wonchai/test-scenario-ethiiybkab-test-case-thibaaykhwaamaetktaangthiisamkhay-1h</link>
      <guid>https://forem.com/thanawat_wonchai/test-scenario-ethiiybkab-test-case-thibaaykhwaamaetktaangthiisamkhay-1h</guid>
      <description>&lt;p&gt;“สถานการณ์ทดสอบ” (Test scenario) และ “กรณีทดสอบ” (Test case) ไม่ใช่สิ่งเดียวกัน แม้มักถูกใช้แทนกันในการวางแผน QA ก็ตาม สถานการณ์ทดสอบบอกว่า “ต้องทดสอบอะไร” ส่วนกรณีทดสอบบอกว่า “ต้องทดสอบอย่างไร” ด้วยข้อมูล ขั้นตอน และผลลัพธ์ที่คาดหวังที่ชัดเจน หากแยกสองเลเยอร์นี้ออกจากกัน แผนทดสอบจะตรวจสอบความครอบคลุมได้ง่ายขึ้น และนำไปทำ automation ได้จริง&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com/?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation" class="crayons-btn crayons-btn--primary"&gt;ลองใช้ Apidog วันนี้&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;คู่มือนี้อธิบายความแตกต่างระหว่างสถานการณ์ทดสอบและกรณีทดสอบ พร้อมวิธีนำไปใช้ในเวิร์กโฟลว์การทดสอบ API ด้วย &lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  สถานการณ์ทดสอบคืออะไร?
&lt;/h2&gt;

&lt;p&gt;สถานการณ์ทดสอบคือข้อความระดับสูงที่ระบุพฤติกรรม เงื่อนไข หรือความสามารถที่ควรถูกตรวจสอบ โดยไม่ลงรายละเอียดระดับ endpoint, payload, test data หรือ assertion&lt;/p&gt;

&lt;p&gt;ให้คิดว่าสถานการณ์คือ “หัวข้อความครอบคลุม” ตัวอย่างเช่น ระบบชำระเงินของอีคอมเมิร์ซอาจมีสถานการณ์ดังนี้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ตรวจสอบการชำระเงินสำหรับผู้ใช้ที่ลงทะเบียนและมีบัตรที่บันทึกไว้&lt;/li&gt;
&lt;li&gt;ตรวจสอบการชำระเงินสำหรับผู้ใช้ทั่วไป&lt;/li&gt;
&lt;li&gt;ตรวจสอบการชำระเงินเมื่อสินค้าหมดสต็อกระหว่างการซื้อ&lt;/li&gt;
&lt;li&gt;ตรวจสอบการชำระเงินเมื่อการชำระเงินถูกปฏิเสธ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;แต่ละบรรทัดตอบคำถามว่า:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ต้องตรวจสอบพฤติกรรมใด&lt;/li&gt;
&lt;li&gt;พฤติกรรมนั้นสำคัญต่อธุรกิจอย่างไร&lt;/li&gt;
&lt;li&gt;มีพื้นที่ใดบ้างที่ต้องครอบคลุมก่อน release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;สถานการณ์ไม่ควรระบุรายละเอียดเช่น &lt;code&gt;POST /orders&lt;/code&gt;, payload หรือ status code เพราะหน้าที่ของมันคือกำหนด “ขอบเขต” ไม่ใช่ “ขั้นตอนการรัน”&lt;/p&gt;

&lt;p&gt;ใช้สถานการณ์เพื่อตรวจว่า:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;requirement สำคัญถูกครอบคลุมครบหรือไม่&lt;/li&gt;
&lt;li&gt;happy path และ unhappy path ถูกพิจารณาหรือยัง&lt;/li&gt;
&lt;li&gt;ทีม product, QA และ engineering เข้าใจตรงกันหรือไม่&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ถ้าสถานการณ์หายไป ต่อให้มีกรณีทดสอบละเอียดแค่ไหน ก็ยังอาจพลาดพฤติกรรมสำคัญของระบบได้&lt;/p&gt;

&lt;h2&gt;
  
  
  กรณีทดสอบคืออะไร?
&lt;/h2&gt;

&lt;p&gt;กรณีทดสอบคือการตรวจสอบที่ระบุรายละเอียดครบพอให้คนหรือเครื่องมือ automation สามารถรันซ้ำแล้วได้ผลลัพธ์เดียวกัน โดยทั่วไปควรมี:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เงื่อนไขเบื้องต้น&lt;/li&gt;
&lt;li&gt;endpoint หรือ action ที่ต้องเรียก&lt;/li&gt;
&lt;li&gt;input หรือ payload ที่ใช้&lt;/li&gt;
&lt;li&gt;expected status code&lt;/li&gt;
&lt;li&gt;expected response body&lt;/li&gt;
&lt;li&gt;assertion หรือเงื่อนไขผ่าน/ไม่ผ่าน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่างจากสถานการณ์ “ตรวจสอบการชำระเงินสำหรับผู้ใช้ทั่วไป” สามารถแตกเป็นกรณีทดสอบได้ดังนี้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /orders&lt;/code&gt; พร้อม payload ผู้ใช้ทั่วไปที่ถูกต้อง ต้องได้ &lt;code&gt;201&lt;/code&gt; และมี &lt;code&gt;order_id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /orders&lt;/code&gt; โดยไม่มีที่อยู่จัดส่ง ต้องได้ &lt;code&gt;400&lt;/code&gt; และ &lt;code&gt;validation_error&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /orders&lt;/code&gt; พร้อม SKU ที่หมดสต็อก ต้องได้ &lt;code&gt;409&lt;/code&gt; และ &lt;code&gt;error: out_of_stock&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่างกรณีทดสอบแบบชัดเจน:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ชื่อกรณี: สร้างคำสั่งซื้อสำหรับผู้ใช้ทั่วไปสำเร็จ

Precondition:
- มีสินค้า SKU-001 ในสต็อก
- ผู้ใช้ยังไม่ได้ login

Request:
POST /orders

Payload:
{
  "items": [
    {
      "sku": "SKU-001",
      "quantity": 1
    }
  ],
  "shipping_address": {
    "name": "Guest User",
    "city": "Bangkok",
    "postal_code": "10110"
  },
  "payment_method": "card"
}

Expected:
- HTTP status เป็น 201
- response มี order_id
- response.status เป็น created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;กรณีทดสอบต้องเฉพาะเจาะจงพอสำหรับ automation หากต้องการโครงสร้างแบบฟิลด์ต่อฟิลด์ อ่านเพิ่มได้ที่ &lt;a href="http://apidog.com/blog/api-test-case-example?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;วิธีเขียนกรณีทดสอบ API&lt;/a&gt; และถ้าต้องการแยกความต่างระหว่าง test case กับ executable code ดู &lt;a href="http://apidog.com/blog/test-case-vs-test-script?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;กรณีทดสอบเทียบกับสคริปต์ทดสอบ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;จุดสำคัญคือความแม่นยำ ประโยคอย่าง “การชำระเงินใช้งานได้” ยังเป็นแค่สถานการณ์ แต่ “ส่ง &lt;code&gt;POST /orders&lt;/code&gt; ด้วย payload ที่ถูกต้อง แล้วคาดหวัง &lt;code&gt;201&lt;/code&gt; พร้อม &lt;code&gt;order_id&lt;/code&gt; ที่ไม่ว่างเปล่า” คือกรณีทดสอบ&lt;/p&gt;

&lt;h2&gt;
  
  
  ความแตกต่างที่สำคัญ
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;มิติ&lt;/th&gt;
&lt;th&gt;สถานการณ์ทดสอบ&lt;/th&gt;
&lt;th&gt;กรณีทดสอบ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ระดับ&lt;/td&gt;
&lt;td&gt;ระดับสูง ระบุสิ่งที่ต้องทดสอบ&lt;/td&gt;
&lt;td&gt;ระดับต่ำ ระบุวิธีทดสอบ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;รายละเอียด&lt;/td&gt;
&lt;td&gt;สั้น กระชับ มักเป็นหนึ่งบรรทัด&lt;/td&gt;
&lt;td&gt;มีขั้นตอน ข้อมูล และ expected result&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;จุดเน้น&lt;/td&gt;
&lt;td&gt;เป้าหมายทางธุรกิจหรือฟังก์ชัน&lt;/td&gt;
&lt;td&gt;การตรวจสอบทางเทคนิค&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ข้อมูลนำเข้า&lt;/td&gt;
&lt;td&gt;ไม่ระบุรายละเอียด&lt;/td&gt;
&lt;td&gt;ระบุ payload, parameter หรือ test data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ผลลัพธ์ที่คาดหวัง&lt;/td&gt;
&lt;td&gt;มักเป็นนัย&lt;/td&gt;
&lt;td&gt;ระบุชัดเจน เช่น status, body, schema, response time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ผู้อ่านหลัก&lt;/td&gt;
&lt;td&gt;Product, QA, Engineering&lt;/td&gt;
&lt;td&gt;QA, Developer, Automation tool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;จำนวน&lt;/td&gt;
&lt;td&gt;ไม่กี่รายการต่อ feature&lt;/td&gt;
&lt;td&gt;หลายรายการต่อสถานการณ์&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ช่วงเวลาที่สร้าง&lt;/td&gt;
&lt;td&gt;ระหว่าง test planning&lt;/td&gt;
&lt;td&gt;หลังจากตกลงสถานการณ์แล้ว&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ความสัมพันธ์เป็นแบบลำดับชั้น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;สถานการณ์ทดสอบ
└── กรณีทดสอบ 1
└── กรณีทดสอบ 2
└── กรณีทดสอบ 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หนึ่งสถานการณ์ควรแตกออกเป็นหลายกรณี สถานการณ์ควบคุม “ความกว้าง” ของ coverage ส่วนกรณีควบคุม “ความลึก” และความเข้มงวดของการตรวจสอบ&lt;/p&gt;

&lt;p&gt;ข้อผิดพลาดที่พบบ่อยคือเขียน test case จำนวนมากโดยไม่มี scenario map ทำให้ตอบไม่ได้ว่า feature ถูกครอบคลุมครบหรือแค่ถูกทดสอบหนักเฉพาะบางมุม&lt;/p&gt;

&lt;p&gt;อีกวิธีคิดคือ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;สถานการณ์ตอบว่า “ครอบคลุมแล้วหรือยัง”&lt;/li&gt;
&lt;li&gt;กรณีทดสอบตอบว่า “ผ่านหรือไม่ผ่าน”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;คุณต้องมีทั้งสองระดับเพื่อจัดการคุณภาพได้จริง&lt;/p&gt;

&lt;h2&gt;
  
  
  วิธีใช้สถานการณ์และกรณีร่วมกัน
&lt;/h2&gt;

&lt;p&gt;เวิร์กโฟลว์ที่นำไปใช้ได้จริงควรเริ่มจากกว้างไปหาเฉพาะเจาะจง&lt;/p&gt;

&lt;h3&gt;
  
  
  1. ระบุสถานการณ์จาก requirement
&lt;/h3&gt;

&lt;p&gt;อ่าน requirement, user story หรือ API documentation แล้วลิสต์พฤติกรรมที่ต้องตรวจสอบ รวมถึง:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;happy path&lt;/li&gt;
&lt;li&gt;validation error&lt;/li&gt;
&lt;li&gt;permission หรือ authentication error&lt;/li&gt;
&lt;li&gt;business rule&lt;/li&gt;
&lt;li&gt;boundary condition&lt;/li&gt;
&lt;li&gt;integration failure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่าง:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Feature: Notes API

สถานการณ์:
- ผู้ใช้สามารถสร้างบันทึกได้
- ผู้ใช้สามารถแก้ไขบันทึกของตัวเองได้
- ผู้ใช้ไม่สามารถแก้ไขบันทึกของผู้อื่นได้
- ผู้ใช้สามารถลบบันทึกได้
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. กำหนด objective ของแต่ละสถานการณ์
&lt;/h3&gt;

&lt;p&gt;เขียนให้ชัดว่า “สำเร็จ” หมายถึงอะไร เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;สถานการณ์: ผู้ใช้สามารถสร้างบันทึกได้

Objective:
- ผู้ใช้ที่ authenticated สามารถสร้างบันทึกใหม่ได้
- ระบบต้อง reject request ที่ข้อมูลไม่ครบ
- ระบบต้อง reject request ที่ไม่มี token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ขั้นตอนนี้ช่วยให้ทีมตกลง scope ก่อนลงรายละเอียด test case&lt;/p&gt;

&lt;h3&gt;
  
  
  3. แตกสถานการณ์เป็นกรณีทดสอบ
&lt;/h3&gt;

&lt;p&gt;สำหรับแต่ละสถานการณ์ ให้เขียน test case ที่มี input และ expected result ชัดเจน&lt;/p&gt;

&lt;p&gt;ตัวอย่าง checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;มี happy path อย่างน้อยหนึ่งกรณีหรือไม่&lt;/li&gt;
&lt;li&gt;มี negative case สำหรับ validation หรือไม่&lt;/li&gt;
&lt;li&gt;มี authentication/authorization case หรือไม่&lt;/li&gt;
&lt;li&gt;มี boundary case หรือไม่&lt;/li&gt;
&lt;li&gt;expected status code ตรงกับเอกสาร API หรือไม่&lt;/li&gt;
&lt;li&gt;response body มี assertion ที่ตรวจ business outcome หรือไม่&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. ตรวจความครบถ้วนของ coverage
&lt;/h3&gt;

&lt;p&gt;ก่อนเริ่ม automation ให้ย้อนกลับมาตรวจ mapping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Scenario: ผู้ใช้สามารถสร้างบันทึกได้
- TC-001 สร้างบันทึกสำเร็จ
- TC-002 title หายไป
- TC-003 ไม่มี token
- TC-004 payload ใหญ่เกินกำหนด
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ถ้าสถานการณ์ใดไม่มี test case แสดงว่ายังไม่มีการตรวจสอบจริง&lt;br&gt;&lt;br&gt;
ถ้า test case ใดไม่ผูกกับสถานการณ์ อาจเป็นการทดสอบที่ไม่มีบริบท coverage&lt;/p&gt;
&lt;h3&gt;
  
  
  5. รันและสรุปผลทั้งสองระดับ
&lt;/h3&gt;

&lt;p&gt;เมื่อรัน test case ให้บันทึกผลระดับกรณี เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TC-001: Passed
TC-002: Passed
TC-003: Failed
TC-004: Passed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จากนั้นสรุปกลับไปที่ระดับสถานการณ์:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Scenario: ผู้ใช้สามารถสร้างบันทึกได้
Status: At risk
Reason: TC-003 failed - unauthenticated request ไม่ถูก reject ตามที่คาดหวัง
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;รายงานแบบนี้มีประโยชน์กว่าแค่จำนวน test ที่ผ่าน เพราะ stakeholder เห็นได้ทันทีว่า feature ใดมีความเสี่ยง&lt;/p&gt;

&lt;p&gt;สำหรับทีมที่ใช้ BDD สถานการณ์สามารถ map กับ Given-When-Then ได้โดยตรง อ่านแนวทางเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/gherkin-guide-bdd-api-testing?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;คู่มือ Gherkin สำหรับการทดสอบ API แบบ BDD&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ตัวอย่างการทำงาน: จากสถานการณ์ไปสู่กรณี
&lt;/h2&gt;

&lt;p&gt;สมมติว่าคุณมี Notes API และต้องการทดสอบพฤติกรรมนี้:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;สถานการณ์: ผู้ใช้สามารถสร้างบันทึกได้
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สถานการณ์นี้บอก “สิ่งที่ต้องตรวจสอบ” แต่ยังไม่พอสำหรับการรันจริง จึงต้องแตกเป็นกรณีทดสอบ&lt;/p&gt;

&lt;h3&gt;
  
  
  กรณีที่ 1: สร้างบันทึกสำเร็จ
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /notes
Authorization: Bearer &amp;lt;valid_token&amp;gt;
Content-Type: application/json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Groceries"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"milk, eggs"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;status code เป็น &lt;code&gt;201&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;response มี &lt;code&gt;id&lt;/code&gt; ที่ไม่ว่าง&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;title&lt;/code&gt; เท่ากับ &lt;code&gt;Groceries&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;มี &lt;code&gt;created_at&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;response time น้อยกว่า 600 ms&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  กรณีที่ 2: ขาดฟิลด์ที่จำเป็น
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /notes
Authorization: Bearer &amp;lt;valid_token&amp;gt;
Content-Type: application/json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"milk, eggs"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;status code เป็น &lt;code&gt;400&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;error&lt;/code&gt; เท่ากับ &lt;code&gt;validation_error&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;details&lt;/code&gt; ระบุว่า field &lt;code&gt;title&lt;/code&gt; หายไป&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  กรณีที่ 3: ไม่ได้ยืนยันตัวตน
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /notes
Content-Type: application/json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Groceries"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"milk, eggs"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;status code เป็น &lt;code&gt;401&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;response ไม่มี &lt;code&gt;id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ระบบไม่สร้าง note ใหม่&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  กรณีที่ 4: payload ใหญ่เกินไป
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /notes
Authorization: Bearer &amp;lt;valid_token&amp;gt;
Content-Type: application/json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Large note"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;2MB text&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;status code เป็น &lt;code&gt;413&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;response มีข้อความ error ที่ชัดเจน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;จากตัวอย่างนี้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;หนึ่งสถานการณ์อธิบาย “อะไร”&lt;/li&gt;
&lt;li&gt;สี่กรณีอธิบาย “ทดสอบอย่างไร”&lt;/li&gt;
&lt;li&gt;แต่ละกรณีสามารถนำไปสร้าง automated API test ได้&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ถ้าในอนาคตเพิ่มฟีเจอร์ “ผู้ใช้สามารถแนบไฟล์ไปยังบันทึกได้” นั่นควรเป็นสถานการณ์ใหม่ และแตกเป็น test case ชุดใหม่ ไม่ควรยัดรวมเข้าไปในสถานการณ์เดิมจน scope ไม่ชัดเจน&lt;/p&gt;

&lt;h2&gt;
  
  
  การสร้างสถานการณ์และกรณีใน Apidog
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; รองรับการจัดโครงสร้างการทดสอบ API ตามลำดับชั้นนี้ เพื่อให้แผนทดสอบในเอกสารและการทดสอบในเครื่องมือสอดคล้องกัน&lt;/p&gt;

&lt;p&gt;ใน Apidog คุณสามารถสร้างสถานการณ์ทดสอบเป็นลำดับของ API requests ที่เกี่ยวข้องกัน เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Login
2. Create note
3. Get note detail
4. Assert created note
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แต่ละ request สามารถมี assertion ของตัวเอง เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;status code&lt;/li&gt;
&lt;li&gt;response body field&lt;/li&gt;
&lt;li&gt;schema validation&lt;/li&gt;
&lt;li&gt;response time&lt;/li&gt;
&lt;li&gt;value ที่ส่งต่อจาก request ก่อนหน้า&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่าง flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /login
└── extract token

POST /notes
└── use token
└── assert status = 201
└── extract note_id

GET /notes/{note_id}
└── assert title = Groceries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แต่ละ request พร้อม assertion ทำหน้าที่เหมือนกรณีทดสอบที่รันได้จริง ส่วนชุดของ request ที่ประกอบกันเป็น behavior หนึ่งชุดทำหน้าที่เป็นสถานการณ์&lt;/p&gt;

&lt;p&gt;Apidog ยังรองรับ data-driven testing เพื่อรันกรณีเดียวกับข้อมูลหลายชุดจาก CSV หรือ JSON เช่น ใช้ทดสอบ validation error หลายแบบโดยไม่ต้องสร้าง test case ซ้ำจำนวนมาก&lt;/p&gt;

&lt;p&gt;จากนั้นสามารถจัดกลุ่มสถานการณ์เป็น &lt;a href="http://apidog.com/blog/test-suites-api-test-automation?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ชุดทดสอบ&lt;/a&gt; เพื่อรันซ้ำได้ เช่น:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;รันในเครื่องระหว่างพัฒนา&lt;/li&gt;
&lt;li&gt;รันตาม schedule&lt;/li&gt;
&lt;li&gt;รันใน CI pipeline&lt;/li&gt;
&lt;li&gt;สร้างรายงานผลระดับกรณีและระดับสถานการณ์&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;มุมมองสองระดับนี้ช่วยให้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;วิศวกรเห็นว่า request หรือ assertion ใดล้มเหลว&lt;/li&gt;
&lt;li&gt;QA เห็นว่า test case ใดต้องแก้&lt;/li&gt;
&lt;li&gt;stakeholder เห็นว่าสถานการณ์หรือ feature ใดมีความเสี่ยง&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;คุณสามารถ &lt;a href="https://apidog.com/download?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ดาวน์โหลด Apidog&lt;/a&gt; เพื่อสร้างสถานการณ์แรกและดูการสรุปผลจาก test case ไปยัง scenario ในเวิร์กโฟลว์จริง&lt;/p&gt;

&lt;h2&gt;
  
  
  ทำไมต้องมีทั้งสถานการณ์และกรณีทดสอบ
&lt;/h2&gt;

&lt;p&gt;ถ้ามีเฉพาะกรณีทดสอบ คุณจะได้รายการยาว ๆ เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TC-001
TC-002
TC-003
TC-004
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แต่ตอบยากว่า feature ใดถูกครอบคลุมครบแล้ว หรือกรณีเหล่านั้นผูกกับ requirement ไหน&lt;/p&gt;

&lt;p&gt;ถ้ามีเฉพาะสถานการณ์ คุณจะได้แผนระดับสูง เช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ตรวจสอบการชำระเงิน
ตรวจสอบการสมัครสมาชิก
ตรวจสอบการลืมรหัสผ่าน
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แต่ไม่มีรายละเอียดพอให้รันซ้ำอย่างสม่ำเสมอ เพราะแต่ละคนอาจตีความ “ตรวจสอบ” ต่างกัน&lt;/p&gt;

&lt;p&gt;แนวทางที่ใช้งานได้จริงคือ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Scenario → Test cases → Assertions → Test report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สองเลเยอร์นี้ยังตอบโจทย์ผู้อ่านต่างกลุ่ม:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product manager อ่านสถานการณ์เพื่อยืนยันว่า test coverage ตรงกับ business requirement&lt;/li&gt;
&lt;li&gt;QA อ่านกรณีเพื่อออกแบบข้อมูลและ expected result&lt;/li&gt;
&lt;li&gt;Developer อ่านกรณีเพื่อ debug และเขียน automation&lt;/li&gt;
&lt;li&gt;Manager อ่านรายงานระดับสถานการณ์เพื่อประเมิน release risk&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;หลักปฏิบัติที่ดีคือ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;รักษาสถานการณ์ให้เสถียร เปลี่ยนเมื่อเจตนาของ feature เปลี่ยน&lt;/li&gt;
&lt;li&gt;อัปเดตกรณีทดสอบเมื่อ API contract, schema หรือ business rule เปลี่ยน&lt;/li&gt;
&lt;li&gt;ผูก test case ทุกตัวเข้ากับสถานการณ์เสมอ&lt;/li&gt;
&lt;li&gt;สรุปรายงานจาก test case ขึ้นไปเป็น scenario status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;เมื่อแยก lifecycle ของสองสิ่งนี้ แผนทดสอบจะอ่านง่ายขึ้น บำรุงรักษาได้ดีขึ้น และเหมาะกับ automation มากขึ้น&lt;/p&gt;

&lt;h2&gt;
  
  
  คำถามที่พบบ่อย
&lt;/h2&gt;

&lt;h3&gt;
  
  
  สถานการณ์ทดสอบเหมือนกับชุดทดสอบหรือไม่?
&lt;/h3&gt;

&lt;p&gt;ไม่เหมือนกัน สถานการณ์อธิบายพฤติกรรมที่ต้องทดสอบ ส่วนชุดทดสอบคือกลุ่มของการทดสอบที่จัดไว้เพื่อการรัน ชุดทดสอบอาจมีกรณีจากหลายสถานการณ์ได้ อ่านเพิ่มได้ที่ &lt;a href="http://apidog.com/blog/test-suite-vs-test-case?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ชุดทดสอบเทียบกับกรณีทดสอบ&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  สถานการณ์หนึ่งควรมีกี่กรณีทดสอบ?
&lt;/h3&gt;

&lt;p&gt;ควรมีมากพอที่จะครอบคลุม happy path, negative path และ boundary condition ที่เกี่ยวข้อง สถานการณ์ง่ายอาจมี 3-4 กรณี ส่วนสถานการณ์ซับซ้อนอาจต้องมีมากกว่านั้น&lt;/p&gt;

&lt;h3&gt;
  
  
  ใครควรเขียนสถานการณ์และใครควรเขียนกรณีทดสอบ?
&lt;/h3&gt;

&lt;p&gt;สถานการณ์มักเขียนร่วมกันระหว่าง product และ QA เพราะเกี่ยวกับ intent ของ feature ส่วนกรณีทดสอบมักเขียนโดย QA หรือนักพัฒนา เพราะต้องใช้รายละเอียดทางเทคนิค เช่น endpoint, payload และ assertion ดูแนวทางเพิ่มเติมได้ที่ &lt;a href="http://apidog.com/blog/test-case-specification?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;รูปแบบข้อกำหนดกรณีทดสอบ&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ถ้าการทดสอบเป็น automation อยู่แล้ว ยังต้องมีสถานการณ์หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ยังต้องมี Automation รันกรณีทดสอบ แต่สถานการณ์บอกว่ากรณีเหล่านั้นครอบคลุม requirement ที่ถูกต้องหรือไม่ หากไม่มี scenario map ระบบ automation อาจแค่รันเร็วขึ้น แต่ยังไม่รับประกันว่า coverage ครบถ้วนจริง&lt;/p&gt;

</description>
    </item>
    <item>
      <title>สร้างและทดสอบ HTTP API ด้วย Tapir ใน Scala</title>
      <dc:creator>Thanawat Wongchai</dc:creator>
      <pubDate>Fri, 22 May 2026 07:27:26 +0000</pubDate>
      <link>https://forem.com/thanawat_wonchai/sraangaelathdsb-http-api-dwy-tapir-ain-scala-529c</link>
      <guid>https://forem.com/thanawat_wonchai/sraangaelathdsb-http-api-dwy-tapir-ain-scala-529c</guid>
      <description>&lt;p&gt;Tapir ย่อมาจาก Typed API descRiptions เป็นไลบรารี Scala สำหรับอธิบาย HTTP endpoint เป็นค่าที่มี type ชัดเจน ตั้งแต่ method, path, input, output ไปจนถึง error case จากคำอธิบายเดียวกัน คุณสามารถสร้าง server route, client และเอกสาร OpenAPI ได้ แนวคิดหลักคือเขียน API contract ครั้งเดียว แล้วให้ Tapir ใช้ contract เดียวกันตลอดทั้งระบบ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apidog.com/?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation" class="crayons-btn crayons-btn--primary"&gt;ลองใช้ Apidog วันนี้&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;ต่างจากการเขียน route โดยตรงใน web framework ตรงที่ Tapir แยก “คำอธิบาย endpoint” ออกจาก “business logic” อย่างชัดเจน endpoint เป็นค่า ส่วน logic เป็นฟังก์ชัน จากนั้น server interpreter จะเชื่อมทั้งสองเข้าด้วยกัน บทความนี้จะแสดงวิธีสร้าง Task API ด้วย Scala, ต่อเข้ากับ Pekko HTTP, สร้าง OpenAPI และทดสอบ endpoint แบบไม่ต้องเริ่ม server จริง&lt;/p&gt;

&lt;h2&gt;
  
  
  สิ่งที่ Tapir มอบให้คุณ
&lt;/h2&gt;

&lt;p&gt;Tapir เหมาะเมื่อคุณต้องการให้ API contract เป็นศูนย์กลางของระบบ โดยมีข้อดีหลัก 3 อย่าง&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Type safety&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
input และ output ของ endpoint ถูกกำหนดเป็น type ดังนั้น compiler จะตรวจสอบว่า handler คืนค่าตรงกับ contract หรือไม่ ถ้า endpoint บอกว่าจะคืน &lt;code&gt;Task&lt;/code&gt; แต่ logic คืน type อื่น โค้ดจะไม่ compile&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Single source of truth&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
endpoint เป็นค่าเดียวที่ใช้สร้าง server, client และ OpenAPI spec ได้ จึงลดโอกาสที่เอกสารกับ implementation จะไม่ตรงกัน แนวคิดนี้ใกล้เคียงกับ &lt;a href="http://apidog.com/blog/api-contract-testing?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;การทดสอบสัญญา API&lt;/a&gt; แต่ถูกบังคับด้วย type system&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Framework independence&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
endpoint description ไม่ผูกกับ Akka HTTP, Pekko HTTP, http4s หรือ Netty คุณเลือก server interpreter แยกต่างหากได้ ทำให้ contract อยู่ได้นานกว่าการตัดสินใจเลือก framework&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;สิ่งที่ควรเข้าใจคือ Tapir ไม่ใช่ web framework และไม่ใช่ code generator แบบที่สร้างไฟล์ Scala ให้คุณแก้เอง Tapir เป็น description layer เหนือ framework ที่คุณเลือก โดย endpoint value คือ source of truth และสิ่งอื่น ๆ เช่น route, client, OpenAPI จะถูกคำนวณจาก value นั้น&lt;/p&gt;

&lt;h2&gt;
  
  
  การตั้งค่าโปรเจกต์
&lt;/h2&gt;

&lt;p&gt;เพิ่ม dependencies ใน &lt;code&gt;build.sbt&lt;/code&gt; ตัวอย่างนี้ใช้ Pekko HTTP เป็น server interpreter และ circe สำหรับ JSON&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;tapirVersion&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.11.7"&lt;/span&gt;

&lt;span class="n"&gt;libraryDependencies&lt;/span&gt; &lt;span class="o"&gt;++=&lt;/span&gt; &lt;span class="nc"&gt;Seq&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;"com.softwaremill.sttp.tapir"&lt;/span&gt; &lt;span class="o"&gt;%%&lt;/span&gt; &lt;span class="s"&gt;"tapir-core"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;tapirVersion&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"com.softwaremill.sttp.tapir"&lt;/span&gt; &lt;span class="o"&gt;%%&lt;/span&gt; &lt;span class="s"&gt;"tapir-pekko-http-server"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;tapirVersion&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"com.softwaremill.sttp.tapir"&lt;/span&gt; &lt;span class="o"&gt;%%&lt;/span&gt; &lt;span class="s"&gt;"tapir-json-circe"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;tapirVersion&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"com.softwaremill.sttp.tapir"&lt;/span&gt; &lt;span class="o"&gt;%%&lt;/span&gt; &lt;span class="s"&gt;"tapir-openapi-docs"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;tapirVersion&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"com.softwaremill.sttp.apispec"&lt;/span&gt; &lt;span class="o"&gt;%%&lt;/span&gt; &lt;span class="s"&gt;"openapi-circe-yaml"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="s"&gt;"0.11.3"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตรวจสอบเวอร์ชันล่าสุดและ module อื่น ๆ ได้จาก &lt;a href="https://tapir.softwaremill.com" rel="noopener noreferrer"&gt;tapir.softwaremill.com&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  การกำหนด typed endpoint
&lt;/h2&gt;

&lt;p&gt;เริ่มจาก domain model และ JSON codec โดยใช้ circe auto derivation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.circe.generic.auto._&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sttp.tapir._&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sttp.tapir.json.circe._&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sttp.tapir.generic.auto._&lt;/span&gt;

&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Boolean&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NewTask&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จากนั้นกำหนด endpoint เป็นค่า โดยประกอบจาก combinator ของ Tapir&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;getTask&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PublicEndpoint&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;, &lt;span class="kt"&gt;ApiError&lt;/span&gt;, &lt;span class="kt"&gt;Task&lt;/span&gt;, &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
  &lt;span class="nv"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;get&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;in&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tasks"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;](&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;errorOut&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;statusCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;sttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;StatusCode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;NotFound&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="py"&gt;and&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonBody&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;ApiError&lt;/span&gt;&lt;span class="o"&gt;]))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;out&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonBody&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;listTasks&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PublicEndpoint&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Unit&lt;/span&gt;, &lt;span class="kt"&gt;Unit&lt;/span&gt;, &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;, &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
  &lt;span class="nv"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;get&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;in&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tasks"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;out&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonBody&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;]])&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;createTask&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PublicEndpoint&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;NewTask&lt;/span&gt;, &lt;span class="kt"&gt;Unit&lt;/span&gt;, &lt;span class="kt"&gt;Task&lt;/span&gt;, &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
  &lt;span class="nv"&gt;endpoint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;post&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;in&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tasks"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;in&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonBody&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;NewTask&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;out&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;statusCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;sttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;StatusCode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Created&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="py"&gt;and&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonBody&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;อ่าน &lt;code&gt;getTask&lt;/code&gt; ได้แบบนี้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;method: &lt;code&gt;GET&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;path: &lt;code&gt;/tasks/{id}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;input: path parameter ชื่อ &lt;code&gt;id&lt;/code&gt; เป็น &lt;code&gt;Int&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;error output: HTTP &lt;code&gt;404&lt;/code&gt; พร้อม JSON body เป็น &lt;code&gt;ApiError&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;success output: HTTP &lt;code&gt;200&lt;/code&gt; พร้อม JSON body เป็น &lt;code&gt;Task&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;PublicEndpoint[I, E, O, R]&lt;/code&gt; มี type parameter 4 ตัว:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;I&lt;/code&gt;: input&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;E&lt;/code&gt;: error output&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;O&lt;/code&gt;: success output&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;R&lt;/code&gt;: required capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่างเช่น:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="nc"&gt;PublicEndpoint&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;, &lt;span class="kt"&gt;ApiError&lt;/span&gt;, &lt;span class="kt"&gt;Task&lt;/span&gt;, &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หมายความว่า endpoint นี้รับ &lt;code&gt;Int&lt;/code&gt;, อาจ fail ด้วย &lt;code&gt;ApiError&lt;/code&gt;, และ success ด้วย &lt;code&gt;Task&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;combinator ที่ใช้บ่อยคือ:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;in(...)&lt;/code&gt; เพิ่ม input เช่น path, query, header หรือ body&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;out(...)&lt;/code&gt; กำหนด success response&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;errorOut(...)&lt;/code&gt; กำหนด error response&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;oneOf(...)&lt;/code&gt; ใช้จำลอง error หรือ response หลายรูปแบบ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;จุดสำคัญคือ endpoint เหล่านี้ยังไม่รันอะไรเลย มันเป็นเพียง contract ที่ compiler เข้าใจ&lt;/p&gt;

&lt;h2&gt;
  
  
  การเพิ่ม server logic
&lt;/h2&gt;

&lt;p&gt;เมื่อต้องการให้ endpoint ทำงานจริง ให้แนบ logic ด้วย &lt;code&gt;serverLogic&lt;/code&gt; โดย logic ต้องคืนค่าเป็น &lt;code&gt;Either[ErrorType, SuccessType]&lt;/code&gt; ภายใน effect เช่น &lt;code&gt;Future&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;ตัวอย่างนี้ใช้ in-memory store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;scala.concurrent.Future&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;scala.collection.concurrent.TrieMap&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sttp.tapir.server.ServerEndpoint&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;store&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TrieMap&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Int&lt;/span&gt;, &lt;span class="kt"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;](&lt;/span&gt;
  &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Write the Tapir tutorial"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;getTaskServer&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ServerEndpoint&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Any&lt;/span&gt;, &lt;span class="kt"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
  &lt;span class="nv"&gt;getTask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;serverLogic&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nv"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;successful&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="nv"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="py"&gt;toRight&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ApiError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="s"&gt;"No task with id $id"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;listTasksServer&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ServerEndpoint&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Any&lt;/span&gt;, &lt;span class="kt"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
  &lt;span class="nv"&gt;listTasks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;serverLogic&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nv"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;successful&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Right&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;values&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;createTaskServer&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ServerEndpoint&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Any&lt;/span&gt;, &lt;span class="kt"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
  &lt;span class="nv"&gt;createTask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;serverLogic&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;newTask&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;id&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;task&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;newTask&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;title&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

    &lt;span class="nv"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

    &lt;span class="nv"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;successful&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Right&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;compiler จะบังคับให้ logic ตรงกับ endpoint contract เช่น &lt;code&gt;getTask&lt;/code&gt; กำหนด error เป็น &lt;code&gt;ApiError&lt;/code&gt; ดังนั้นฝั่งซ้ายของ &lt;code&gt;Either&lt;/code&gt; ต้องเป็น &lt;code&gt;ApiError&lt;/code&gt; เท่านั้น ถ้าคืน type อื่นจะ compile ไม่ผ่าน&lt;/p&gt;

&lt;h2&gt;
  
  
  รันด้วย Pekko HTTP
&lt;/h2&gt;

&lt;p&gt;แปลง &lt;code&gt;ServerEndpoint&lt;/code&gt; ให้เป็น route จริงด้วย Pekko HTTP interpreter&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.apache.pekko.actor.typed.ActorSystem&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.apache.pekko.actor.typed.scaladsl.Behaviors&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.apache.pekko.http.scaladsl.Http&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sttp.tapir.server.pekkohttp.PekkoHttpServerInterpreter&lt;/span&gt;

&lt;span class="k"&gt;implicit&lt;/span&gt; &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;system&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ActorSystem&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
  &lt;span class="nc"&gt;ActorSystem&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Behaviors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"task-api"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;system.executionContext&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;routes&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt;
  &lt;span class="nc"&gt;PekkoHttpServerInterpreter&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="py"&gt;toRoute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getTaskServer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;listTasksServer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;createTaskServer&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;newServerAt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"localhost"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตอนนี้ API ทำงานที่ &lt;code&gt;localhost:8080&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;ลองเรียกด้วย &lt;code&gt;curl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080/tasks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080/tasks/1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8080/tasks &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"title":"Ship Tapir API"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หากต้องการเปลี่ยนจาก Pekko HTTP ไปเป็น interpreter อื่น คุณเปลี่ยนเฉพาะส่วน interpreter โดยไม่ต้องแก้ endpoint description&lt;/p&gt;

&lt;h2&gt;
  
  
  การสร้างเอกสาร OpenAPI
&lt;/h2&gt;

&lt;p&gt;เพราะ endpoint เป็นค่า OpenAPI spec จึงสร้างจาก endpoint เหล่านั้นได้โดยตรง ไม่ต้องเขียน annotation หรือดูแลไฟล์ spec แยกเอง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sttp.tapir.docs.openapi.OpenAPIDocsInterpreter&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sttp.apispec.openapi.circe.yaml._&lt;/span&gt;

&lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;docs&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAPIDocsInterpreter&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="py"&gt;toOpenAPI&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getTask&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;listTasks&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;createTask&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
  &lt;span class="s"&gt;"Task API"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"1.0"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;docs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;toYaml&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;docs.toYaml&lt;/code&gt; จะสร้างเอกสาร &lt;a href="https://spec.openapis.org/oas/latest.html" rel="noopener noreferrer"&gt;OpenAPI&lt;/a&gt; จาก endpoint ที่ใช้งานจริง&lt;/p&gt;

&lt;p&gt;ถ้าต้องการ Swagger UI ให้เพิ่ม module &lt;code&gt;tapir-swagger-ui-bundle&lt;/code&gt; แล้ว serve UI จาก server เดียวกันได้ สเปคและ route จะไม่คลาดเคลื่อนกัน เพราะทั้งคู่สร้างจาก endpoint value เดียวกัน&lt;/p&gt;

&lt;p&gt;OpenAPI ที่ได้ยังใช้ต่อกับเครื่องมืออื่นได้ เช่น นำเข้าไปที่ &lt;a href="https://apidog.com?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;Apidog&lt;/a&gt; เพื่อดู API reference, ส่ง request จาก console และสร้าง mock server จาก spec เดียวกับที่ Scala สร้างออกมา เหมาะสำหรับทีม frontend ที่ต้องเริ่มทำงานก่อน backend deploy จริง&lt;/p&gt;

&lt;p&gt;workflow ที่แนะนำคือ:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;กำหนด endpoint ด้วย Tapir&lt;/li&gt;
&lt;li&gt;สร้าง OpenAPI จาก endpoint&lt;/li&gt;
&lt;li&gt;export spec เป็นส่วนหนึ่งของ build หรือ CI&lt;/li&gt;
&lt;li&gt;นำ spec ไปใช้กับ API tool, mock server หรือ frontend contract&lt;/li&gt;
&lt;li&gt;ทดสอบ implementation เทียบกับ contract เดียวกัน&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ทิศทางของ contract จะชัดเจน: Scala type → OpenAPI → mock/client/frontend&lt;/p&gt;

&lt;h2&gt;
  
  
  การทดสอบ API
&lt;/h2&gt;

&lt;p&gt;Tapir endpoint ทดสอบได้ 2 ระดับ&lt;/p&gt;

&lt;h3&gt;
  
  
  1. ทดสอบ logic โดยไม่ต้องเปิด server
&lt;/h3&gt;

&lt;p&gt;ใช้ Tapir stub interpreter เพื่อสร้าง test backend จาก server endpoint แล้วส่ง request ด้วย &lt;a href="https://sttp.softwaremill.com" rel="noopener noreferrer"&gt;sttp&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scala"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.scalatest.flatspec.AsyncFlatSpec&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.scalatest.matchers.should.Matchers&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sttp.client3._&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sttp.client3.testing.SttpBackendStub&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sttp.tapir.server.stub.TapirStubInterpreter&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sttp.client3.circe._&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.circe.generic.auto._&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaskApiSpec&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;AsyncFlatSpec&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;Matchers&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="nv"&gt;backend&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TapirStubInterpreter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;SttpBackendStub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;asynchronousFuture&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;whenServerEndpointRunLogic&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getTaskServer&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;whenServerEndpointRunLogic&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;createTaskServer&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;

  &lt;span class="s"&gt;"GET /tasks/1"&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="s"&gt;"return the seeded task"&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;basicRequest&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="s"&gt;"http://test.com/tasks/1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;response&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asJson&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;map&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;code&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
        &lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;title&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="nc"&gt;Right&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Write the Tapir tutorial"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="s"&gt;"GET /tasks/999"&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="s"&gt;"return a 404 with an error body"&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;basicRequest&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="s"&gt;"http://test.com/tasks/999"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;response&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;asJson&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;ApiError&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;map&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nv"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;code&lt;/span&gt; &lt;span class="n"&gt;shouldBe&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ข้อดีของแนวทางนี้:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ไม่ต้อง bind port&lt;/li&gt;
&lt;li&gt;รันเร็ว&lt;/li&gt;
&lt;li&gt;ทดสอบ logic ผ่าน HTTP abstraction&lt;/li&gt;
&lt;li&gt;ยังใช้ contract เดียวกับ server จริง&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. ทดสอบ integration แบบเปิด server จริง
&lt;/h3&gt;

&lt;p&gt;ใช้ integration test จำนวนไม่มากเพื่อยืนยันว่า server binding, routing และ HTTP stack ทำงานจริง ส่วน coverage หลักควรอยู่ที่ stub-based test เพราะเร็วกว่า แนวทางนี้สอดคล้องกับการ &lt;a href="http://apidog.com/blog/what-is-automated-testing?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ทดสอบอัตโนมัติ&lt;/a&gt; ที่ดี: test ส่วนใหญ่ควรรันเร็ว และมี test หนักเฉพาะจุดที่จำเป็น&lt;/p&gt;

&lt;p&gt;หากต้องการตรวจ contract แบบ manual หรือทำ mock server จาก OpenAPI ให้ &lt;a href="https://apidog.com/download?utm_source=dev.to&amp;amp;utm_medium=wanda&amp;amp;utm_content=n8n-post-automation"&gt;ดาวน์โหลด Apidog&lt;/a&gt; แล้วนำเข้าไฟล์ OpenAPI ที่ Tapir สร้างขึ้น&lt;/p&gt;

&lt;h2&gt;
  
  
  สรุป workflow สำหรับใช้งานจริง
&lt;/h2&gt;

&lt;p&gt;สำหรับโปรเจกต์ Scala API คุณสามารถวางโครงสร้างการทำงานแบบนี้:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;นิยาม domain model และ JSON codec&lt;/li&gt;
&lt;li&gt;นิยาม endpoint ด้วย Tapir&lt;/li&gt;
&lt;li&gt;แนบ &lt;code&gt;serverLogic&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;เลือก server interpreter เช่น Pekko HTTP&lt;/li&gt;
&lt;li&gt;สร้าง OpenAPI จาก endpoint เดียวกัน&lt;/li&gt;
&lt;li&gt;ใช้ stub interpreter สำหรับ unit/integration-light test&lt;/li&gt;
&lt;li&gt;export OpenAPI ไปยัง API tool หรือ mock server&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ผลลัพธ์คือ API contract อยู่ใน Scala type system และถูกใช้ซ้ำทั้ง server, test และ documentation&lt;/p&gt;

&lt;h2&gt;
  
  
  คำถามที่พบบ่อย
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tapir ใน Scala คืออะไร?
&lt;/h3&gt;

&lt;p&gt;Tapir ย่อมาจาก Typed API descRiptions เป็นไลบรารี Scala สำหรับอธิบาย HTTP endpoint เป็นค่าที่มี type ชัดเจน จากคำอธิบายเดียวกัน สามารถสร้าง server route, client และ OpenAPI documentation ได้&lt;/p&gt;

&lt;h3&gt;
  
  
  ฉันจำเป็นต้องใช้ Akka HTTP กับ Tapir หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ไม่จำเป็น Tapir แยก endpoint description ออกจาก server interpreter คุณสามารถใช้ Pekko HTTP, http4s, Netty, Vert.x หรือ interpreter อื่นได้โดยไม่ต้องเปลี่ยน endpoint contract&lt;/p&gt;

&lt;h3&gt;
  
  
  Tapir สร้าง OpenAPI ได้อย่างไร?
&lt;/h3&gt;

&lt;p&gt;Tapir OpenAPI module อ่าน endpoint value แล้วสร้าง OpenAPI spec จาก metadata, input, output และ error output ที่นิยามไว้ใน endpoint เพราะ server และ docs มาจากค่าเดียวกัน เอกสารจึงไม่ควรคลาดเคลื่อนจาก implementation&lt;/p&gt;

&lt;h3&gt;
  
  
  ฉันจะทดสอบ Tapir API โดยไม่ต้องเริ่ม server ได้อย่างไร?
&lt;/h3&gt;

&lt;p&gt;ใช้ Tapir stub interpreter เพื่อสร้าง sttp test backend จาก &lt;code&gt;ServerEndpoint&lt;/code&gt; จากนั้นส่ง request ด้วย sttp เหมือนเรียก HTTP จริง แต่ไม่ต้อง bind port ทำให้ test เร็วและเหมาะกับการรันใน CI&lt;/p&gt;

&lt;h3&gt;
  
  
  ฉันสามารถใช้ OpenAPI จาก Tapir กับเครื่องมือ API อื่นได้หรือไม่?
&lt;/h3&gt;

&lt;p&gt;ได้ OpenAPI ที่ Tapir สร้างเป็นสเปคมาตรฐาน คุณสามารถนำเข้าไปยังเครื่องมืออย่าง Apidog เพื่อดู API reference, ส่ง request, ตรวจ contract และสร้าง mock server สำหรับทีม frontend ได้&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
