DI for test
Published: 2020-12-06 10:59:58

บทความนี้เป็นบันทึกที่จดไว้เพื่อแชร์สิ่งที่รู้ลงไปถ้าผิดอะไรช่วยกันคอมเม้นไว้ได้นะครับ

วันนี้ผมมาในหัวข้อเรื่อง dependency injection แต่ก่อนหน้านี้หัวข้อมันคือ Structure สำหรับการ mock เพื่อ test ที่ชื่อหัวข้อแบบนี้เพราะคิดไม่ออก

สิ่งที่เราจะได้รับจากบทความนี้คือ Loose Coupling และ Dependency Injection มีความสำคัญกับการ mock เพื่อ test ยังไง ผมเป็นคนหนึ่งที่เคยพยามที่จะเขียน test ต่างๆ แต่ติดปัญหา
- function นี้เป็น private เราจะ test ยังไงควรเปลี่ยน pubilc สำหรับ test ไหม
- แล้วทำไมเราต้องทำ dependency injection ในเมื่อเราสามารถประกาศตัวแปลใช้ได้ตรงๆ ได้

โดย ผมจะไม่สนใจว่าการ test นี้จะเป็นการเทสแบบไหน unit หรือ integration เอาแค่ concept

#สารบัญ
0. [เกรินนำ](#chapter-0)
1. [Tight Coupling](#chapter-1)
2. [Loose Coupling & Dependency injection](#chapter-2)
3. [การ mock เพื่อ automate test](#chapter-3)
4. [สรุป](#chapter-4)

#Loose coupling และ Dependency injection คืออะไร <a name="chapter-0"></a>
Loose coupling คือถ้าเราไปแปลใน google มันจะแปลว่า "ข้อต่อหลวม" มันคือการเขียนโปรแกรมให้ dependency ของ class หลุดออกจากกันเพื่อ mock data เช่น

##โจทย์
ถ้าผมอยากจะเขียน api get User by status
http method : GET 
path : /user/status
ผมต้องการ การ get user by status จาก database
สิ่งที่ผมวางไว้คือ userRepository คือตัวจัดการเอาของออกจาก database
ส่วน userService คือตัวที่จะนำมาคำนวนต่างๆ (ตัวอย่างขอไม่มีการคำนวน)
##project structure
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/6sl8b1q2n3zaswxejums.png)

#Tight Coupling <a name="chapter-1"></a>
เป็นการสร้าง dependency ที่ผูกกันติดจนยากที่จะ mock userRepository getUserByStatus เพราะเกิดการ new ใน class
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nannikycarbe0kp164b5.png)

เผื่อมองภาพไม่ออก การที่ userRepository ถูก new ใน class userService มันทำให้เราเข้าไปแก้ไขอะไรในนั้นได้ยาก
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/i2b1yn678irs7tm6e0rd.png)

#Loose Coupling <a name="chapter-2"></a>
จากตัวอย่างด้านบนถ้าเราเอา userRepository มาใส่ใน constructor เป็น parameter มันจะเกิดการ Loose coupling เพราะ userRepository ไม่ได้ขึ้นอยู่กับ class UserService การส่งสิ่งที่เราจะใช้ผ่าน constructor ถือเป็นการทำ Dependency injection ผ่าน constructor

![ใน typescript เอา private ไปใส่ใน constructor มันจะกลายเป็น 
property ของ class](https://dev-to-uploads.s3.amazonaws.com/i/es6kemuc3z78k4e4uob3.png)
ใน typescript เอา private ไปใส่ใน constructor มันจะกลายเป็น property ของ class เลย

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/kldritz7qgtv04kki1c0.png)
สิ่งที่เกิดขึ้นคือ เราสามารถที่จะแก้ไข class userRepository ก่อนจะส่งเข้าไปได้

# การ mock เพื่อ test <a name="chapter-3"></a>
(mock คือการสร้างของจำลองไว้ test)
จากด้านบนเราจะเห็นแล้วว่าการที่เราจะ mock ของนั้นจะต้องมีโครงสร้างของโปรแกรมที่ดีพอที่จะทำให้เรา mock ได้ Loose Coupling 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/345zaw2u2fhzo8vs8xkq.png)

ตัวอย่างนี้จะใช้ jest นะครับ
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/brsf8ceyll7mcp8uc5xk.png)
ผม mock getCountUserByStatus ให้ return 20 มาเลย
ถ้าใน service มีการคำนวนอะไรเราก็จะสามารถ expect ค่านั้นได้เลย

#สรุป <a name="chapter-4"></a>
การ mock เพื่อ test นั้นจำเป็นต้องวางโครงสร้างของโปรแกรมให้ดีเพื่อที่การ mock เพื่อ test จะได้ทำได้ง่าย ไม่ว่าจะเป็นการแยก class ให้ดี หรือการทำ dependency injection ก็ตามถ้าเรา loose coupping ก็ไม่จำเป็นต้องเปลี่ยน function หรือ property เป็น public เพื่อเทส (เพราะมันไม่ควรทำ)

ผิดถูกยังไง comment ได้นะครับ