เมื่อ Token ตัวร้ายหมดอายุ ตอนที่ 2 (Refresh Token in Android with Retrofit Part 2)
ถึงแม้เราจะได้ผ่านพ้นช่วงเวลาในการ implement มาแล้ว แต่งานของเรานั้นยังไม่หมดเพราะว่าในตอนนี้เราจะมาเขียน Test ให้กับเจ้า Interceptor กันต่อ
ส่วนใครยังไม่ได้อ่านตอนแรกกลับไปอ่านได้ที่นี่ครับ
Setup
เอาล่ะ มาเตรียมวัตถุดิบในการ Test กันก่อน โดยสิ่งที่เราจะใช้ก็คือ
- Mockito
- OkHttp Mock Web Server
- Retrofit Mock
- Gson
testImplementation 'org.mockito:mockito-core:3.2.4'
testImplementation 'com.squareup.okhttp3:mockwebserver:4.2.2'
testImplementation 'com.squareup.retrofit2:retrofit-mock:2.6.2'
implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
It’s Test time!
1. Mock Refresh Token Service
อย่างที่รู้กันว่าภายใน Interceptor ของเราจะต้องมีการยิง Refresh Token ด้วย เราจึงต้องสร้าง Mock ของ Refresh Token Service ขึ้นมาเตรียมเอาไว้ก่อนเพื่อรอวันที่จะใส่เข้าไปใน Interceptor อาจจะใส่เข้าไปผ่านทาง Constructor หรือจะ Inject เข้าไปก็ตามสะดวกเลย แต่ตอนนี้เรามา mock กันก่อนนะครับ
val refreshTokenService = mock(RefreshTokenService::class.java)
พอเราสร้างตัว mock แล้วเราก็กำหนดให้มัน return response ของ refresh token ออกมาแบบนี้ครับ
`when`(refreshTokenService.refreshToken()).thenReturn(Calls.response(RefreshTokenResponse()))
จะเห็นว่าผมให้มัน return เป็น Calls
ซึ่งโดยปกติแล้วเวลาเราเรียก service โดยใช้ Retrofit มันจะ return Call
เพื่อรอวันที่จะโดนเรียก enqueue หรือ execute แล้วรับค่าจาก service จริงๆ แต่ว่า Calls เนี่ยมันคือตัว mock ของ Call ครับ อยู่ใน Retrofit Mock ที่เราใส่มานั่นแหละ มันต่างจาก Call ตรงที่เราสามารถกำหนดได้เองเลยว่าตอนที่มันถูกเรียก enqueue หรือ execute แล้วจะให้ค่าที่ได้กลับมาเป็นอะไร
2. Create Mock Web Server
TLDR;
สำหรับคนที่ยังไม่รู้จัก Mock Web Server นะครับ มันก็คือการที่เราสร้าง Web Server จำลองขึ้นมา เราสามารถกำหนด URL ให้มันได้ กำหนดได้ว่าตอนที่เราเรียก service จะให้ return อะไรกลับไป และที่สำคัญก็คือเราสามารถกำหนดได้เลยว่าจะให้มันผ่าน Interceptor อะไรบ้างในสร้างเรียก service จำลองนี้ นี่ล่ะเหตุผลที่เราจะใช้เจ้านี่ในการ Test
แน่นอนว่าเราต้องเริ่มจากสร้าง object MockWebServer
ขึ้นมา แล้วจากนั้นก็กำหนดว่า server จะ return อะไรให้บ้างโดยใช้คำสั่ง enqueue()
หากเราใส่ enqueue หลายครั้ง server จะ return ค่าตามลำดับที่ถูกเรียก เช่น เรียก server ครั้งแรกจะ return ค่าของ enqueue อันแรก พอเรียกอีกก็จะไล่ตามลำดับ
จาก code จะกำหนดให้การเรียก server ครั้งแรก return เป็น 401 Token Expired จากนั้นเมื่อถูกเรียกอีกครั้งถึงจะ return ค่าที่ success จริงๆ เพราะเราต้องการให้ flow เป็นแบบนี้
call server (1st) → return 401 → Interceptor do refresh token → Interceptor call server again → call server (2nd) → return 200
3. Create OkHttpClient and Request
เราได้ mock ฝั่ง server กันไปแล้วต่อไปเราจะสร้างฝั่ง client เพื่อจะเอาไว้เรียก server กันบ้าง
คราวนี้ถึงเวลาที่ Interceptor ของเราจะออกโรงแล้ว ผมสร้าง TokenExpiredInterceptor พร้อมกับใส่ refreshTokenService ที่เรา mock เอาไว้ แล้วใส่ interceptor เข้าไปใน client ของเรา
พอเราสร้าง client แล้วเราก็สร้าง request แล้วยัดเข้าไปใน client อีกที
เท่านี้ client ของเราก็พร้อมเรียก mock server ของเราแล้ว
4. Call the Mock Server and Verify Result
ต่อจากข้างบนเลยนะครับ เราได้สร้าง call จาก OkHttpClient
กันไว้แล้ว เราก็สั่ง execute()
ได้เลย! แล้วก็เอาผลลัพท์ที่ได้มา assert ตามสะดวกเนอะ จากนั้น shutdown server แล้วก็พนมมือขึ้นมาบูชาสิ่งศักดิ์สิทธิ์ขอให้ผ่านไปได้ด้วยดี สาธุ~ เท่านี้ก็เป็นอันเรียบร้อยแล้วครับ
refreshToken()
จริงๆผลลัพท์การ test จะเป็นประมาณนี้นะครับ จะเห็นว่าเราจะได้ response สองครั้ง ครั้งแรกเป็น 401 แล้วครั้งที่สองเป็น 200 ตามที่เราต้องการ
และนี่ก็เป็น code ทั้งหมดครับ
Conclusion
ในการ test Interceptor นั้นเราจะใช้ Mock Web Server ช่วย เพื่อที่เราจะได้เลือกทดสอบ Interceptor ของเราได้ แล้วเราก็สามารถวาง flow ของการ test ได้ด้วย เช่น ให้ส่ง Request อะไรไป ผ่าน Interceptor อะไรบ้าง แล้วได้ Response อะไรกลับมา แล้วใช้ Mockito ช่วย mock ของอื่นๆ เพิ่มเติม
ก็จบกันไปอีกบทความนะครับสำหรับการ test Interceptor ด้วย Mock Web Server แต่ว่ามันเอาไปประยุกต์ใช้ประโยช์ได้หลายอย่างนะครับ เราอาจจะเขียน Interceptor ที่แก้ไข request ข้างในก่อนส่งไปยัง server ตอน test เราก็สามารถดึง request ที่แก้แล้วออกมาดูอะไรอะไรแบบนี้
ขอพระเจ้าอวยพรให้กับการเขียน Test ของคุณ แล้วไว้เจอกันใหม่ครับ