Mock service ของคุณ หรือ ของคนอื่นด้วย Mountebank กันเถอะ
ปัจจุบันนี้ เวลาที่เรา implement application ต่าง ๆ ขึ้นมา ถ้าหากทุกอย่างติดต่อกันเองภายใน applications ทั้งหมดก็คงไม่เป็นปัญหาอะไรในการเชื่อมต่อกันเพื่อทำการทดสอบระบบ แต่เนื่องด้วยปัจจุบัน เรา implement กันแบบ microservice แยก module ต่าง ๆ ออกจากกัน อาจจะแบ่งกันออกเป็นหลาย ๆ ทีม ทีมนึงอาจจะดู 3–4 service อีกทีมดู 3–4 serviceหรือ บางทีเราก็อาจจะจำเป็นต้องทำการติดต่อไปกับระบบอื่น ที่ไม่ใช่ของตนเอง เรามีเพียง spec ที่เขาบอกว่า ถ้าส่ง A ไป จะต้องได้ Z กลับมา
ทีนี้เวลาที่เรา implement นอกจากเราจะจำเป็นต้องทำตัว request ให้ตรงกับสิ่งที่อีก service ต้องการแล้ว เรายังจะต้อง implement ให้ handle response ของอีก service ด้วย ว่าเมื่อเขาส่งอะไรมา เราจะเอาไปทำอะไรต่อ
สมมุติ เราคือ Service “X” ต้องทำการเชื่อมต่อไปที่ Service “Y”
โดยใช้ RESTful Web Service โดยมี request แบบง่าย ๆ ดังนี้
URL: https://iamgique.com/customer/v1/profiles/createMethod: POST
Header:
Key: Content-Type
Value: application/json
Body:
{
"requestTransactionId": "xxx",
"name": "TestName",
"lastname": "TestLastname",
"cardIdentification": "1212121212121"
}
Response ที่ Service “Y” จะส่งคืนมาจะคือ
Body:
{
"request_id": "12345678",
"response_code": "0000",
"response_desc": "SUCCESS"
}
จากตัวอย่างข้างบนเป็นตัวอย่าง Request Response แบบง่าย ๆ เชื่อว่าทุกท่านเคยเจอกันมาแล้ว
ปัญหามันอยู่ตรงนี้ครับ “ผมเชื่อเหลือเกินว่า หลายคนเคยเจอ” Service “Y” ตาย / ปิดเครื่อง / implement ยังไม่เสร็จ / เราจะยิง perf แบบ มีของที่จะใช้อย่างจำกัด หรืออะไรก็ตามแต่ที่ทำให้เรา ไม่ควรยิง หรือ ยิงไปหา Service “Y” ไม่ได้ นั่นแหละครับ ที่พูดมาซะยาวทั้งหมด เพื่อจะบอกว่า มาใช้ Mountebank เพื่อ Mock service กันเถอะ
Mountebank คือ open source ตัวนึงเป็นเครื่องมือสำหรับสร้าง stub และ mock ซึ่ง provide cross-platform, multi-protocol รองรับการทำงานผ่าน พวก HTTP, HTTPS, TCP, SMTP และ รันบน nodejs
Website official: http://www.mbtest.org
มาดูตัวอย่างแล้วลองทำเล่นกัน
ขั้นแรกเลยทำให้มันใช้ได้ก่อน ด้วยความที่มันรันบน nodejs
ไปโหลดกันได้ที่ https://nodejs.org/en/download/ ถ้ามีแล้วก็ข้ามไปนะครับ
จากนั้นก็ install moutebank ด้วย npm หรือ yarn เอาที่ชอบ
install: npm install -g mountebankrun: mb //Command for run mountebank
ผมจะมาลอง Demo ให้ดูครับ กรณีนี้เราจะทำการ create user จาก service “X” ยิงไป create user ที่ service “Y” แต่เนื่องจาก service “Y” อาจจะยัง implement ไม่เสร็จ หรือ การ create user จำเป็นต้องใช้เลขบัตรประชาชนจริง เพราะว่า service “Y” มีการ verify เลขบัตรประชาชนเอาไว้ เราจึงจำเป็นต้อง mock service “Y” เพื่อให้เรายิงได้ตลอดกาล (ไม่ได้หมายความว่าเราจะไม่ Test service “Y” จริง ๆ นะครับ)
imposters.ejs{
“imposters”: [{
“port”: “6969”, // port ที่เราจะให้ mountebank mock
“protocol”: “http”, // ให้ protocol http
“name”: “profiles”, // ตามใจ
“stubs”: [{
“predicates”: [ <% include profiles/success/success_predicates.ejs %> ],
“responses”: [ <% include profiles/success/success_response.ejs %> ]
},{
“predicates”: [ <% include profiles/fail/fail_predicates.ejs %> ],
“responses”: [ <% include profiles/fail/fail_response.ejs %> ]
},{
“responses”: [ <% include proxy.ejs %> ]
}]
}]
}// ในส่วนของ stubs จะมีอยู่ด้วยกัน 2 อย่างคือ predicates กับ response
// predicates คือการจำลองทางเข้ามาของ service ที่เราจะยิงไปหา
// response คือการตอบกลับ ว่าเราต้องการตอบอะไรออกไป-----------------------------proxy.ejs{
“proxy”: {
“to”: “https://www.google.com",
“mode”: “proxyAlways”
}
}// ในกรณีที่การ request ไม่เข้าเงื่อนไขที่กำหนด จะให้ยิงไปไหน (เช่น https://www.google.com)
มา mock ในส่วนของ success กัน
success_predicates.ejs{
“and”:[{
“equals”:{
“path”:”/customer/v1/profiles/create”,
“method”:”POST”
}
},{
“matches”:{
“body”:{
“cardIdentification”:”^XY.*”,
“requestTransactionId”:”^.*”
}
}
}]
}// ถ้า request
// มี path: /customer/v1/profiles/create
// มี method: POST
// ในส่วนของ body
// ถ้าส่ง cardIdentification มีค่านำหน้าคือ XY ตามด้วยอักขระอะไรก็ได้
// ถ้าส่ง requestTransactionId มีค่าเข้ามาเป็นอะไรก็ได้
// จะเข้าเงื่อนไขทั้งหมด
success_response.ejs{
"is": {
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"request_id":"12345678",
"response_code":"0000",
"response_desc":"SUCCESS"
}
}
}// การ return response แบบ success
มา mock ในส่วนของ fail กัน
fail_predicates.ejs{
“and”:[{
“equals”:{
“path”:”/customer/v1/profiles/create”,
“method”:”POST”
}
},
{
“matches”:{
“body”:{
“cardIdentification”:”^FF.*”,
“requestTransactionId”:”^.*”
}
}
}]
}// ถ้า request
// มี path: /customer/v1/profiles/create
// มี method: POST
// ในส่วนของ body
// ถ้าส่ง cardIdentification มีค่านำหน้าคือ FF ตามด้วยอักขระอะไรก็ได้
// ถ้าส่ง requestTransactionId มีค่าเข้ามาเป็นอะไรก็ได้
// จะเข้าเงื่อนไขทั้งหมด
fail_response.ejs{
"is": {
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"request_id":"12345678",
"response_code":"1001",
"response_desc":"mock error"
}
}
}
// การ return response แบบ fail
หลังจากที่ท่านทำตามทั้ง 6 files เสร็จแล้ว ให้ลอง run ทดสอบดูด้วยคำสั่งตามนี้
mb — configfile imposters.ejs — allowInjection
จากนั้นให้ท่านลองเปิด browser ทำการยิงไปที่ url http://localhost:6969/customer/v1/profiles/create
จะขึ้นหน้า error 404 ของ Google แต่ !! ไม่ต้องตกใจ เราตั้งใจทำแบบนั้นเอง เนื่องจากที่เรา request ไปไม่ตรงกับเงื่อนไขทั้งหมด เช่นที่เราทำไว้เรารับ method post แต่ที่เรา request ไปตรง ๆ นี่มันเป็น method get เมื่อไม่เข้าเงื่อนไข มันก็จะไม่เข้า mock มันจะโดดไปหา google ตามที่ได้ตั้งค่าไว้ที่ “proxy.ejs”
เรามาลองยิงให้เข้า success case กัน
ให้ท่านเปิด Postman หรือ Insomnia REST เอาตามที่ท่านถนัด ในตัวอย่างจะเป็น postman นะครับ
ทีนี้มาลองยิงให้เข้า mock ตัว error กันบ้าง
ที่ผ่านมาเราลองยิงไปแบบ success case และ fail case ตามที่เราได้ mock เอาไว้แล้ว ด้วย request ที่เป็น key หลักก็คือ “cardIdentification”
ถ้าหากส่ง ตัวอักษรนำหน้ามาเป็น XY ตามด้วยอักขระใด ๆ จะทำให้เกิด success case
ถ้าหากส่ง ตัวอักษรนำหน้ามาเป็น FFตามด้วยอักขระใด ๆ จะทำให้เกิด fail caseอยากให้ท่านลองเอา XY หรือ FF ออกดูว่าถ้าไม่มีทั้งสองอันนี้ ใส่เป็นค่าอื่นแทนจะเกิดอะไรขึ้น เพื่อให้เข้าใจการทำงานของมันมากขึ้น ผลก็คือ Error 404 (Not Found)!!1 เนื่องจากไม่ตรงเงื่อนไข มันจึง วิ่งไปหา google ซึ่ง google ก็จะบอกว่า แกยิงอะไรมาหาฉันนนนนนน ทำนองนี้
ซึ่งถ้าหากท่านเปลี่ยนจาก google เป็น service “Y” ซะเพียงเท่านี้เวลาท่านต้องการยิงไปเทสระบบจริง ๆ ท่านก็เพียงแค่ไม่ต้องใส่ prefix “XY” หรือ “FF” ให้มันมันก็จะวิ่งไปที่ service “Y” แล้ว
ท่านสามารถ clone git เพื่อไปลองได้ที่นี่ exampleMountebank
จากตัวอย่างข้างต้น เพื่อน ๆ ก็น่าสามารถ mock service กันได้แล้วนะครับ
ลองนำไป adapt ดู อันที่จริง mountebank มันยังทำอะไรได้อีกเยอะ ลองไปศึกษาต่อกันได้ที่ official website ของเขาครับ
หวังว่าบทความนี้จะมีประโยชน์ ต่อผู้ที่ประสบปัญหาทำนองนี้อยู่นะครับ