Mock service ของคุณ หรือ ของคนอื่นด้วย Mountebank กันเถอะ

Sakul Montha
4 min readDec 26, 2017

--

mountebank logo

ปัจจุบันนี้ เวลาที่เรา 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
default port 2525
ถ้าเปิดมาแล้วเจอ สวัสดีเพื่อน แบบนี้ถือว่าใช้ได้แล้วครับ

ผมจะมาลอง Demo ให้ดูครับ กรณีนี้เราจะทำการ create user จาก service “X” ยิงไป create user ที่ service “Y” แต่เนื่องจาก service “Y” อาจจะยัง implement ไม่เสร็จ หรือ การ create user จำเป็นต้องใช้เลขบัตรประชาชนจริง เพราะว่า service “Y” มีการ verify เลขบัตรประชาชนเอาไว้ เราจึงจำเป็นต้อง mock service “Y” เพื่อให้เรายิงได้ตลอดกาล (ไม่ได้หมายความว่าเราจะไม่ Test service “Y” จริง ๆ นะครับ)

สร้าง Project ขึ้นมาวาง structure ดังนี้
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
หากท่านเขียนถูก เมื่อทำการ run ควรจะได้ดังนี้
จากนั้นให้ท่านลองเปิด browser ทำการยิงไปที่ url http://localhost:6969/customer/v1/profiles/create
จะขึ้นหน้า error 404 ของ Google

จะขึ้นหน้า error 404 ของ Google แต่ !! ไม่ต้องตกใจ เราตั้งใจทำแบบนั้นเอง เนื่องจากที่เรา request ไปไม่ตรงกับเงื่อนไขทั้งหมด เช่นที่เราทำไว้เรารับ method post แต่ที่เรา request ไปตรง ๆ นี่มันเป็น method get เมื่อไม่เข้าเงื่อนไข มันก็จะไม่เข้า mock มันจะโดดไปหา google ตามที่ได้ตั้งค่าไว้ที่ “proxy.ejs”

เรามาลองยิงให้เข้า success case กัน

ให้ท่านเปิด Postman หรือ Insomnia REST เอาตามที่ท่านถนัด ในตัวอย่างจะเป็น postman นะครับ

ให้ท่านเขียน request ดังนี้
response ที่ได้จะเป็น header success 200 และมี body ดังภาพ

ทีนี้มาลองยิงให้เข้า mock ตัว error กันบ้าง

ให้ท่านเขียน request ดังนี้
response ที่ได้จะเป็น header success 200 และมี body ดังภาพ

ที่ผ่านมาเราลองยิงไปแบบ 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 ของเขาครับ
หวังว่าบทความนี้จะมีประโยชน์ ต่อผู้ที่ประสบปัญหาทำนองนี้อยู่นะครับ

--

--

Sakul Montha
Sakul Montha

Written by Sakul Montha

Chief Product Officer, a man who’s falling in love with the galaxy.

Responses (2)