หลังจากที่ไปร่วมงาน ThougthWorks Talks Tech 
เรื่อง Sanely Grow your Microservices with Consumer-Driven Contract
จึงทำการสรุปและขยายความของ Consumer-Driven Contract 
เพื่อให้เข้าใจว่าเป็นมาอย่างไร
และมีความแตกต่างจากการทดสอบแบบอื่น ๆ อย่างไร

เริ่มต้นด้วยระบบงานนั้น มีหลายส่วนงานต้องทำงานร่วมกัน

ดังนั้นการทดสอบว่าแต่ละส่วนงาน
สามารถทำงานร่วมกันได้อย่างถูกต้อง เป็นไปตามที่คาดหวัง
จึงเป็นเรื่องที่สำคัญและจำเป็นอย่างมาก
เพื่อไม่ให้เกิดข้อผิดพลาดบน production server

ประเด็นที่สำคัญคือ เราจะทำการทดสอบอย่างไรได้บ้าง ?

จาก slide เรื่อง Testing Strategies in a Microservice Architecture 
แบ่งการทดสอบออกเป็น 5 แบบคือ

  1. Unit test
  2. Integration test
  3. Component test
  4. Contract test
  5. End-to-End test

การทดสอบที่น่าสนใจและมักทำให้สับสนมีดังนี้

1. Integration testing

เป็นการทดสอบระหว่างผู้ให้บริการ (Provider) และผู้ใช้บริการ (Consumer)
เพื่อทำให้มั่นใจว่า provider นั้นส่งผลลัพธ์ (Response)
ตามคำร้องขอ (Request) ที่ส่งมาจาก consumer
ทำการกำหนดรูปแบบของ request และ response ไว้อย่างชัดเจน (Pre-defined)
สิ่งที่เตรียมไว้จะถูกเรียกว่า contract

แสดงขอบเขตการทดสอบแบบ Integration ไว้ดังรูป

https://martinfowler.com/articles/microservice-testing/#testing-integration-diagram

2. End-to-End testing

เป็นการทดสอบผ่าน interface ต่าง ๆ ของ provider และ consumer
จะมีการทำงานร่วมกันระหว่าง ส่วนการทำงานมากขึ้น
หรือเรามักจะเรียกว่า dependency 
ส่งผลให้การทดสอบชนิดนี้
ต้องใช้เวลาในการเตรียมและทดสอบนานมาก ๆ
ดังนั้นชุดการทดสอบแบบนี้จึงต้องมีเท่าที่จำเป็นเท่านั้น
เน้นไปที่พฤติกรรมหลักของผู้ใช้งานเป็นหลัก

โดยที่ interface ของการทดสอบอาจจะเป็น User Interface
เช่น Web browser และ mobile เป็นต้น
หรืออาจจะเป็น API เช่น WebService และ RESTFul API เป็นต้น
แน่นอนว่า
ต้องมีการกำหนด contract ระหว่าง consumer กับ provider ไว้เช่นเดิม
แต่เป็น contract ผ่าน interface นั่นเอง

แสดงขอบเขตของการทดสอบดังรูป

https://martinfowler.com/articles/microservice-testing/#testing-end-to-end-diagram

3. Component testing หรือ Mock/Stub testing

ปัญหาหลักของการทดสอบแบบ integration และ end-to-end
คือ การทดสอบบน environment จริง หรือทำการทดสอบกับ provider จริง ๆ
ส่งผลให้การจัดการและเตรียมสำหรับการทดสอบยาก
แถมบ่อยครั้งยังมีปัญหามากมาย
ทั้งข้อมูลที่เปลี่ยนแปลงตลอดเวลา
ทั้งการเชื่อมต่อมีปัญหา เช่นระบบ network ล่มเป็นต้น
ทั้งไม่สามารถทดสอบซ้ำ ๆ ได้เร็วตามที่ต้องการ

ดังนั้นจึงเป็นที่มีของการจำลองฝั่ง provider ขึ้นมา
บ่อยครั้งเราจะเรียกว่าการ Mock หรือ Stub provider
ทำให้เราสามารถทดสอบในแต่ละส่วนงานได้ง่าย
แถมทดสอบซ้ำแล้วซ้ำเล่าได้ตามที่ต้องการ
ทั้งในกรณี success และ failure

การทดสอบในลักษณะนี้ จะเรียกว่า Component testing
ซึ่งมีความใกล้เคียงกับ integration test อย่างมาก
แต่ต่างกันที่เป้าหมายของมันนั่นเอง

แสดงขอบเขตของการทำงานดังรูป

https://martinfowler.com/articles/microservice-testing/#testing-component-in-process-diagram

4. Contract testing

จากการทดสอบทั้ง 3 แบบที่ผ่านมานั้น
สิ่งที่มีเหมือนกันคือ contract
หรือชุดการทดสอบที่ประกอบไปด้วย  Request จากฝั่ง consumer และ response ที่ได้รับจาก provider

ปัญหาที่ตามมาคือ 
เราจะมั่นใจได้อย่างไรว่า contract เหล่านี้
ยังคงเป็นไปตามที่เราคาดหวัง และถูกต้องเสมอ

ยกตัวอย่างเช่น จาก Component testing นั้น
เราทำการ mock provider ขึ้นมา
ถ้า response จากฝั่ง provider จริง
มีการเปลี่ยนแปลง เราจะทำอย่างไร ?

ถ้าไปตรวจสอบหรือสร้าง contract ระหว่าง consumer กับ provider แบบแปะ ๆ
ก็จะเป็น integration test ไป ซึ่งก็เกิดปัญหาเดิม ๆ
ดังนั้นถ้าเราทำการบันทึก contract ของ provider ใหม่เมื่อมีการเปลี่ยนแปลง
และตอนทดสอบให้จำลอง provider จาก contract นั้น ๆ ขึ้นมา
น่าจะมีประโยชน์กว่าไหม

รวมทั้งการตรวจสอบ response จาก provider นั้น
ไม่จำเป็นต้องตรวจสอบข้อมูลแบบเป๊ะ ๆ
แต่ทำการตรวจสอบในเครื่องของโครงสร้างข้อมูลที่ควรจะเป็น
หรือเฉพาะข้อมูลที่สนใจและสำคัญเท่านั้น
การทดสอบแบบนี้จึงเรียกว่า Contract testing

https://martinfowler.com/bliki/ContractTest.html

การสร้าง contract นั้นจะทำบน consumer และ provider จริง ๆ
โดยเริ่มจากการบันทุกหรือ record contract ขึ้นมาก่อน
จากนั้นจึงทำการทดสอบผ่าน contract นี้เท่านั้น

ที่สำคัญเราสามารถนำ contract ของการทดสอบแบบนี้
มาทดสอบหรือจำลองขึ้นมาตาม contract ที่สร้างขึ้นมาจาก provider จริง ๆ ได้อีกด้วย แสดงดังรูป

เมื่อ provider มีการเปลี่ยนแปลง
จะทำการตรวจสอบหรือ verify ว่า response จาก provider
ในแต่ละ request ของ contract ต่าง ๆ ตรงกันหรือไม่
ถ้าไม่ก็จะ fail ซึ่งเราจะรู้ว่า การเปลี่ยนแปลงนั้นทันที
จากนั้นก็ทำการบันทึก contract ไหม่ต่อไป
ซึ่งตรงนี้เราสามารถ integrate ไปกับระบบ Continuous Integration ได้เลย
แสดงดังรูป

Library ของ Contract testing นั้น มีดังนี้

จะเห็นได้ว่า การทดสอบแต่ละแบบนั้น

เข้ามาช่วยเติมเต็ม หรือ ช่วยแก้ไขปัญหาที่เกิดจากการทดสอบ
เพื่อเสริมสร้างความมั่นใจต่อระบบ
เพื่อเสริมสร้างการพูดคุยและทำงานร่วมกันของคนในส่วนต่าง ๆ
สุดท้าย
การทดสอบจำเป็นจะต้องมีความน่าเชื่อถือและทำงานได้อย่างรวดเร็วนั่นเอง

วันนี้เราทำการทดสอบแล้วหรือยัง