บทที่ 6: Parameterization ด้วย CSV Data Set Config¶
⏰ Pre-chapter Retrieval¶
แนะนำ: อ่านบทนี้หลังจากผ่านไปอย่างน้อย 1 วันหลังบทที่ 5
ก่อนอ่านบทนี้ ลองตอบ:
ใน Test Plan ที่สร้าง HTTP Request Sampler ในบทก่อนหน้า สมมติ Thread Group มี 50 threads และ HTTP Request ใช้ค่า hardcode เช่น username=testuser และ password=pass1234 — จะเกิดอะไรขึ้นกับข้อมูลบน server เมื่อ test รัน? นี่เป็นปัญหาอะไรในการ simulate real users?
เขียนคำตอบก่อน อย่าเพิ่งอ่านต่อ — และเขียนเหตุผลสั้นๆ 1–2 ประโยคว่าทำไมถึงตอบแบบนั้น
เฉลย: 50 threads จะ login ด้วย username เดิมพร้อมกัน — เกิดปัญหา 2 ระดับ:
Server side: ระบบอาจบล็อก session ซ้ำ, trigger rate limiting, หรือ backend logic อาจมี edge case เฉพาะเมื่อ user เดิม login หลาย session — ไม่ใช่ scenario ที่ real users ทำ
Data contamination: ถ้า test มี write operations เช่น สั่งซื้อ, อัปเดตข้อมูล — ทุก thread จะ operate บน account เดียวกัน ผลอาจขัดแย้งกัน เช่น thread A เพิ่ง update address แล้ว thread B ก็ update ซ้อน ทำให้ test data corrupted
Remediation path: ถ้าตอบได้แค่ว่า "มัน login ซ้ำกัน" แต่ยังไม่เข้าใจว่าทำไมถึงเป็นปัญหา — ลองนึกถึง scenario จริง: ถ้าร้านค้าทดสอบระบบ checkout แต่ทุก transaction ใช้ account เดิม ผล test จะสะท้อน real users ได้ไหม?
Novice fallback: เหมือนทดสอบคิวแคชเชียร์โดยให้คนเดิมยืนซ้ำทุกช่อง แทนที่จะเป็นลูกค้า 50 คนที่ต่างกัน — คิวอาจเร็วหรือช้าคนละแบบกับความเป็นจริง
1. วัตถุประสงค์¶
เมื่ออ่านบทนี้จบ คุณจะสามารถ:
- อธิบาย ว่า Parameterization คืออะไรและทำไมถึงจำเป็นสำหรับ realistic load test
- สร้าง CSV file และ config CSV Data Set Config ใน JMeter ได้ถูกต้องครบทุก field
- ใช้ variable จาก CSV ใน HTTP Request Sampler ด้วย
${variable_name}syntax - เลือก ระหว่าง CSV Data Set Config กับ User Defined Variables ให้เหมาะกับ use case
- ระบุ 3 common mistakes ที่พบบ่อย (path ผิด, Sharing Mode ผิด, Recycle + Stop Thread ขัดกัน)
2. ทำไมต้องรู้?¶
สมมติทีมทดสอบ API login ของระบบ e-learning — test ผ่านดี แต่พอตรวจสอบผล server logs พบว่า:
- Account testuser001 ถูก login พร้อมกัน 100 sessions
- Account ถูก lock โดย security system เพราะตรวจพบ "concurrent login ผิดปกติ"
- Response time พุ่งสูงเพราะ server lock contention บน record เดิม
test บอกว่า "ผ่าน" แต่ไม่ได้วัดสิ่งที่อยากวัดจริงๆ เพราะ real users 100 คน ไม่ได้ใช้ account เดียวกัน
Parameterization แก้ปัญหานี้โดยทำให้แต่ละ thread ใช้ข้อมูลที่แตกต่างกัน — เหมือน users จริงที่ต่างคนต่าง credentials ต่างคนต่าง product ID ต่างคนต่าง search query
3. Analogy: สคริปต์ละครที่มีนักแสดงต่างกัน¶
ลองนึกภาพ test plan ของ JMeter เหมือน บทละคร (script)
- บทละครบอกว่า "ตัวละครต้องไปที่ counter แล้วพูดชื่อตัวเอง จากนั้นยื่น ID"
- ถ้าทุกคนอ่านบทเดิมแต่มีชื่อและ ID ต่างกัน — นั่นคือ parameterization
- CSV file คือ cast list — รายชื่อนักแสดงพร้อม prop แต่ละคน
- CSV Data Set Config คือ stage manager ที่คอยบอกว่า "scene นี้ใช้คนไหน"
${username}ใน script คือ stage direction ที่บอกให้นักแสดงแต่ละคนพูดชื่อตัวเอง
⚠️ ถ้าเชื่อ analogy นี้ 100% จะเข้าใจผิดว่า: CSV ทุก row จะถูกใช้อย่างเท่าเทียมกันเสมอ — ผิด ถ้า Sharing Mode = "All threads" และ threads มากกว่า CSV rows JMeter จะ Recycle กลับ row 1 ทำให้ rows แรกถูกใช้บ่อยกว่า rows หลัง และถ้า Recycle = False test จะหยุดกลางคันเมื่อ CSV หมด — behavior นี้เปลี่ยนไปทั้งหมดขึ้นอยู่กับ Sharing Mode และ Recycle setting
4. เนื้อหาหลัก¶
4.1 CSV Data Set Config: ทำไมต้องมี¶
ก่อนจะรู้ว่า config อะไร ต้องรู้ว่าปัญหาคืออะไร
ปัญหาของ hardcoded values: - ทุก thread ใช้ข้อมูลเดิม → ไม่ simulate real diversity of users - ถ้า server ล็อก duplicate session → test พังด้วยเหตุผลผิด (ไม่ใช่ performance แต่เป็น data issue) - write operations บน record เดิม → test data corrupted
CSV Data Set Config แก้ปัญหาโดย: - อ่าน CSV file ที่มี rows ของข้อมูลต่างๆ - แจก row ให้ threads ตาม Sharing Mode ที่กำหนด - แต่ละ thread ได้ข้อมูลต่างกัน → simulate real users ได้จริง
4.2 CSV Data Set Config: Fields ทีละตัว¶
เพิ่ม CSV Data Set Config ได้ที่: คลิกขวาที่ Thread Group → Add → Config Element → CSV Data Set Config
Field 1: Filename
"Path to CSV file" (source: https://jmeter.apache.org/usermanual/component_reference.html)
- ระบุ path ของ CSV file
- ✅ Best practice: เก็บ CSV ไว้ใน directory เดียวกับ
.jmxfile แล้วใช้ชื่อไฟล์เปล่าๆ เช่นusers.csv - เหตุผล: relative path ทำให้ test plan portable — ย้ายไปรันบนเครื่องอื่นหรือ CI/CD server ได้โดยไม่ต้องแก้ path
⚠️ Gotcha สำคัญ — Working Directory ของ JMeter:
JMeter ตีความ relative path จาก working directory ขณะรัน ไม่ใช่จาก directory ที่เก็บ .jmx file
- GUI mode: working directory =
apache-jmeter-x.x.x/bin/→users.csvหมายถึง/path/to/jmeter/bin/users.csv - CLI mode (ตัวอย่างที่อาจ fail):
jmeter -n -t /home/user/tests/test.jmx→ working directory ยังเป็นbin/ดังนั้นusers.csvจะไม่พบ
วิธีแก้ที่ recommended:
# วิธีที่ 1 (portable): ใช้ path relative ต่อ .jmx file ด้วย built-in property
Filename: ${__P(test.basedir,/default/path)}/users.csv
# วิธีที่ 2 (ง่ายที่สุด): ใช้ absolute path เสมอ
Filename: /home/user/tests/users.csv
# วิธีที่ 3 (สำหรับ CLI): cd ไปที่ folder test plan ก่อน
cd /home/user/tests && jmeter -n -t test.jmx
Field 2: Variable Names
"Comma-separated list of variable names" (source: https://jmeter.apache.org/usermanual/component_reference.html)
- ชื่อ variable ที่จะ map กับแต่ละ column ใน CSV (คั่นด้วย comma)
- ถ้า CSV row คือ
alice,pass123และ Variable Names คือusername,password - →
${username}=alice,${password}=pass123 - ถ้าปล่อยว่าง JMeter จะใช้ row แรกของ CSV เป็น header (ชื่อ column)
Field 3: Delimiter
"Character separating values (default: comma)" (source: https://jmeter.apache.org/usermanual/component_reference.html)
- ค่า default:
,(comma) - เปลี่ยนได้ถ้า data มี comma เช่น address — ใช้
|หรือ\t(tab) แทน - ถ้าใช้ tab ให้พิมพ์
\tในช่อง Delimiter
Field 4: Allow Quoted Data
- ถ้า
True— ค่าที่อยู่ใน double quotes จะถูก parse เป็น 1 field แม้มี comma ข้างใน - เช่น
"Smith, John",pass123→ field 1 =Smith, John, field 2 =pass123 - ใช้เมื่อ data มี comma ภายใน field เช่น address หรือ full name
Field 5: Recycle on EOF
"Restart from beginning when file ends" (source: https://jmeter.apache.org/usermanual/component_reference.html)
True(default): เมื่ออ่าน CSV ครบทุก row แล้ว กลับไปเริ่มต้นที่ row แรกใหม่False: เมื่อ CSV หมด — ขึ้นอยู่กับ Stop Thread on EOF- ใช้
Trueเมื่อ: test รันนานกว่า rows ที่มี (เช่น soak test 8 ชั่วโมงกับ CSV 100 rows) - ใช้
Falseเมื่อ: ต้องการให้แต่ละ row ถูกใช้ครั้งเดียว เช่น test ที่ data สำคัญและห้ามซ้ำ
Field 6: Stop Thread on EOF
"Terminate thread when file exhausted" (source: https://jmeter.apache.org/usermanual/component_reference.html)
True: เมื่อ CSV หมด thread จะหยุดทำงานFalse: thread ไม่หยุด (ใช้ร่วมกับ Recycle on EOF = True หรือ False มีผลต่างกัน — ดู section 6c)- ใช้
Trueเมื่อ: test design ให้รันตาม data — "ทดสอบ 1000 orders ครบแล้วหยุด"
Field 7: Sharing Mode
"Thread/thread group scope" [controls how file data is distributed among threads] (source: https://jmeter.apache.org/usermanual/component_reference.html)
Options: - All threads (แนะนำสำหรับ real-world): ทุก thread ใน test plan ใช้ pointer เดียวกัน — แต่ละ request ได้ row ถัดไปตามลำดับ ไม่มีการซ้ำ - Current thread group: แต่ละ Thread Group มี pointer ของตัวเอง — Thread Group A กับ B อ่าน CSV แยกกัน - Current thread: แต่ละ thread มี pointer ของตัวเอง — thread A เริ่มที่ row 1, thread B เริ่มที่ row 1 เช่นกัน → ทุก thread อ่านข้อมูลเดิม!
✅ Best practice: ใช้ Sharing Mode = "All threads" สำหรับ real-world test
เหตุผล: "All threads" ทำให้ทุก request ได้ data ต่างกัน เหมือน users จริงที่ต่างคนต่าง credentials ถ้าใช้ "Current thread" ทุก thread จะวนซ้ำ data ชุดเดิมตั้งแต่ row 1 ซึ่งไม่ simulate real diversity
⏸ self-check (Backward Retrieval): ก่อนอ่านต่อ — อธิบาย Sharing Mode = "Current thread" vs "All threads" ว่าต่างกันอย่างไร และ scenario ไหนที่ "Current thread" จะให้ผล misleading เขียนคำตอบลงกระดาษก่อน — และเขียนเหตุผลสั้นๆ 1–2 ประโยคว่าทำไมถึงตอบแบบนั้น — แล้วค่อยอ่านต่อ
เฉลย: - Sharing Mode = "All threads" — CSV pointer เป็น global ทุก thread ใน test plan แชร์ตัวนับเดียวกัน — thread 1 ได้ row 1, thread 2 ได้ row 2, thread 3 ได้ row 3 — เหมาะสำหรับ simulating unique users ที่ใช้ credentials ต่างกัน - Sharing Mode = "Current thread" — แต่ละ thread มี CSV pointer ของตัวเอง เริ่มจาก row 1 เสมอ — thread 1 ได้ row 1, thread 2 ก็ได้ row 1 เช่นกัน — เหมาะสำหรับ scenario ที่ thread ต้องทำ full workflow ซ้ำๆ ด้วย data ชุดเดิม
ถ้าตอบไม่ถูก: กลับอ่าน Section 4.1 (Sharing Mode) อีกครั้ง โดยเน้น table เปรียบเทียบ
4.3 ใช้ Variable ใน HTTP Request¶
หลังตั้งค่า CSV Data Set Config แล้ว ใช้ตัวแปรใน HTTP Request Sampler ด้วย syntax ${variable_name}
ตัวอย่าง:
HTTP Request: POST /api/auth/login
Body Data (JSON):
{
"username": "${username}",
"password": "${password}"
}
${username} และ ${password} จะถูกแทนที่ด้วยค่าจาก CSV ก่อน JMeter ส่ง request
✅ Best practice: ใช้ variable ใน Path ได้ด้วย เช่น /api/products/${product_id} — ทำให้ test เรียก product ID ต่างๆ แทนที่จะเรียก product เดิมซ้ำ
4.4 User Defined Variables vs CSV: เมื่อไหรใช้อะไร¶
JMeter มีอีก 1 วิธีในการ define variables: User Defined Variables
"Define some user-defined variables at the Test Plan level...anyplace that value is found in your recorded samples will be replaced" (source: https://jmeter.apache.org/usermanual/best-practices.html)
เมื่อไหรใช้ User Defined Variables:
- ค่าที่ คงที่ตลอด test เช่น base URL, API version, timeout
- ค่าที่ไม่เปลี่ยนตาม thread หรือ iteration
- ตัวอย่าง: BASE_URL = https://api.example.com, API_VERSION = v2
เมื่อไหรใช้ CSV Data Set Config: - ข้อมูลที่ต้องการให้ ต่างกันต่อ thread หรือ iteration เช่น username, product ID, order ID - ข้อมูลที่มีหลาย rows
ข้อจำกัดสำคัญของ User Defined Variables:
"Configuration elements are processed by a separate thread. Therefore functions such as __threadNum do not work properly in elements such as User Defined Variables." (source: https://jmeter.apache.org/usermanual/functions.html)
ใช้ User Defined Variables สำหรับค่า static เท่านั้น — ไม่ใช่ dynamic per-thread values
สรุปตาราง:
| Use Case | วิธีที่แนะนำ |
|---|---|
| Base URL, API key ที่ใช้ทั้ง test | User Defined Variables |
| Username/password ต่างกันต่อ user | CSV Data Set Config |
| Product ID จาก list 1,000 items | CSV Data Set Config |
| Environment (staging/production) | User Defined Variables |
| Random search keywords | CSV Data Set Config หรือ ${__Random()} |
4.5 Random Variable Controller และ Built-in Functions¶
${__Random(min,max)}
"The random function returns a random number that lies between the given min and max values." (source: https://jmeter.apache.org/usermanual/functions.html)
ตัวอย่าง: ${__Random(1,1000)} → สุ่มตัวเลขระหว่าง 1-1000
ใช้เมื่อ: ต้องการ random ID โดยไม่มี CSV เช่น /api/products/${__Random(1,500)}
${__CSVRead()}
"In most cases, the newer CSV Data Set Config element is easier to use." (source: https://jmeter.apache.org/usermanual/functions.html)
แนะนำให้ใช้ CSV Data Set Config แทน __CSVRead() เพราะ config-based approach ดูแลง่ายกว่าและมี UI ที่ชัดเจน
⏸ self-check (Bloom's L4 — Analysis): สมมติ test plan มี Thread Group 50 threads, CSV มี 10 rows, Sharing Mode = "All threads", Recycle on EOF = True — ในแต่ละ iteration thread ที่ 11 จะได้ data จาก row ไหน? ทำไม? เขียนคำตอบลงกระดาษก่อน — และเขียนเหตุผลสั้นๆ 1–2 ประโยคว่าทำไมถึงตอบแบบนั้น — แล้วค่อยอ่านต่อ
เฉลย: กับ Sharing Mode = "All threads" และ Recycle = True: - Thread 1 Loop 1 → Row 1, Thread 2 Loop 1 → Row 2, ..., Thread 10 Loop 1 → Row 10 - Thread 11 Loop 1 → Row 1 (Recycle กลับต้น), ..., Thread 20 Loop 1 → Row 10 - Thread 21-30 → Row 1-10 อีกรอบ (recycle) และ Thread 31-50 เช่นกัน - ข้อสังเกต: ด้วย 50 threads และ 10 rows — แต่ละ row ถูกใช้โดย 5 threads พร้อมกัน → ทดสอบ concurrent access บน account เดียวกัน - ถ้าต้องการ unique user per thread: ต้องมีอย่างน้อย 50 rows ใน CSV หรือปรับ Sharing Mode
ถ้าตอบไม่ถูก: กลับอ่าน Section 4.1 (Sharing Mode + Recycle on EOF) และลอง trace ด้วยกระดาษ — ถ้าระบุจุดที่เข้าใจผิดไม่ได้: อ่าน section 4 ทั้งหมดใหม่
5. ตัวอย่าง 3 ระดับ¶
Beginner: User Credentials สำหรับ Login API¶
Scenario: ทดสอบ POST /api/auth/login ด้วย 5 users ที่มี credentials ต่างกัน
ขั้นตอนที่ 1: สร้าง CSV file
สร้างไฟล์ users.csv ในโฟลเดอร์เดียวกับ .jmx file:
username,password
alice@example.com,AlicePass123
bob@example.com,BobPass456
carol@example.com,CarolPass789
dave@example.com,DavePass012
eve@example.com,EvePass345
ขั้นตอนที่ 2: Config CSV Data Set Config ใน JMeter
# ยังไม่ได้ทดสอบ — ต้องการ JMeter 5.6.3
CSV Data Set Config:
Name: User Credentials
Filename: users.csv
# ทำไม relative path? เพราะ .jmx และ CSV อยู่โฟลเดอร์เดียวกัน
# portable — ย้ายไปเครื่องอื่นไม่ต้องแก้ path
Variable Names: username,password
# ทำไม ไม่ปล่อยว่าง? เพราะเราไม่ต้องการให้ row แรก (header) เป็น data
# ถ้าปล่อยว่าง JMeter อ่าน row แรกเป็น variable names → ถูกต้อง แต่ header จะถูก skip
# ระบุ explicit ดีกว่า ชัดเจนกว่า
Delimiter: ,
# ค่า default ใช้ได้เพราะ CSV ใช้ comma ปกติ
Allow Quoted Data: False
# ทำไม False? ข้อมูลง่ายๆ ไม่มี comma ใน field — ไม่ต้องการ
Recycle on EOF: True
# ทำไม True? ถ้า Thread Loop > 1 ต้องวน CSV ซ้ำ
# 5 rows สำหรับ 5 threads — ถ้า loop 3 รอบ จะได้ 15 requests ต้องวนกลับ
Stop Thread on EOF: False
# ทำไม False? เราตั้ง Recycle = True อยู่แล้ว EOF จะไม่เกิด
Sharing Mode: All threads
# ทำไม All threads? ต้องการให้แต่ละ thread ได้ user ต่างกัน
# thread 1 = alice, thread 2 = bob, ... — สะท้อน real users
ขั้นตอนที่ 3: ใช้ variable ใน HTTP Request
# ยังไม่ได้ทดสอบ — ต้องการ JMeter 5.6.3
HTTP Request Sampler:
Method: POST
Path: /api/auth/login
Body Data:
{
"email": "${username}",
"password": "${password}"
}
Headers (ผ่าน HTTP Header Manager):
Content-Type: application/json
Intermediate: Parameterize Product ID สำหรับ Inventory Management System¶
Scenario: ทีม QA ต้องทดสอบ GET /api/inventory/items/${item_id} ของระบบ warehouse management ด้วย product IDs จาก catalog จริง 100 items
สร้าง CSV:
item_id,item_name,expected_category
WH-001,Hydraulic Pump,machinery
WH-002,Safety Helmet,ppe
WH-003,Forklift Battery,power
...
# (ครบ 100 rows)
Config สำหรับ Load Test:
# ยังไม่ได้ทดสอบ — ต้องการ JMeter 5.6.3
Thread Group:
Threads: 50
Ramp-Up: 50 seconds
Loop Count: Infinite
Scheduler: Duration = 300 seconds (5 นาที)
CSV Data Set Config:
Filename: warehouse_items.csv
Variable Names: item_id,item_name,expected_category
Delimiter: ,
Recycle on EOF: True # 50 threads × หลาย loop > 100 rows → ต้องวน
Stop Thread on EOF: False
Sharing Mode: All threads # thread ต่างๆ ได้ item_id ต่างกัน
Uniform Random Timer:
Random delay maximum: 1500
Constant delay offset: 500 # delay 500-2000ms เลียนแบบ warehouse staff
# ที่ scan barcode แล้วรอดู result
HTTP Request Sampler:
Method: GET
Path: /api/inventory/items/${item_id}
Response Assertion (เพิ่มเพื่อ validate):
Field to test: Response Body
Contains: "${expected_category}"
# ตรวจสอบว่า category ใน response ตรงกับที่คาดไว้
ทำไม expected_category ถึงอยู่ใน CSV?
เพราะ test ที่ดีต้องตรวจสอบ correctness ไม่ใช่แค่ latency — การเก็บ expected value ใน CSV ทำให้ assertion ยืดหยุ่น ไม่ต้อง hardcode ใน script
Advanced: JWT Token Refresh Logic ใน Test Plan¶
Scenario: API ต้องการ JWT token ที่ expire ทุก 15 นาที — test ที่รัน 1 ชั่วโมงต้องจัดการ token refresh ระหว่างทาง
ปัญหา: CSV Data Set Config ใส่ static token ไม่ได้สำหรับกรณีนี้ เพราะ token expire
แนวทาง: Login และ Extract Token เป็น Flow ใน Test Plan
# ยังไม่ได้ทดสอบ — ต้องการ JMeter 5.6.3
Test Plan Structure:
├── Thread Group: API Load Test
│ ├── CSV Data Set Config
│ │ Filename: users.csv
│ │ Variable Names: username,password
│ │ Sharing Mode: All threads
│ │
│ ├── [Once Only Controller] ← รันครั้งเดียวตอนเริ่ม thread
│ │ └── HTTP Request: POST /api/auth/login
│ │ Body: {"username": "${username}", "password": "${password}"}
│ │
│ │ + JSON Extractor (เพิ่มใต้ HTTP Request นี้)
│ │ Variable Name: access_token
│ │ JSON Path: $.data.access_token
│ │
│ │ + JSON Extractor (อีกตัว)
│ │ Variable Name: token_expires_at
│ │ JSON Path: $.data.expires_at
│ │
│ ├── [If Controller] ← ตรวจสอบว่า token ใกล้ expire ไหม
│ │ Condition: ${__jexl3(System.currentTimeMillis() > Long.parseLong("${token_expires_at}") - 60000)}
│ │ # ถ้า current time > (expire_time - 1 นาที) → เข้า block นี้เพื่อ refresh
│ │ └── HTTP Request: POST /api/auth/refresh
│ │ Body: {"refresh_token": "${refresh_token}"}
│ │
│ │ + JSON Extractor
│ │ Variable Name: access_token ← update ค่า token ใหม่
│ │
│ └── HTTP Request: GET /api/protected/data
│ Headers:
│ Authorization: Bearer ${access_token}
│ # ใช้ token ที่ได้จาก login หรือ refresh ล่าสุด
Trade-off Discussion:
| แนวทาง | ข้อดี | ข้อเสีย |
|---|---|---|
| Once Only Controller + token extraction | ทำ login จริงเหมือน user | เพิ่ม complexity ใน test plan |
| Static token ใน CSV (ไม่ expire หรือ expire ยาวมาก) | ง่ายมาก ไม่ต้องทำ refresh logic | ไม่ realistic กับ security policy จริง |
| Pre-generate tokens ก่อน test (script ภายนอก) | แยก concerns ชัดเจน | ต้อง maintain 2 ส่วน (token generator + test plan) |
เมื่อไหรใช้แนวทางไหน: - Test สั้น (< 10 นาที) และ token expire ยาว → pre-generate ใน CSV ก็พอ - Test ยาว (> token expiry) หรือ test security flow → ใช้ Once Only + If Controller - Production-grade test ที่ต้องการ full realistic simulation → Once Only + refresh logic
6. Common Mistakes¶
❌ (a) Path ของ CSV ผิด (Absolute vs Relative)¶
แบบผิด:
Filename: C:\Users\john\tests\users.csv
# ใช้ได้บนเครื่อง john เท่านั้น
# พอรันบน CI/CD server หรือเครื่อง team member อื่น → FileNotFoundException
🔍 สัญญาณ: error message ว่า FileNotFoundException หรือ No such file or directory เมื่อรันบนเครื่องอื่น
🤔 ถามตัวเอง: "ถ้าฉันส่ง .jmx file นี้ให้ teammate รัน เขาจะต้องแก้ Filename ไหม?"
แบบถูก:
(source: https://jmeter.apache.org/usermanual/component_reference.html — Filename: "Path to CSV file")
❌ (b) Sharing Mode ผิดทำให้ Threads ใช้ Data ซ้ำ¶
แบบผิด:
Sharing Mode: Current thread
# ทุก thread เริ่มอ่าน CSV จาก row 1
# thread 1, 2, 3 ... ทุกตัวได้ username เดิม (alice) ในทุก iteration แรก
🔍 สัญญาณ: ดู server access log แล้วเห็น username เดิมปรากฏซ้ำๆ มาจาก IP ต่างๆ แทนที่จะเห็น username หลากหลาย 🤔 ถามตัวเอง: "ถ้า test ของฉันมี 50 threads และ CSV มี 50 rows — ทุก thread ควรได้ username ต่างกันไหม? Sharing Mode ที่ใช้อยู่ทำแบบนั้นได้ไหม?"
แบบถูก:
Sharing Mode: All threads
# thread ทั้งหมดแชร์ pointer เดียว
# thread 1 ได้ row 1, thread 2 ได้ row 2, thread 3 ได้ row 3 ...
(source: https://jmeter.apache.org/usermanual/component_reference.html — Sharing mode: "Thread/thread group scope")
❌ (c) Recycle on EOF = False + Stop Thread on EOF = False ทำให้ Variable ว่าง¶
แบบผิด:
Recycle on EOF: False
Stop Thread on EOF: False
# เมื่อ CSV หมด JMeter จะพยายามอ่าน row ต่อไปแต่ไม่มี
# ค่า variable จะกลายเป็น "<EOF>" หรือ string ว่าง
# HTTP Request จะส่ง username="" หรือ username="<EOF>"
🔍 สัญญาณ: request บางตัวได้ response "invalid username" หรือ 400 Bad Request โดยที่ test data ดูเหมือนถูกต้อง — แต่จริงๆ CSV หมดแล้ว 🤔 ถามตัวเอง: "CSV ของฉันมีกี่ rows? threads รัน iterations กี่รอบ? rows พอไหม?"
แบบถูก (เลือก 1 ใน 3):
# Option A: ต้องการวนซ้ำ data
Recycle on EOF: True
Stop Thread on EOF: False
# Option B: ต้องการหยุดเมื่อ data หมด
Recycle on EOF: False
Stop Thread on EOF: True
# Option C: สร้าง CSV ให้มี rows มากพอกว่า threads × loops
# ไม่ต้องกังวลเรื่อง EOF เลย
(source: https://jmeter.apache.org/usermanual/component_reference.html — Recycle on EOF: "Restart from beginning when file ends" / Stop Thread on EOF: "Terminate thread when file exhausted")
7. สรุปบท¶
คำถาม Retrieval — ตอบก่อนดูเฉลย¶
หยุดอ่าน เขียนคำตอบลงกระดาษก่อน อย่างน้อย 30 วินาทีต่อข้อ — และเขียนเหตุผลสั้นๆ 1–2 ประโยคว่าทำไมถึงตอบแบบนั้น ก่อนดูเฉลย
คำถาม 1: อธิบายด้วยคำพูดตัวเองว่า Sharing Mode = "All threads" ต่างจาก "Current thread" อย่างไร — และในการทำ login test ที่มี 100 threads กับ CSV 100 rows คุณจะใช้ mode ไหนและทำไม?
คำถาม 2: ถ้า CSV มี 50 rows, threads = 10, Loop Count = 10, Recycle on EOF = True, Sharing Mode = All threads — แต่ละ thread จะใช้ data จาก CSV ตาม pattern ไหน? มีโอกาสที่ rows บางอันจะถูกใช้บ่อยกว่าอื่นไหม?
คำถาม 3 (code-based): ดู config ด้านล่างนี้แล้วบอกว่ามีปัญหาอะไร:
Test Plan สำหรับ E-commerce Order API:
CSV Data Set Config:
Filename: C:\jmeter-tests\orders\product_ids.csv
Variable Names: (ปล่อยว่าง)
Recycle on EOF: False
Stop Thread on EOF: False
Sharing Mode: Current thread
HTTP Request:
GET /api/products/${product_id}
# แต่ CSV header row คือ "product_id,product_name"
เฉลย คำถาม 1: "All threads" — ทุก thread แชร์ pointer เดียว thread หนึ่งอ่าน row แล้ว pointer เลื่อน thread ถัดไปได้ row ถัดไป → ทุก thread ได้ข้อมูลต่างกัน "Current thread" — แต่ละ thread มี pointer ของตัวเอง เริ่มที่ row 1 เหมือนกันทุก thread → ทุก thread ได้ข้อมูลเดิม
สำหรับ login test 100 threads: ใช้ "All threads" — ต้องการให้แต่ละ thread ใช้ account ต่างกัน simulate 100 unique users
เฉลย คำถาม 2: 100 iterations ทั้งหมด (10 threads × 10 loops) ด้วย CSV 50 rows และ Recycle = True — แต่ละ row จะถูกใช้เฉลี่ย 2 ครั้ง (100/50) แต่การแจกขึ้นอยู่กับ timing และ thread interleaving — rows ช่วงต้นอาจถูกใช้บ่อยกว่าเล็กน้อยถ้า threads เริ่มต้นพร้อมกัน ในทางปฏิบัติถือว่ากระจายได้ดีพอ
เฉลย คำถาม 3 (ปัญหาใน config):
ปัญหา 4 จุด:
1. Path absolute: C:\jmeter-tests\... จะพังบน OS อื่นหรือเครื่องอื่น → แก้เป็น relative path product_ids.csv
2. Variable Names ว่าง: JMeter จะอ่าน row แรก (product_id,product_name) เป็น variable names — ในกรณีนี้ถูกต้อง แต่ควรระบุ explicit เพื่อความชัดเจน
3. Recycle = False + Stop Thread = False: เมื่อ CSV หมด ${product_id} จะเป็น <EOF> → request ส่ง /api/products/<EOF> → 404 ทุกตัว → แก้เป็น Recycle = True หรือ Stop Thread = True
4. Sharing Mode = Current thread: ทุก thread เริ่มที่ product_id ตัวแรก → ทุก thread request product เดิม → ไม่ simulate diversity → แก้เป็น All threads
Generation Effect — ลองสร้างเอง:
หยุดสักครู่ก่อนปิดบทนี้ เลือก 1 ใน 2:
A) คิด scenario จากงาน/โปรเจคของตัวเอง ว่าถ้าต้องทดสอบ API ของคุณ ข้อมูลอะไรที่ควรอยู่ใน CSV? แต่ละ column คืออะไร? จะตั้ง Sharing Mode และ Recycle on EOF เป็นอะไร?
B) เขียน CSV file 5 rows สำหรับ scenario ที่ต่างจากตัวอย่างในบทนี้ทั้งหมด แล้วระบุ CSV Data Set Config settings ที่จะใช้ พร้อมเหตุผลแต่ละ field
การสร้าง CSV ด้วยตัวเองทำให้เชื่อม concept กับ use case จริงได้ — ดีกว่าการอ่านซ้ำ 3 รอบ
Remediation Path: - ถ้าตอบคำถาม 1 ไม่ได้ → อ่าน section 4.2 Field 7 (Sharing Mode) ใหม่ - ถ้าตอบคำถาม 2 ผิด → ลองวาด diagram บนกระดาษ: 10 threads, pointer เดียว, 50 rows — ตาม pointer ว่าจะเลื่อนอย่างไร - ถ้าตอบคำถาม 3 ได้แค่ 1-2 ข้อ → อ่าน section 6 (Common Mistakes) ทั้ง 3 ข้ออีกครั้ง
บทที่ 7: Assertions — ตรวจสอบว่า Response ถูกต้อง →