Implementing Service Tests (การ Implement Service Test)

การ Implement unit test นั้นค่อนข้างง่ายเมื่อมองในภาพรวม และมีเอกสารมากมายที่อธิบายวิธีเขียนมัน ส่วน service test และ end-to-end test เป็นสิ่งที่น่าสนใจกว่า โดยเฉพาะในบริบทของ microservices ดังนั้นเราจะโฟกัสไปที่สิ่งเหล่านั้นต่อไป

service test ของเราต้องการทดสอบส่วนหนึ่งของฟังก์ชันการทำงานทั่วทั้ง microservice และเฉพาะ microservice นั้นเท่านั้น ดังนั้นถ้าเราต้องการเขียน service test สำหรับ Customer microservice จาก Figure 9-3 เราจะ deploy instance ของ Customer microservice และอย่างที่คุยกันไปก่อนหน้านี้ เราต้องการ stub Loyalty microservice ออกไป เพื่อให้มั่นใจได้ดีขึ้นว่าความล้มเหลวของเทสต์สามารถจับคู่กับปัญหาของ Customer microservice เองได้

อย่างที่เราสำรวจใน Chapter 7 เมื่อเรา check in ซอฟต์แวร์ของเรา สิ่งแรกๆ ที่ automated build ของเราจะทำคือสร้าง binary artifact สำหรับ microservice ของเรา ตัวอย่างเช่น การสร้าง container image สำหรับซอฟต์แวร์เวอร์ชันนั้น ดังนั้นการ deploy จึงค่อนข้างตรงไปตรงมา แต่เราจะจัดการกับการปลอม downstream collaborator อย่างไร

service test suite ของเราต้อง stub downstream collaborator ออกไป และตั้งค่า microservice ที่กำลังถูกทดสอบให้เชื่อมต่อกับ stub service เหล่านั้น จากนั้นเราต้องตั้งค่า stub ให้ส่ง response กลับมาเพื่อเลียนแบบ microservice จริง

Mocking or Stubbing (Mock หรือ Stub)

เมื่อ ผมพูดถึงการ stub downstream collaborator ผมหมายถึงการที่เราสร้าง stub microservice ที่ตอบสนองด้วย response ที่เตรียมไว้ล่วงหน้าต่อ request ที่รู้จักจาก microservice ที่กำลังถูกทดสอบ ตัวอย่างเช่น ผมอาจบอก stub Loyalty microservice ของผมว่าเมื่อถูกถามหายอดคงเหลือของลูกค้า 123 ให้คืนค่า 15,000 เทสต์ไม่สนใจว่า stub จะถูกเรียก 0, 1 หรือ 100 ครั้ง อีกรูปแบบหนึ่งคือการใช้ mock แทน stub

เมื่อใช้ mock ผมจะไปไกลกว่านั้นอีก โดยตรวจสอบให้แน่ใจว่ามีการเรียกเกิดขึ้นจริง ถ้าการเรียกที่คาดหวังไม่เกิดขึ้น เทสต์จะล้มเหลว การ implement แนวทางนี้ต้องการความฉลาดมากขึ้นใน fake collaborator ที่เราสร้างขึ้น และถ้าใช้มากเกินไปก็อาจทำให้เทสต์เปราะบางได้ อย่างไรก็ตาม อย่างที่กล่าวไว้ stub ไม่สนใจว่าจะถูกเรียก 0, 1 หรือหลายครั้ง

แต่บางครั้ง mock ก็มีประโยชน์มากในการยืนยันว่า side effect ที่คาดหวังเกิดขึ้นจริง ตัวอย่างเช่น ผมอาจต้องการตรวจสอบว่าเมื่อผมสร้างลูกค้าใหม่ ยอดคะแนนสะสม ใหม่ถูกตั้งค่า ให้ลูกค้ารายนั้น ความสมดุลระหว่างการเรียก stubbing กับ mocking นั้นละเอียดอ่อน และมีความเสี่ยงพอๆ กันทั้งใน service test และ unit test แต่โดยทั่วไปแล้ว ผมใช้ stub มากกว่า mock อย่างมากสำหรับ service test สำหรับการพูดคุยเชิงลึกเกี่ยวกับการแลกเปลี่ยนนี้ ลองดู Growing Object-Oriented Software, Guided by Tests โดย Steve Freeman และ Nat Pryce 3

โดยทั่วไปแล้ว ผมแทบไม่ใช้ mock สำหรับการทดสอบประเภทนี้ แต่การมีเครื่องมือที่สามารถ implement ได้ทั้ง mock และ stub นั้นมีประโยชน์

แม้ผมจะรู้สึกว่า stub และ mock นั้นถูกแยกความแตกต่างได้ค่อนข้างดีแล้ว แต่ผมก็รู้ว่าความแตกต่างนี้อาจทำให้บางคนสับสนได้ โดยเฉพาะเมื่อบางคนโยนคำศัพท์อื่นๆ เข้ามาด้วย เช่น fakes , spies และ dummies Gerard Meszaros เรียกสิ่งเหล่านี้ทั้งหมด รวมถึง stub และ mock ว่า “Test Doubles”

A Smarter Stub Service (Stub Service ที่ฉลาดขึ้น)

โดยปกติแล้ว สำหรับ stub service ผมมักจะสร้างมันขึ้นมาเอง ผมใช้ทุกอย่างตั้งแต่ Apache web server หรือ nginx ไปจนถึง embedded Jetty container หรือแม้แต่ Python web server ที่ launch ผ่าน command-line เพื่อ launch stub server สำหรับ test case แบบนี้ ผมคงทำงานซ้ำแบบเดิมซ้ำแล้วซ้ำเล่าในการสร้าง stub เหล่านี้ Brandon Byars เพื่อนร่วมงานเก่าที่ Thoughtworks ของผม อาจช่วยประหยัดงานให้พวกเราหลายคนได้มากด้วย stub/mock server ของเขาที่เรียกว่า mountebank

คุณสามารถมองว่า mountebank เป็นเหมือน software appliance ขนาดเล็กที่ programmable ผ่าน HTTP ความจริงที่ว่ามันเขียนด้วย NodeJS นั้นไม่มีผลอะไรเลยกับ service ที่เรียกใช้มัน เมื่อ mountebank launch คุณจะส่งคำสั่งบอกให้มันสร้าง "imposter" หนึ่งตัวหรือมากกว่า ซึ่งจะตอบสนองบน port ที่กำหนดด้วย protocol เฉพาะ (ปัจจุบันรองรับ TCP, HTTP, HTTPS และ SMTP) และกำหนดว่า imposter เหล่านี้ควรส่ง response อะไรเมื่อมี request ส่งเข้ามา มันยังรองรับการตั้ง expectation ถ้าคุณต้องการใช้มันเป็น mock ด้วย เพราะ mountebank instance เดียวสามารถรองรับการสร้าง imposter ได้หลายตัว คุณจึงสามารถใช้มัน stub downstream microservices หลายตัวได้

mountebank มีประโยชน์นอกเหนือจาก automated functional testing ด้วย ตัวอย่างเช่น Capital One ใช้ mountebank แทนที่ mocking infrastructure เดิมสำหรับ performance test ขนาดใหญ่ของพวกเขา 4

ข้อจำกัดหนึ่งของ mountebank คือมันไม่รองรับการ stub protocol แบบ messaging ตัวอย่างเช่น ถ้าคุณต้องการมั่นใจว่า event ถูกส่งอย่างถูกต้อง (และอาจถูกรับด้วย) ผ่าน broker คุณจะต้องมองหาโซลูชันที่อื่น นี่เป็นจุดหนึ่งที่ Pact อาจช่วยได้ ซึ่งเป็นสิ่งที่เราจะดูเพิ่มเติมในไม่ช้า

ดังนั้นถ้าเราต้องการรัน service test สำหรับ Customer microservice ของเราเท่านั้น เราสามารถ launch Customer microservice และ mountebank instance ที่ทำหน้าที่เป็น Loyalty microservice ของเราบนเครื่องเดียวกันได้ และถ้าเทสต์เหล่านั้นผ่าน ผมก็สามารถ deploy Customer service ได้ทันที! หรือว่าทำได้จริงหรือ แล้ว service ที่เรียก Customer microservice อย่าง helpdesk และ web shop ล่ะ เรารู้หรือไม่ว่าเราได้ทำการเปลี่ยนแปลงที่อาจทำให้พวกมันพัง แน่นอนว่าเราลืมเทสต์สำคัญที่อยู่บนสุดของ pyramid ไป นั่นคือ end-to-end test