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

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

จากตอนที่แล้วเรารู้แล้วว่าเจ้า Spec เนี่ยเป็นตัวคอยนำทางให้เราว่าจะทำอะไรบ้างคร่าวๆ แล้วรายละเอียดข้างในล่ะเห้ย มีแค่ function เปล่าๆจะทำอะไรได้
แล้วยังไงต่อ ซักทีเหอะ ถ้า spec คือพระเอก แล้ว UI ล่ะ ก็คือนางเอกไง

Part 1 : มึง…….กูรู้สึกว่างเปล่าเมื่ออยู่ในหน้าจอ test ( 2 สิ่งที่ต้องนึกถึง )

ชิบหายใช้เวลาเยอะมากกว่าจะเขียนได้ 1 case ( ช้าไม่เป็นก็เร็วไม่ได้ )

จากเคสของ part ที่ 1

Given : SetProduct20Items (ถ้า list ของสินค้าเกิน 10 อัน)
When : GetProduct (สินค้า)
Then : ShouldShowOnly10ItemWithoutExpired (แสดง 10 อันพอและ filter สินค้าที่หมดอายุออก)

สมมุติว่าเราทำ UI เสร็จแล้วออกมาหน้าตาประมาณนี้

ด้วย code หน้าตาประมาณนี้เราก็พอจะเดาออกได้ว่า model ที่เราจะต้องใช้ในการ render มี properties อะไรบ้างเช่น

  • imageName: String
  • title: String
  • expiredDate: Date

พอมีข้อมูลแบบนี้แล้วไป implement test กันต่อเล้ยยยย

Given : SetProduct20Items (ถ้า list ของสินค้าเกิน 10 อัน)

จากรูปภาพด้านบน อย่างที่เห็นมันจะรันไม่ผ่านเพราะว่า class Product ยังไม่ถูกสร้าง และอาจจะสงสัยว่าทำไม TDD ต้องทำแบบนั้น ช่วงแรกๆก็สงสัยเหมือนกันว่าทำไมวะ แต่พอทำไปทำมา มันก็อ๋อทันที

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

แต่เมื่อโปรแกรมใหญ่ขึ้นมีความซับซ้อนมากขึ้น build ทีนึงก็ช้า เขียนไปลบไปอยู่งั้น logic ที่สร้างใหม่ ไปพัง logic เก่าตอนไหนก็ไม่รู้ มันมี function ที่ใช้บ้างไม่ใช้บ้างหรือเปล่าก็ไม่รู้ รู้แต่ว่าส่งๆให้ QA ไปหา bug เอา ผ่ามมมมมมมม

ดังนั้นจุดประสงค์หลักของการเขียน TDD คือ การออกแบบโครงสร้าง Production code ด้วย test

ดังนั้นไอ class ที่สร้างขึ้นมาแล้วแดงๆ มันหมายความว่า เรากำลังออกแบบว่า feature ของเราจะมี class ชื่อ ProductModel นะเว้ย แล้วก็มี properties 3 ตัว

  • ตัวแรก imageName ที่มี type เป็น String นะ
  • ตัวสอง title ที่มี type เป็น String นะ
  • ตัวสาม expiredDate ที่มี type เป็น Date

ซึ่งไม่ได้ออกแบบลอยๆนะ มี spec และ UI ให้ดูประกอบ ก็เลยเน้นย้ำไปตั้งแต่ part 1 ว่าไปหา spec มาละให้เยอะๆ ละเอียดๆ จะช่วยให้การออกแบบง่ายขึ้น และถูกต้อง

ต่อไปเราจะออกแบบ When : GetProduct (สินค้า)

จากรูปภาพ สมมุติว่าผมใช้ pattern MVVM เนอะ ผมก็ออกแบบว่าจะใช้ viewModel ในการช่วย getProduct มาจาก API ด้วย categoryId แล้ว process logic ต่างๆในนั้นก่อนส่งให้ view ซึ่ง view ทำหน้าที่อ่าน state จาก viewModel อย่างเดียวพอ หรือถ้าสะดวกจะใช้ pattern อื่นก็ได้นะไม่ว่ากัน

ซึ่ง sut ย่อมาจาก System Under Test แปลว่า ระบบที่จะถูก test และถูก focus มากที่สุดในที่นี้ก็คือ ProductViewModel นั่นเอง และควรมี sut ตัวเดียวใน 1 test case นะจ๊ะ

และสุดท้ายเราก็จะมาออกแบบ
Then: ShouldShowOnly10ItemWithoutExpired (แสดง 10 อันพอและ filter สินค้าที่หมดอายุออก)

จากรูปภาพด้านบน ผมก็ออกแบบต่อว่า viewModel ของผมเนี่ยเมื่อ process logic อะไรเสร็จแล้วก็จะ return productList มากับ callback แล้วคาดหวังว่ามันควรที่จะได้ จำนวน item = 10 ตาม spec

ถ้าจะใช้ตัวจัดการ Asynchronous code แบบอื่นๆ เช่น AsyncAwait, Combine, RxSwift หรือตัวอื่นๆก็แล้วแต่เลยครับ แต่มันจะมีการเขียน test ที่ใช้วิธีต่างกันไป ในที่นี้ขอใช้ callback เพื่อให้เข้าใจง่าย

ส่วน WithoutExpired ( filter สินค้าที่หมดอายุออก ) ผมขอเก็บเอาไว้ไปอธิบายต่อใน part หน้า

ซึ่งขอสรุป part 2 ดังนี้…. TDD ในขั้นที่เรามักเรียกว่าทำให้มันแดง ( Fail ) คือการแปลง spec จากตัวหนังสือ จาก flowChart จาก UI หรือจากรูปภาพอะไรก็แล้วแต่ มาเป็น test เพื่อนำไปออกแบบโครงสร้าง production code ที่มีคุณภาพ และถูกต้อง ไม่ over ไม่ลบๆสร้างๆบ่อยๆ ถึงจะช้าในตอนแรกแต่รับรองเมื่อออกแบบมาอย่างดี เมื่อถึงตอน implement ทำได้เร็วแน่นอน ซึ่งตอนหน้าเราจะมาลองสร้าง class จากสิ่งที่เราออกแบบกัน

--

--