TDD is not the f*ucking red, green and blue Part 3

Jassadakorn.ket
te<h @TDG
Published in
3 min readJun 3, 2022

มาถึง part นี้ทุกคนอาจจะสงสัยว่าเอ๊ะ มึงมันทำได้จริงหรอวะ โจทย์ที่ยกตัวอย่างมันก็ดูง่ายเวลาใช้ทำงานจริงมันไม่ได้นะ ก็ขอตอบว่าทำได้จริงดิ ก็ตอนทำงานอยู่ทุกวันนี้ใช้อยู่ตลอด ถ้าไม่เชื่อก็มาสมัครงานที่เดียวกันนะเดี๋ยวทำให้ดู

  1. มึง…….กูรู้สึกว่างเปล่าเมื่ออยู่ในหน้าจอ test ( 2 สิ่งที่ต้องนึกถึง )
  2. ชิบหายใช้เวลาเยอะมากกว่าจะเขียนได้ 1 case ( ช้าไม่เป็นก็เร็วไม่ได้ )

ในตอนที่แล้วเราค้างกันไว้ที่ตรงนี้เนอะ ที่เราออกแบบไว้

ซึ่งเมื่อเราออกแบบโค๊ดของเราเรียบร้อยแล้ว ก็ได้เวลาไป implement ของจริงกัน

แต่ว่าระหว่าง implement
ระวัง…….!!!!!!!!!!!!!!

What the fuck รันกี่ครั้งทำไมแม่งไม่เหมือนเดิม ( 3 สิ่งต้องห้าม )

จากประสบการณ์ของผมแล้วจะมี 3 สิ่งต้องห้าม ที่เราไม่ควรนำไปใส่ไว้ใน sut หรือ System Under Test หรือไอเจ้า ProductViewModel ดังนี้ เพื่อให้ผล test มันเหมือนเดิมตลอด

  1. UI ( ก็เมื่อกี้สร้างแยกไปแล้วไง ถ้าจะ test UI ไปทำ UI Test นู่นเลย)
  2. Singleton ( สร้างไว้ไม่รู้มันจะตายห่าเมื่อไหร่ จะดูของข้างในก็ลำบาก )
  3. Dependencies อื่นๆ ( หมายถึงพวก class ที่เราไม่สามารถเข้าไปควบคุมค่าต่างๆได้ )

เมื่อเข้าใจดังนี้แล้ว ขั้นตอนต่อไปคือเตรียมหน้าจอของเราให้พร้อมโดยหา icon นี้ที่มุมขวาบนของ Xcode แล้วกดไป 1 ที ( หากยังไม่เข้าใจด้านบนไม่เป็นไรมีเฉลยอยู่ )

ก็จะได้หน้าจอตามนี้

โดยด้านซ้ายเราจะเปิด test อยู่เสมอ

ส่วนด้านขวาจะเป็น production code

แต่ production code ยังไม่ได้สร้าง ก็ไปสร้างซะเริ่มจาก ProductModel กันก่อนตามที่เราออกแบบไว้เลย

อย่าลืม testable import Product ด้วยเพราะมันจะไม่เห็น class ถ้าเราไม่ทำ
สังเกตุว่า test ของเราเริ่มเขียวแล้วยินดีด้วยสายเขียวทั้งหลาย

จากประสบการณ์ของผมแล้ววิธีนี้จะทำให้เรา focus กับ test มากกว่าการสลับหน้าจอไปมาซึ่งมันทำให้การทำ TDD ช้าลงอย่างมาก ถ้ามีสองจอให้แยกคนละจอไปเลย

จากนั้นให้ไปสร้าง class ProductViewModel ตามที่ออกแบบไว้

จากภาพด้านบน ลองทดสอบ return array ว่างเปล่ากลับไป ผลคือ AssertEqual ไม่ผ่านเพราะว่าเรายังไม่ได้เอา given หรือ productList ในบรรทัดที่ 18 ไปใช้เลย

ก่อนที่จะไปถึงตรงนั้นเราลองมาแหกกฎกันมั้ย ลองเอา 3 สิ่งต้องห้ามนี้ไปใส่ไว้ใน sut หรือ viewModel กัน

  1. ลองเอา UI เข้าไปใส่ดูดีมั้ยนะ

จากภาพด้านบน ลองใส่ UIView ที่มีขนาดเท่ากับหน้าจอ เข้าไปใน viewModel จากนั้น return size ออกมาเพื่อ AssertEqual ใน test ผลคือผ่านแต่ แต่ แต่

ลองสลับ device เป็น ipad ดู

ผลที่ได้คือพังครับ เพราะว่าอะไร? เพราะว่า UI มันเปลี่ยนแปลงไปตามขนาดของ device สมมุติ view ที่ผมเอาเข้ามามันเป็น autolayout เนอะ มันก็จะอาจจะแสดงผลผิดเพี้ยนไปมากกว่านี้ก็ได้ทำให้ test ของเรารันหลายครั้งผลอาจจะไม่เหมือนเดิม
ดังนั้นพยายามหลีกเลี่ยงซะ

2. ลองแหกกฎด้วย singleton ดูบ้างด้วย class LoginManager

จากภาพด้านบน class LoginManager ทำหน้าที่เก็บ state ว่า login หรือยังโดยมี function ให้ login และ logout เพื่อสลับ state การ login

แต่เมื่อเราลองใช้ function login ดูผลที่ได้ก็ยัง fail อยู่ เพราะอะไร ?
เพราะว่า function login() ต้องใช้เวลา 5 วินาทีในการ process แต่ test ของเรามันจบไปก่อนแล้วทีนี้มันก็เลยเอา state ที่เป็น true มาใช้ไม่ทัน

มีหลายคนบอกว่าใช้ WaitForExpectations แล้ว set timeout ซัก 6 วินาที
ก็จะบอกอย่างนี้ว่าถ้ารอนานขนาดนั้นก็กลายเป็น integration test แล้วโว้ยยย

3. สุดท้ายลองแหกอีกทีด้วย Dependencies อื่น

จากภาพด้านบน ProductAPI ถูกสร้างขึ้นเพื่อ get data จาก api เพื่อส่งไปให้ ProductViewModel ต่ออีกที แต่ api นี้มันไม่มีอยู่จริงผมก็เลยส่ง completion 10 item ออกไปเลย ซึ่งถ้า api นี้มันอยู่จริงล่ะ?

มันก็ต้องใช้เวลาในการ process อาจจะเป็น 2 วิ 3 วิ หรือ 4 วิ ซึ่งมันก็จะคล้ายกับเคส singleton ที่ test จะจบก่อนที่ api จะเสร็จซึ่งผลที่ได้มันอาจจะผิด หรือไม่เหมือนเดิมขึ้นอยู่กับ server ว่าจะเป็นยังไง

จากภาพด้านบนลองใส่ item 20 อันก็ยังผ่านเลยเอ๊ะยังไงนะ? เป็นไปได้ไงนะ?

อ่าว ?????????
ถ้าไม่ให้ใส่ Singleton ลงไป
ถ้าไม่ให้ใส่ API ลงไป

แล้วจะทำยังไงให้ดึงข้อมูลได้ล่ะ เดี๋ยวตอนหน้ามาต่อกัน

ขอสรุปว่า ในขั้นตอนที่เรามักเรียกว่าทำให้เขียวก็คือ การ implement ตาม test ตาม spec ที่เราออกแบบไว้ ซึ่งให้ระวังอย่าให้มี 3 สิ่ง UI, Singleton, Dependencies เพราะอาจจะทำให้ผลที่ได้ผิดเพี้ยนไปจากที่เราต้องการ

--

--