โดย สยามัค มาห์มูดี
TDD หรือ Test-Driven Development เป็นแนวทางการพัฒนาซอฟต์แวร์ที่เขียนการทดสอบก่อนที่จะนำโค้ดจริงไปใช้ ป>
จำเป็นต้องมีความเข้าใจที่ชัดเจนเกี่ยวกับ "อะไร" และ "อย่างไร" ในข้อกำหนดของโปรเจ็กต์/ฟีเจอร์ ป>
TDD ช่วยให้เขียนโค้ดน้อยลงแต่เพียงพอ ช่วยป้องกันข้อผิดพลาดทั่วไปในการพัฒนาซอฟต์แวร์ เช่น วิศวกรรมมากเกินไป ความครอบคลุมของการทดสอบมากเกินไป ข้อกำหนดหลักที่ขาดหายไป ฟังก์ชันและคลาสที่ใหญ่เกินไป และคำสั่งโค้ดที่ซับซ้อนมากเกินไป ป>
โดยรวมแล้ว การมีโค้ดเบสที่กระชับ ครอบคลุมการทดสอบหน่วยแล้ว และสะอาดตาก็ช่วยได้ เมื่อเวลาผ่านไป ยังช่วยประหยัดต้นทุนการพัฒนาและการบำรุงรักษาโค้ดอีกด้วย
ในบทความนี้เราจะพูดถึงการทำงานของ TDD ป>
บริบทคือสภาพแวดล้อมการพัฒนา Android ดังนั้นเราจะใช้ Kotlin และ JUnit5 กับโปรเจ็กต์ตัวอย่างเพื่อสาธิตขั้นตอนต่างๆ ป>
อย่างไรก็ตาม คำแนะนำและเทคนิคที่นี่สามารถฝึกฝนได้ในภาษาการเขียนโปรแกรมอื่นเช่นกัน
ข้อกำหนดเบื้องต้น
- ความรู้พื้นฐานของ Kotlin
- ความรู้พื้นฐานในการเขียนการทดสอบหน่วย
- ความรู้เกี่ยวกับการเยาะเย้ยและการยืนยัน
เราจะใช้ Kotlin เป็นภาษาการเขียนโปรแกรมและ JUnit5 เพื่อเขียนการทดสอบหน่วย
Mockito จะถูกใช้เพื่อทำงานกับการเยาะเย้ยและสายลับ
กลุ่มเป้าหมายคือนักพัฒนาซอฟต์แวร์จากแพลตฟอร์มใดๆ ที่กำลังมองหาบทใหม่ในอาชีพของตน ป>
แม้ว่าบริบทจะเป็น Android แต่เนื้อหาไม่ได้พูดถึงคุณสมบัติเฉพาะของแพลตฟอร์ม แต่เรามุ่งเน้นไปที่เทคนิค บันทึก และความท้าทายเมื่อพัฒนาด้วย TDD แทน
หากข้างต้นโอเคสำหรับคุณ มาเริ่มกันเลย
การพัฒนาที่ขับเคลื่อนด้วยการทดสอบทำงานอย่างไร
วงจร TDD ป>
กระบวนการพัฒนาเป็นไปตามวงจรของ:
- การเขียนการทดสอบที่ล้มเหลว (สี่เหลี่ยมสีชมพู)
- การนำโค้ดไปใช้เพื่อทำให้การทดสอบผ่าน (สี่เหลี่ยมสีเขียว)
- การปรับโครงสร้างโค้ดใหม่ (สี่เหลี่ยมสีน้ำเงิน) ตามความจำเป็น ในขณะเดียวกันก็ให้แน่ใจว่าการทดสอบยังคงผ่านการทดสอบต่อไป (สี่เหลี่ยมสีเขียวอ่อน)
- การเขียนการทดสอบที่ล้มเหลวใหม่ (รีสตาร์ทโฟลว์อีกครั้ง)
การเขียนการทดสอบที่ล้มเหลว (สี่เหลี่ยมสีชมพู)
ในขั้นตอนนี้ คุณจะเริ่มต้นด้วยการอธิบายว่าคุณต้องการให้โค้ดทำอะไร ป>
ลองจินตนาการว่าคุณกำลังทดสอบโค้ดของคุณเพื่อดูว่าโค้ดทำงานถูกต้องหรือไม่ การทดสอบนี้เหมือนกับคำถามที่คุณถามโค้ด เช่น "คุณทำงานนี้ได้ไหม" ป>
ในตอนแรก Code ของคุณไม่ทราบคำตอบ ดังนั้นคุณจึงเขียน Test ที่ควรล้มเหลว เนื่องจาก Code ของคุณยังไม่รู้ว่าจะต้องทำงานอย่างไร การทดสอบที่ล้มเหลวนี้เปรียบเสมือนสัญญาณเตือนสีชมพูที่บอกคุณว่ามีบางอย่างไม่ถูกต้อง
เมื่อคุณเสร็จสิ้นขั้นตอนนี้แล้ว JUnit5 จะสร้างรายงานที่ครอบคลุมจากการทดสอบที่คุณสร้างขึ้น การทดสอบเหล่านี้จะเป็นตัวแทนผลงานของคุณที่จับต้องได้ ป>
ตอนนี้ ลองจินตนาการว่าผู้จัดการโครงการของคุณกำลังอ่านกรณีทดสอบเหล่านี้เพื่อประเมินทั้งความครอบคลุมและความแม่นยำของการเข้าใจคุณสมบัติหรือผลิตภัณฑ์ของคุณ การเปิดรับมุมมองนี้จะทำให้มีความเข้าใจที่ชัดเจนยิ่งขึ้นเกี่ยวกับความสำคัญของระยะการพัฒนานี้
เปลี่ยนความสนใจของคุณจากความซับซ้อนทางเทคนิคไปสู่พฤติกรรมของซอฟต์แวร์ ป>
แทนที่จะจมอยู่กับประเด็นทางเทคนิคที่สำคัญ ให้มุ่งความสนใจไปที่วิธีการทำงานของซอฟต์แวร์และการโต้ตอบกับผู้ใช้และส่วนประกอบอื่นๆ ป>
การเปลี่ยนแปลงมุมมองนี้ทำให้คุณสามารถจัดลำดับความสำคัญของการดำเนินการและผลลัพธ์ที่ตั้งใจไว้ของซอฟต์แวร์ ซึ่งนำไปสู่การทดสอบที่สะท้อนถึงพฤติกรรมในโลกแห่งความเป็นจริงได้อย่างแม่นยำ ป>
ด้วยการมุ่งเน้นไปที่พฤติกรรมมากกว่าเรื่องเล็กน้อยทางเทคนิค คุณมั่นใจได้ว่าการทดสอบของคุณสอดคล้องกับวัตถุประสงค์ของซอฟต์แวร์และความคาดหวังของผู้ใช้อย่างใกล้ชิด ป>
ในบางกรณี คุณอาจจบลงด้วยกรณีทดสอบไม่เกินสองสามกรณีต่อส่วนประกอบ (ซึ่งมีจุดประสงค์:ทำงานน้อยลงแต่ตรงเป้าหมาย) และนั่นก็ไม่เป็นไร ตราบใดที่คุณครอบคลุมข้อกำหนดด้านพฤติกรรมทั้งหมดของโปรเจ็กต์
เคล็ดลับ
- เจาะจง: เขียนกรณีทดสอบที่ชัดเจนและเฉพาะเจาะจงโดยเน้นไปที่ลักษณะหนึ่งของพฤติกรรมโค้ดของคุณ
- เริ่มต้นอย่างง่าย:เริ่มต้นด้วยกรณีทดสอบที่ง่ายที่สุดซึ่งครอบคลุมฟังก์ชันพื้นฐานที่คุณต้องการ
@Test fun `a sum is calculated from two input numbers`() {}
- ใช้ชื่อที่สื่อความหมาย:ตั้งชื่อการทดสอบของคุณอย่างอธิบายเพื่อให้ใครก็ตามที่อ่านการทดสอบรู้ว่าการทดสอบกำลังตรวจสอบอะไร
@Test fun `Font Ratio is fetched from data source INITIALLY`() {}
ข้อผิดพลาดทั่วไปที่ควรหลีกเลี่ยง
- การทดสอบมากเกินไปในครั้งเดียว: หลีกเลี่ยงการทดสอบหลายอย่างในการทดสอบครั้งเดียว ซึ่งอาจทำให้ระบุได้ยากว่าสิ่งใดล้มเหลว
// Don't do this
@Test fun `pixelSize fits the standart sizes while fontSize is bigger than minumum supported font size but matches the list of special levels of size`() {}
- ขึ้นอยู่กับรายละเอียดการใช้งาน: อย่าเขียนการทดสอบที่เชื่อมโยงกับการทำงานภายในของโค้ดอย่างแน่นหนา การทดสอบควรเน้นที่พฤติกรรม ไม่ใช่การนำไปปฏิบัติ
// Don't do this
@Test fun `pixelSize is Long and Non-Null and fits the standart sizes then calculated font size is non-null and of type Dimention`() {}
การใช้รหัสเพื่อผ่านการทดสอบ (จัตุรัสสีเขียว)
เมื่อคุณทำแบบทดสอบเรียบร้อยแล้ว ก็ถึงเวลาสอนโค้ดของคุณถึงวิธีทำงานอย่างถูกต้อง ป>
คุณเขียนโค้ดจริงที่ควรทำให้การทดสอบผ่าน และโค้ดของคุณตอบคำถามได้อย่างถูกต้อง ป>
เมื่อโค้ดของคุณผ่านการทดสอบ มันก็เหมือนกับไฟเขียวที่บอกว่า "ได้ ฉันสามารถทำงานได้แล้ว!" ป>
ขั้นตอนนี้เป็นเรื่องเกี่ยวกับการทำให้แน่ใจว่าโค้ดของคุณเข้าใจและสามารถแก้ไขปัญหาที่คุณขอให้ทำได้
เคล็ดลับ
- เขียนโค้ดขั้นต่ำ:เขียนโค้ดที่ง่ายที่สุดที่ทำให้การทดสอบล้มเหลวผ่าน คุณสามารถอ่านเพิ่มเติมเกี่ยวกับวิธีหลีกเลี่ยงวิศวกรรมมากเกินไปได้ที่นี่
// Test Case
@Test fun `Storage stores font ratio in key-value`() {
// Given
val fontRatio = 2.0f
val mockEditor = mockk<SharedPreferences.Editor>(relaxed = true)
every { mockSharedPreference.edit() } returns mockEditor
every { mockEditor.putFloat(any(), any()) } returns mockEditor
every { mockEditor.apply() } just Runs
// When
storage.saveFontRatio(fontRatio)
// Then
verify(exactly = 1) {
mockEditor.apply()
}
}
// Correct Implementation - Avoid extra implementation
class SharedPreferenceHelper(
private val sharedPreferences: SharedPreferences
) {
fun saveFontRatio(fontRatio: Float) {
sharedPreferences.edit().putFloat("font-ratio", fontRatio).apply()
}
}
// Wrong Implementation
class SharedPreferenceHelper(
private val sharedPreferences: SharedPreferences
) {
fun saveFontRatio(fontRatio: Float) {
if (fontRatio <= 0.0f)
throw IllegalArgumentException("Font ratio must be greater than 0.0f")
storeValue(key = FONT_RATIO_KEY, value = fontRatio)
}
private fun storeValue(key: String, value: Float){
val editor = sharedPreferences.edit() editor.putFloat(key, value)
editor.apply()
}
fun getFontRatio(): Float {
return sharedPreferences.getFloat("font_ratio", 1.0f) }
}
- หลีกเลี่ยงการทำซ้ำ: อย่าทำซ้ำรหัส หากคุณพบว่าตัวเองเขียนตรรกะที่คล้ายกันในหลายๆ ที่ ให้พิจารณาปรับโครงสร้างใหม่ การปรับปรุงโค้ดหลักนี้สามารถทำได้ในระยะนี้ แต่หากการแก้ไขอาจมีผลข้างเคียงก็ไม่ต้องสนใจ
class ... {
override fun getDefaultFontSize(): Float {
val zoomRatio = DEFAULT_SSPEED * DEFAULT_FONT_RATIO / deviceDensity
val fontSize = zoomRatio * standardFontSize
return fontSize
}
override fun getFontSizeBySSpeed(speed: Int): Float {
val zoomRatio = speed * DEFAULT_FONT_RATIO / deviceDensity
val fontSize = zoomRatio * standardFontSize
return fontSize
}
}
class ... {
override fun getDefaultFontSize(): Float = calculate(DEFAULT_AGE)
override fun getFontSizeByAge(age: Int): Float = calculate(age)
private fun calculate(age: Int): Float {
val zoomRatio = age * DEFAULT_FONT_RATIO / deviceDensity
val fontSize = zoomRatio * standardFontSize
return fontSize
}
}
ข้อผิดพลาดทั่วไปที่ควรหลีกเลี่ยง:
- ก้าวไปข้างหน้า:อย่าเขียนโค้ดมากเกินความจำเป็นเพื่อผ่านการทดสอบ TDD เป็นเรื่องเกี่ยวกับการพัฒนาแบบค่อยเป็นค่อยไป TDD สนับสนุนแนวทางการพัฒนาแบบค่อยเป็นค่อยไปและทีละขั้นตอน เมื่อคุณก้าวไปข้างหน้า คุณกำลังพยายามแก้ไขปัญหาที่ยังไม่เกี่ยวข้องโดยตรงกับการทดสอบปัจจุบันที่คุณกำลังดำเนินการอยู่ เป้าหมายหลักคือการมุ่งเน้นไปที่งานปัจจุบันที่กำลังทำอยู่ ซึ่งผ่านการทดสอบปัจจุบัน โดยไม่ถูกมองข้ามจากฟังก์ชันการทำงานในอนาคต
- การเพิกเฉยต่อความล้มเหลวในการทดสอบ:หากการทดสอบไม่ล้มเหลวตั้งแต่แรก คุณอาจพลาดกรณีสำคัญไป สิ่งนี้อาจดูเหมือนไม่น่าจะเกิดขึ้นตั้งแต่แรกเห็น แต่หลังจากการพัฒนาส่วนประกอบการทดสอบของคุณแล้ว คุณจะเริ่มเขียนการทดสอบหลายรายการสำหรับวิธีเดียวเพื่อทดสอบแง่มุมต่างๆ ของตรรกะ นี่คือจุดที่คุณไม่ควรมีความสุขหากตรรกะที่ยังไม่ได้ใช้งานของคุณผ่านการทดสอบ พูดง่ายๆ ก็คือนี่คือวิธีจับจุดบกพร่องในระยะการพัฒนา ดังนั้นจงคาดหวังความล้มเหลวในเวลาที่ควรจะเป็น
การปรับโครงสร้างโค้ดใหม่ (จัตุรัสสีน้ำเงิน) และรับรองความสำเร็จในการทดสอบ (จัตุรัสสีเขียวซีด)
เมื่อโค้ดของคุณผ่านการทดสอบแล้ว ก็ถึงเวลาทำความสะอาด ป>
คุณอาจเห็นวิธีทำให้โค้ดของคุณเป็นระเบียบมากขึ้น เข้าใจง่ายขึ้น หรือเร็วขึ้นอีกด้วย คิดว่าเป็นการจัดระเบียบห้องของคุณเมื่อคุณเล่นเสร็จแล้ว ทำให้ทุกอย่างเป็นระเบียบและเป็นระเบียบหลังจากที่คุณเล่นเสร็จแล้ว คุณปรับปรุงโค้ดของคุณโดยไม่ต้องเปลี่ยนสิ่งที่ทำ ป>
ขณะที่คุณทำเช่นนี้ คุณจะทำการทดสอบทั้งหมดต่อไปเพื่อให้แน่ใจว่ายังคงผ่าน หากการทดสอบล้มเหลวในระหว่างขั้นตอนนี้ มันจะเหมือนกับสัญญาณเตือนสีเขียวอ่อนที่บอกคุณว่าสิ่งที่คุณทำความสะอาดอาจทำให้โค้ดเสียหายโดยไม่ตั้งใจ
คุณสามารถถือว่าส่วนนี้เป็นขั้นตอนการบำรุงรักษาโค้ดแยกต่างหาก ป>
สมมติว่าคุณได้รับมอบหมายงานให้ล้างโค้ดเก่า และตรวจสอบให้แน่ใจว่าเป็นไปตามหลักเกณฑ์ด้านคุณภาพโค้ดของทีมตลอดจนข้อกำหนดของผลิตภัณฑ์ ป>
ตลอดกระบวนการนี้ กุญแจสำคัญคือการรักษาสมดุลอย่างระมัดระวัง ปรับปรุงในขณะเดียวกันก็รับประกันว่าการทดสอบของคุณจะประสบความสำเร็จต่อไป
ต่อไปนี้เป็นแนวคิดและกลยุทธ์บางส่วนสำหรับระยะการปรับโครงสร้างใหม่:
- ความชัดเจนของโค้ด:ลดความซับซ้อนของส่วนที่ซับซ้อน แทนที่ชื่อตัวแปรที่ไม่ชัดเจน และปรับปรุงความคิดเห็นเพื่อทำให้โค้ดง่ายขึ้นสำหรับผู้อื่น (และตัวคุณในอนาคต) ที่จะเข้าใจ
- ความเป็นโมดูล:แบ่งฟังก์ชันขนาดใหญ่ออกเป็นฟังก์ชันย่อยและเน้นเฉพาะ ซึ่งจะทำให้โค้ดของคุณเป็นแบบโมดูลาร์มากขึ้นและช่วยให้บำรุงรักษาและทดสอบได้ง่ายขึ้น
- ลบความซ้ำซ้อน:ระบุโค้ดที่ซ้ำกันและรวมไว้ในฟังก์ชันหรือคลาสที่นำมาใช้ซ้ำได้ ซึ่งจะช่วยขจัดความซ้ำซ้อนและรับประกันความสม่ำเสมอ
- การเพิ่มประสิทธิภาพ:ระบุพื้นที่ที่สามารถปรับปรุงประสิทธิภาพได้ อย่างไรก็ตาม ให้เพิ่มประสิทธิภาพเฉพาะเมื่อคุณมีเป้าหมายด้านประสิทธิภาพที่เฉพาะเจาะจงและมีหลักฐานว่าโค้ดเป็นจุดคอขวด การเพิ่มประสิทธิภาพที่นี่คือเพื่อหลีกเลี่ยงการใช้ทรัพยากรและไม่ทำให้โค้ดมีประสิทธิภาพ
- การจัดรูปแบบที่สอดคล้องกัน:รักษารูปแบบโค้ดที่สอดคล้องกัน โดยยึดตามแบบแผนของทีมหรือโครงการของคุณ
- โค้ดที่ไม่ได้ใช้:ลบตัวแปร ฟังก์ชัน หรือการนำเข้าที่ไม่ได้ใช้ซึ่งทำให้โค้ดเบสไม่เป็นระเบียบ
- การปรับปรุงการทดสอบ:คุณสามารถเพิ่มการทดสอบได้ทุกเมื่อที่ต้องการ ซึ่งต่างจากการรับรู้ทั่วไปเกี่ยวกับ TDD ปรับปรุงชุดการทดสอบโดยการเพิ่มกรณีการทดสอบใหม่เพื่อให้ครอบคลุมสถานการณ์ที่ไม่ได้ได้รับการแก้ไขก่อนหน้านี้ ซึ่งช่วยรักษาความครอบคลุมของการทดสอบ
- เอกสารประกอบ:หากวัตถุประสงค์ของโค้ดของคุณไม่ชัดเจนจากตัวโค้ดในทันที ให้พิจารณาเพิ่มหรือปรับปรุงเอกสารเพื่ออธิบายจุดประสงค์และการใช้งาน หลีกเลี่ยงการทำให้เป็นนิสัย โดยมีจุดมุ่งหมายเพื่อใช้เป็นคำอธิบายเสริมสำหรับกรณีสำคัญๆ เพื่อหลีกเลี่ยงความสับสน
โปรดทราบว่าโค้ด TDD ควรแสดงออกได้เองและไม่ขึ้นอยู่กับเอกสารประกอบ
โปรดจำไว้ว่าในขณะที่ปรับโครงสร้างใหม่ สิ่งสำคัญคือต้องทำการทดสอบทั้งหมดต่อไปเพื่อให้แน่ใจว่าการทดสอบจะผ่านต่อไป
เคล็ดลับ
- ให้การทดสอบครอบคลุม:ตรวจสอบให้แน่ใจว่าการทดสอบของคุณครอบคลุมสถานการณ์ต่างๆ เพื่อตรวจจับผลข้างเคียงที่ไม่ได้ตั้งใจในระหว่างการปรับโครงสร้างใหม่
- ค่อยๆ ปรับเปลี่ยนโครงสร้างใหม่:ทำการเปลี่ยนแปลงเล็กๆ น้อยๆ ในโค้ดของคุณและทำการทดสอบบ่อยๆ เพื่อตรวจจับการถดถอยตั้งแต่เนิ่นๆ
ข้อผิดพลาดทั่วไปที่ควรหลีกเลี่ยง:
- การปรับโครงสร้างใหม่โดยไม่มีการทดสอบ: การปรับโครงสร้างใหม่โดยไม่ต้องมีการทดสอบอาจทำให้เกิดลักษณะการทำงานที่ไม่คาดคิดได้ หากมีโอกาสที่จะพลาดส่วนหนึ่งของตรรกะ ให้ลองเขียนแบบทดสอบนั้น
- การเปลี่ยนแปลงโค้ดจำนวนมาก:บางครั้งเราก็ต้องเปลี่ยนจำนวนบรรทัดมากกว่าที่เราพัฒนาเพื่อให้การทดสอบผ่าน พิจารณาเฟสการปรับโครงสร้างแยกต่างหากเสมอ แทนที่จะทำการเปลี่ยนแปลงมากเกินไปในระยะการพัฒนา เนื่องจากเป็นตัวเลือกที่ปลอดภัยกว่าและมีค่าใช้จ่ายน้อยกว่า
การเขียนการทดสอบที่ล้มเหลวใหม่ (การรีสตาร์ทโฟลว์)
ตอนนี้คุณคิดถึงสิ่งต่อไปที่คุณต้องการให้โค้ดของคุณทำ ป>
คุณเริ่มต้นด้วยการเขียนการทดสอบใหม่ที่ควรจะล้มเหลวเนื่องจากโค้ดของคุณยังไม่รู้วิธีทำงานใหม่ นี่เหมือนกับการให้โค้ดของคุณมีความท้าทายใหม่ให้แก้ไข ป>
จากนั้นให้คุณทำซ้ำทั้งวงจร:ทำการทดสอบให้ผ่านด้วยโค้ด (สี่เหลี่ยมสีเขียว) ทำความสะอาดหากจำเป็น (สี่เหลี่ยมสีน้ำเงิน) และทำการทดสอบต่อไปเพื่อให้แน่ใจว่าทุกอย่างใช้งานได้ (สี่เหลี่ยมสีเขียวอ่อน) ป>
ด้วยวิธีนี้ คุณจะก้าวไปข้างหน้าและสร้างโค้ดทีละขั้นตอนอยู่เสมอ
เคล็ดลับ
- ขั้นตอนที่เพิ่มขึ้น:เพิ่มการทดสอบใหม่สำหรับฟังก์ชันการทำงานใหม่ทีละน้อยเพื่อรักษาเส้นทางการพัฒนาที่ชัดเจน แทนที่จะพยายามใช้คุณลักษณะที่ซับซ้อนทั้งหมดในคราวเดียว คุณแยกส่วนย่อยออกเป็นส่วนย่อยๆ ที่สามารถจัดการได้ และสร้างการทดสอบสำหรับแต่ละส่วนเหล่านี้ แนวทางนี้ช่วยรักษาเส้นทางการพัฒนาที่ชัดเจนและมั่นคง ช่วยให้คุณมีสมาธิ ลดความเสี่ยง และรับประกันว่าการเพิ่มซอฟต์แวร์แต่ละรายการจะได้รับการทดสอบอย่างละเอียด
- วงจรคำติชม:ใช้คำติชมจากการเขียนการทดสอบที่ล้มเหลวเพื่อเป็นแนวทางในการดำเนินการของคุณ Feedback loop เน้นลักษณะการวนซ้ำของ TDD เมื่อคุณสร้างการทดสอบใหม่และสังเกตว่าล้มเหลว คุณจะได้รับข้อมูลเชิงลึกอันมีค่าเพื่อเป็นแนวทางในการนำไปปฏิบัติ
ต่อไปนี้เป็นวิธีการทำงานของวงจรป้อนกลับ:
- การตั้งค่าความคาดหวัง:เมื่อคุณเขียนการทดสอบใหม่ คุณจะต้องกำหนดความคาดหวังว่าโค้ดควรทำงานอย่างไร สิ่งนี้จะให้ความกระจ่างว่าคุณมีเป้าหมายที่จะบรรลุผลสำเร็จด้วยฟีเจอร์ใหม่ของคุณ
- ความล้มเหลวครั้งแรก:การทดสอบล้มเหลวในตอนแรกเนื่องจากโค้ดที่เกี่ยวข้องเพื่อตอบสนองความคาดหวังขาดหายไปหรือไม่สมบูรณ์ ความล้มเหลวเบื้องต้นนี้เป็นส่วนหนึ่งของกระบวนการ TDD ตามธรรมชาติ
- แนะนำการใช้งานของคุณ:ความคิดเห็นจากความล้มเหลวของการทดสอบชี้ให้คุณเห็นว่าโค้ดใดที่ต้องเขียนหรือแก้ไข ซึ่งจะกลายเป็นแผนงานสำหรับการพัฒนาของคุณ โดยสรุปว่าฟังก์ชันใหม่ควรมีลักษณะอย่างไร
- ความก้าวหน้าที่เพิ่มขึ้น:เมื่อคุณใช้โค้ดที่จำเป็นเพื่อให้การทดสอบผ่าน คุณกำลังสร้างฟังก์ชันการทำงานที่ต้องการเพิ่มขึ้นเรื่อยๆ แต่ละขั้นตอนจะได้รับคำแนะนำจากผลตอบรับที่ได้จากการทดสอบที่ไม่ผ่าน
- การยืนยัน:เมื่อการใช้งานของคุณเสร็จสมบูรณ์ คุณจะทำการทดสอบอีกครั้ง หากผ่าน จะเป็นการยืนยันว่ารหัสใหม่ของคุณเป็นไปตามความคาดหวังที่คุณตั้งไว้ในตอนแรก
วงจรตอบรับช่วยให้แน่ใจว่าการพัฒนาของคุณสอดคล้องกับเป้าหมายที่ตั้งใจไว้ของซอฟต์แวร์ของคุณ
ข้อผิดพลาดทั่วไปที่ควรหลีกเลี่ยง:
- การเขียนการทดสอบหลังการใช้งาน:อย่าเขียนการทดสอบหลังจากที่คุณใช้งานคุณสมบัตินี้แล้ว TDD เป็นเรื่องเกี่ยวกับการเขียนการทดสอบก่อน แม้แต่ตรรกะเล็กๆ น้อยๆ ที่เพิ่มเข้าไปก่อนโค้ดทดสอบก็หมายความว่าอาจมีการสิ้นเปลืองทรัพยากร/ข้อบกพร่องในโค้ด ประเด็นทั้งหมดคือการไม่เพิ่มตรรกะใดๆ เว้นแต่จะมีความจำเป็นจากชุดทดสอบ
- การข้ามการทดสอบที่ล้มเหลว:อย่าข้ามขั้นตอนนี้แม้ว่าคุณจะคิดว่าคุณรู้วิธีใช้งานฟีเจอร์นี้แล้วก็ตาม
นี่คือเหตุผลที่คุณไม่ควรข้ามขั้นตอนการทดสอบที่ล้มเหลว แม้ว่าคุณจะมั่นใจแล้วก็ตาม:
- ความชัดเจนของเจตนา:การเขียนการทดสอบที่ล้มเหลวจะทำให้เจตนาของคุณสำหรับคุณลักษณะนี้ชัดเจนขึ้น โดยบังคับให้คุณพิจารณาพฤติกรรมและผลลัพธ์ที่แน่นอนที่คุณตั้งเป้าไว้ก่อนที่จะเริ่มนำไปใช้
- การตรวจสอบสมมติฐาน:แม้ว่าคุณจะคิดว่าคุณเข้าใจคุณลักษณะนี้แล้ว การสร้างการทดสอบจะทำให้แน่ใจได้ว่าสมมติฐานของคุณถูกต้อง ความเข้าใจของคุณอาจจะถูกต้อง แต่การทดสอบจะพิสูจน์ได้
- ตาข่ายนิรภัย:การเขียนการทดสอบที่ล้มเหลวจะเป็นการสร้างตาข่ายนิรภัยที่ป้องกันการถดถอยในอนาคต โดยทำหน้าที่เป็นข้อกำหนดสำหรับคุณลักษณะนี้และช่วยตรวจจับผลข้างเคียงที่ไม่ได้ตั้งใจ
- การพัฒนาแบบค่อยเป็นค่อยไป:TDD ส่งเสริมการพัฒนาแบบค่อยเป็นค่อยไป คุณลักษณะใหม่แต่ละอย่างถูกสร้างขึ้นทีละขั้นตอน โดยมีความก้าวหน้าที่ชัดเจนตั้งแต่การทดสอบที่ล้มเหลวไปจนถึงการใช้งานจริง การข้ามขั้นตอนนี้จะขัดขวางความก้าวหน้านั้น
- เอกสารประกอบ:การทดสอบที่ล้มเหลวจะบันทึกลักษณะการทำงานที่คาดหวังของคุณลักษณะ เอกสารนี้มีค่าสำหรับคุณและทีมของคุณ โดยเฉพาะอย่างยิ่งเมื่อกลับมาดูโค้ดอีกครั้งในอนาคต โปรดจำไว้เสมอว่ามีระบบในการสร้างรายงานโดยการแสดงรายการรหัสทดสอบทั้งหมดของคุณสำหรับผู้จัดการผลิตภัณฑ์และ QA รายงานเหล่านั้นเปิดเผยรายละเอียดที่คุณเห็นในผลิตภัณฑ์ ดังนั้น พยายามโน้มน้าวพวกเขาว่าคุณเข้าใจประเด็นนั้นอย่างถี่ถ้วน
วิธีการพัฒนาโดยใช้ TDD
TDD เน้นย้ำถึงความสำคัญของการเขียนการทดสอบอัตโนมัติเพื่อขับเคลื่อนการออกแบบและพัฒนาซอฟต์แวร์ สิ่งนี้นำไปสู่โค้ดที่เชื่อถือได้ บำรุงรักษาได้ และเปลี่ยนแปลงได้ง่ายขึ้นเมื่อเวลาผ่านไป
แต่เราจะนำไปปฏิบัติได้อย่างไร? โดยการลองใช้และทำความคุ้นเคยทีละน้อย
มาลองใช้ TDD ในขณะที่พัฒนาฟีเจอร์ใหม่เพื่อสาธิตวิธีที่เราสามารถเริ่มใช้งานมันในโลกแห่งความเป็นจริงได้
เราจะใช้คุณสมบัติการตั้งค่าขนาดตัวอักษรอัตโนมัติ ป>
เรามีแอปข่าวและผู้ใช้สามารถตั้งค่าความเร็วในการเลื่อนอัตโนมัติสำหรับฟีดข่าวได้ ป>
เราต้องการใช้คุณลักษณะที่ปรับขนาดแบบอักษรของหน้าจอจากความเร็วการเลื่อนที่ตั้งไว้ในหน้าโปรไฟล์ผู้ใช้
หากผู้ใช้ตั้งค่าความเร็วในการเลื่อนจาก 0 เป็น 1 ขนาดตัวอักษรควรเพิ่มขึ้น 1.3 ป>
การเพิ่มความเร็วการเลื่อนใดๆ มากกว่า 1 จะส่งผลให้ขนาดตัวอักษรเพิ่มขึ้น 1.2 ป>
คุณลักษณะนี้ช่วยให้ผู้ใช้ได้รับประสบการณ์ที่ดีขึ้นในขณะที่อ่านข่าว
ฉันยังได้แชร์โค้ดที่เราสำรวจในพื้นที่เก็บข้อมูล GitHub นี้ด้วย
อย่าลังเลที่จะโคลนและเล่นกับมัน
ลองทำตามขั้นตอนในขณะที่เราก้าวหน้าในการพัฒนา สิ่งนี้จะช่วยให้คุณเข้าใจเทคนิคและวิธีคิดในบริบทของ TDD ได้จริง ป>
ดังนั้น ให้เปิด Android Studio และสร้างโปรเจ็กต์ใหม่
แผนภูมิการไหลของข้อมูลคุณสมบัติการตั้งค่าขนาดตัวอักษรอัตโนมัติ
DFD ของคุณลักษณะตัวอย่างในแอป Android ป>
ด้านบนนี้เป็นแผนผังลำดับงานว่ากระแสข้อมูลควรมีลักษณะอย่างไรในท้ายที่สุด ป>
ต่อไปนี้เป็นโครงร่างขององค์ประกอบที่น่าสนใจแต่ละส่วน:
กรณีการใช้งานนี้จะขึ้นอยู่กับ
ใน
ใน
เราจะมีส่วนประกอบ UI ที่จำเป็นในส่วนการตั้งค่าผู้ใช้เพื่อให้ผู้ใช้สามารถป้อนความเร็วการเลื่อนที่ต้องการได้ ซึ่งสามารถทำได้โดยใช้องค์ประกอบ UI มาตรฐานของ Android เช่น
นี่คือรายละเอียดสำหรับฟีเจอร์ง่ายๆ และทุกครั้งที่คุณทำขั้นตอนนี้จะชัดเจนมากขึ้นว่าขั้นตอนและผลลัพธ์สุดท้ายคืออะไร การดำเนินการเพื่อการเปลี่ยนแปลงของคุณถือเป็นสิ่งสำคัญ
ขั้นตอนแรกคือสร้างคลาสทดสอบเองเสมอ ในกรณีนี้ เราจะมีคลาสการทดสอบอย่างน้อยดังต่อไปนี้:
ฉันชอบเริ่มต้นด้วยส่วน ViewModel ป>
ViewModel เป็นส่วนประกอบทางสถาปัตยกรรม Android ที่จะหลีกเลี่ยงการเปลี่ยนแปลงวงจรชีวิต (เช่น เบื้องหน้า พื้นหลัง ที่โฟกัส) ดังนั้นจึงเป็นสถานที่ที่ดีในการจัดเก็บรัฐของเรา ป>
มาสร้างไฟล์ทดสอบภายใน
TDD ในทางปฏิบัติไม่เหมือนกับการพัฒนางานแบบเดิม ป>
ด้วย TDD เราใช้ IDE เพื่อเพิ่มกระบวนการสร้างไฟล์และคุณสมบัติ (ฟิลด์) แต่เราสร้างไฟล์ทดสอบด้วยตนเอง! หลังจากลองไม่กี่ครั้ง IDE ด้านนี้จะมีประโยชน์
สร้างโครงสร้างโค้ด (แพ็คเกจ) จากนั้นสร้างคลาสทดสอบของคุณโดยคลิกขวาที่แพ็คเกจแล้วเลือก
เลือกชื่อที่สื่อความหมาย (บางทีคุณควรปฏิบัติตามแบบแผนหากคุณยังไม่ได้ทำ) ป>
ตัวอย่างเช่น:
ตอนนี้เรามาสร้างการทดสอบเปล่ากันดีกว่า พยายามให้กว้างที่สุด ป>
ตามแผนภาพ เราจะไม่มีตรรกะมากนักสำหรับฟีเจอร์นี้ ป>
มีสองกลยุทธ์หลักในการเขียนฟังก์ชันการทดสอบหน่วย:
มาเลือกหนึ่งรายการและติดตามการทดสอบทั้งหมดของคุณ
แนวคิดก็เหมือนกัน:จัดกลุ่มโค้ดทดสอบของคุณเพื่อให้อ่านและบำรุงรักษาได้ง่าย
นี่คือสิ่งที่ฉันมีตอนนี้:
มาทำการทดสอบกันดีกว่า!
มันล้มเหลว. ที่จริงแล้ว บิลด์ล้มเหลว- ไม่ใช่การทดสอบ ป>
ขอแสดงความยินดี! เราเพิ่งมาถึงขั้นตอนแรกในวงจร TDD:
เนื่องจากยังไม่มี ViewModel เราจึงมีสีแดง ป>
ตอนนี้ เรามาสร้างอินสแตนซ์ของ ViewModel กันดีกว่า
ดังนั้นเราจึงใช้ IDE เพื่อสร้างคลาสที่ขาดหายไปหรือโค้ดที่ยังไม่ได้ใช้งาน ป>
หากต้องการให้ป๊อปอัปโต้ตอบนี้ ฉันเลื่อนตัวชี้ไปที่ส่วนที่ยังไม่ได้ใช้งานแล้วกด Option + Return (บน macOS) ป>
จากนั้น ปฏิบัติตามตัวเลือกที่ให้มา:
และตอนนี้เรามาทำการทดสอบอีกครั้ง (ขั้นตอนสุดท้าย):
ใช่! มันผ่านไป
โปรดทราบว่าการทดสอบเหล่านี้มีเนื้อความว่างและไม่ได้ทดสอบอะไรเลย! ถูกต้องและไม่เป็นไร
เราควรสร้างคลาสการทดสอบทั้งหมดต่อไป (ยังคงมีเนื้อหาทดสอบว่างเปล่า) สำหรับทุกส่วนประกอบในแผนภาพ DFD - ฉันได้แชร์สิ่งเหล่านี้ไว้ที่ตอนต้นของบทความ
ซึ่งจะช่วยให้มีความเข้าใจคุณลักษณะนี้ชัดเจนยิ่งขึ้นก่อนที่เราจะนำไปใช้
ในที่สุด เราจะมีคลาสการทดสอบประมาณ 3-4 คลาสที่มีสถานการณ์ทั่วไปและการทดสอบหน่วยให้ครอบคลุม ป>
มันจะมีลักษณะดังนี้:
ลองใช้หนึ่งในนั้นเป็นตัวอย่าง
แต่ก่อนหน้านั้น เราจะต้องทำงานกับโมเดลข้อมูล UI และโดเมนของฟีเจอร์นี้ ป>
ดังนั้น เพื่อให้สามารถย้ายข้อมูลไปมาได้ เรามาสร้างคลาสข้อมูลที่คุณต้องการล่วงหน้ากันดีกว่า
กลับไปที่
โปรดทราบว่ากุญแจสำคัญในที่นี้คือการอ่านการทดสอบอย่างละเอียดและหลีกเลี่ยงการนำไปใช้เพิ่มเติมหรือการยืนยันใดๆ ป>
อนุญาตให้ดำเนินการตามข้อกำหนดเท่านั้น
ในกรณีนี้ เราต้องการกระแสข้อมูลที่เชื่อมต่อกับแหล่งข้อมูลที่สร้างขึ้นก่อนหน้านี้(
อย่าลืม:เราจำเป็นต้องมีการทดสอบที่ล้มเหลวก่อน
สังเกตส่วนที่ยังไม่ได้ใช้งานภายในตัวฟังก์ชันทดสอบ (ทำเครื่องหมายด้วยตัวอักษรสีแดง) ป>
ตอนนี้ เรามาติดตั้งโค้ด จากนั้นปรับโครงสร้างใหม่เพื่อให้ผ่านไป
ฉันใช้ไลบรารี MockK สำหรับการเยาะเย้ยคลาสและวัตถุ และใช้ Turbine เพื่อทดสอบสตรีม Flow ที่นี่
หากคุณไม่คุ้นเคยกับพวกเขาอย่าตกใจ! เพียงตรวจสอบหน้าเว็บอย่างเป็นทางการและลองใช้ดู ป>
มาสร้างการพึ่งพากันก่อนและเพิ่มลงใน ViewModel โดยใช้ Named Arguments อาร์กิวเมนต์ที่มีชื่อช่วยเราในการสร้างพารามิเตอร์ผ่าน IDE เพื่อแนะนำชื่อที่ถูกต้องผ่านโค้ดทดสอบ
ทำเช่นเดียวกันกับ
ในที่สุด รหัสทดสอบสุดท้ายอาจเป็นรหัสด้านล่าง:
โปรดทราบว่าเราไม่ได้ใช้ส่วนภายในของ ViewModel หรือ Repository ที่นี่
เราเพียงสร้างส่วนที่ขาดหายไปเพื่อลบข้อผิดพลาดออกจากตัวทดสอบ ป>
เราจะนำรายละเอียดเหล่านั้นไปใช้ในการวนซ้ำครั้งถัดไป ป>
ดำเนินการทดสอบทันที ป>
แน่นอนว่ามันจะล้มเหลว เพราะเราไม่ได้ใช้
ตอนนี้ ปรับโครงสร้าง ViewModel ใหม่เพื่อให้ผ่านการทดสอบ (การใช้งานขั้นต่ำ) ป>
ในกรณีนี้ คุณเพียงแค่ต้องเชื่อมต่อโฟลว์สถานะกับพื้นที่เก็บข้อมูล เราได้เพิ่มมันเป็นการพึ่งพาในการวนซ้ำครั้งก่อนแล้ว ป>
นี่คือรหัสสุดท้าย:
ทำการทดสอบอีกครั้ง และบูม! ผ่านไป!
ด้วยการทดสอบหน่วยนี้ เราได้นำส่วนหลักของ
อย่าปฏิบัติต่อกรณีทดสอบเหล่านี้เหมือนกับที่คุณทำกับการทดสอบหน่วยปกติ (ซึ่งทำงานและผ่านอย่างรวดเร็ว) ป>
ใช้เวลาทำความเข้าใจข้อกำหนดทางเทคนิคและผลิตภัณฑ์ก่อน จากนั้นจึงเผยแพร่แผนและเริ่มดำเนินการ หลังจากลองไม่กี่ครั้ง คุณจะคิดแบบ TDD ได้ง่ายขึ้น
ซอร์สโค้ดและพื้นที่เก็บข้อมูลสำหรับโปรเจ็กต์นี้มีอยู่ในหน้า GitHub ของฉัน
โปรดตรวจสอบและทำตามขั้นตอนถัดไปให้เสร็จสิ้น ฉันได้แยกการวนซ้ำในสาขาต่างๆ เพื่อให้คุณสามารถเปรียบเทียบกับการใช้งานของคุณเองได้
ดังนั้น หลังจากดำดิ่งลงสู่ Test-Driven Development (TDD) และสำรวจอย่างเจาะลึกแล้ว ฉันต้องบอกว่ามันเป็นตัวเปลี่ยนเกม! ป>
ให้ฉันอธิบายให้คุณฟัง:
ประเด็นสำคัญ:
เราเริ่มต้นด้วยการสร้างคลาสทดสอบและเขียนฟังก์ชันทดสอบที่ว่างเปล่า แต่ตอนนี้เป็นหน้าที่ของคุณที่จะต้องทำให้เสร็จ (หรือคุณสามารถข้ามไปยัง repo ที่แชร์ได้ทันที :) ) ป>
อาจดูแปลกเล็กน้อยที่มีการทดสอบที่ไม่ได้ทำอะไรเลย แต่ทั้งหมดนี้เป็นส่วนหนึ่งของแผน
ต่อไป เราได้วางแผนการที่ชัดเจนเกี่ยวกับสิ่งที่ฟีเจอร์ของเราควรทำ โดยอิงตามแผนภูมิการไหลของข้อมูล สิ่งนี้ช่วยให้เราเข้าใจข้อกำหนดก่อนที่จะเริ่มใช้งาน
เมื่อมีแผนอยู่ในมือแล้ว เราจึงเริ่มใช้ส่วนประกอบที่จำเป็น (ViewModel ในกรณีนี้) เพื่อให้แน่ใจว่าการทดสอบของเราล้มเหลวก่อน ใช่แล้ว การทดสอบที่ล้มเหลวเป็นสิ่งที่ดีใน TDD!
เราค่อยๆ เชื่อมต่อส่วนต่างๆ เข้าด้วยกัน เช่น การตั้งค่า
นอกจากนี้เรายังจัดการกับองค์ประกอบ UI ซึ่งช่วยให้ผู้ใช้สามารถป้อนความเร็วการเลื่อนที่ต้องการได้ และตรวจสอบให้แน่ใจว่าขนาดตัวอักษรได้รับการปรับขนาดตามนั้น (ตรวจสอบ repo ของโครงการ)
ตลอดกระบวนการ เราตรวจสอบให้แน่ใจว่าโค้ดของเราสะอาดและเรียบง่าย โดยมุ่งเน้นที่สิ่งจำเป็นเพื่อให้การทดสอบผ่าน ไม่มีความซับซ้อนที่ไม่จำเป็นที่นี่!
ในตอนท้าย เรามีคุณลักษณะ "การตั้งค่าขนาดแบบอักษรอัตโนมัติ" ของเราพร้อมทำงาน พร้อมด้วยการทดสอบที่ผ่านการทดสอบอย่างยอดเยี่ยม
โปรดจำไว้ว่า TDD ไม่ได้เกี่ยวกับการเร่งทดสอบหรือการเขียนโค้ดอย่างบ้าคลั่ง มันเกี่ยวกับการไตร่ตรองและรอบคอบในกระบวนการพัฒนาของคุณ ซึ่งจะให้ผลตอบแทนมหาศาลในระยะยาว
ดังนั้น หากคุณต้องการยกระดับเกมการพัฒนาซอฟต์แวร์ของคุณ ลองลองใช้ TDD ดูสิ! เป็นแนวทางที่มีประสิทธิภาพซึ่งจะทำให้โค้ดของคุณแข็งแกร่งขึ้น ลดข้อบกพร่อง และทำให้คุณเป็นนักพัฒนาโดยรวมที่ดีขึ้น
สิ่งที่ฉันแชร์คือวิธีที่เราทำงานในทีมและสิ่งนี้ได้ผลสำหรับเรา แต่นั่นไม่ใช่โซลูชันที่สมบูรณ์แบบสำหรับทุกทีม/บริษัท คุณต้องค้นหาว่าเป็นของคุณหรือไม่ โปรดแจ้งให้เราทราบหากคุณคิดว่าฉันสามารถปรับปรุงโซลูชันนี้ได้
ขอให้มีความสุขในการเขียนโค้ด! 🚀
เรียนรู้การเขียนโค้ดฟรี หลักสูตรโอเพ่นซอร์สของ freeCodeCamp ช่วยให้ผู้คนมากกว่า 40,000 คนได้งานในตำแหน่งนักพัฒนา เริ่มต้น 09รหัส> คลาสจะจัดการตรรกะสำหรับการคำนวณและจัดเก็บ 13 ขึ้นอยู่กับความเร็วการเลื่อนที่เลือก ป> 25 เก็บ 38 ค่า45 มีวิธีจัดเก็บและเรียกข้อมูล 58 ค่าโดยใช้ 65 กลไก เมื่อใดก็ตามที่มี 76 ใหม่ ถูกส่งไปยังที่เก็บข้อมูล สิ่งที่สังเกตได้ใดๆ จะได้รับการปล่อยก๊าซเรือนกระจกด้วยค่าล่าสุด81 มีอินสแตนซ์ของ 99 ที่ถูกเรียกเมื่อใดก็ตามที่ผู้ใช้อัปเดตความเร็วการเลื่อน ซึ่งจะทริกเกอร์การคำนวณใหม่ของ 108 และจัดเก็บผ่านพื้นที่เก็บข้อมูล117 หรือส่วนประกอบ UI ที่กำหนดเอง (เราจะไม่พูดถึงส่วนเหล่านี้)วิธีการเขียนแบบทดสอบ
121รหัส> 130รหัส> 144รหัส> 159 ไดเร็กทอรีของซอร์สโค้ดตามพาธแพ็กเกจเดียวกันกับโค้ดฟีเจอร์จริง163 ประเภท ป> 174 โดยที่ 184 คือชื่อของส่วนประกอบที่ทดสอบใช้ IDE เพื่อสร้างไฟล์ ป>
@Test fun `strategy A`(){
// Arrange
// Act
// Assert
}
@Test fun `strategy B`(){
// Given
// When
// Then
}
class UserProfileViewModel is tested for` {
// Unimplemented Class
val viewModel = UserProfileViewModel()
@Test
fun Font Ratio is fetched from data source`(){}
@Test
fun `Scroll Speed update is called so fontSize calculations are triggered`() {}
@Test
fun `Font Ratio is updated with new emissions from data source`() {}
}
การทดสอบล้มเหลวเนื่องจากเป้าหมายการทดสอบหายไป ป>
ขั้นตอนแรกในรอบ TDD ป>
การดำเนินการ TDD:สร้างไฟล์เป้าหมายผ่านไฟล์ UnitTest ป>
เลือกปลายทางที่ถูกต้องสำหรับไฟล์ใหม่ ป>
แสดงการทดสอบที่ผ่านการทดสอบ ป>
กรณีทดสอบที่ว่างเปล่าน้อยที่สุด ป>
196 ของเรา คลาสทดสอบ เรามีฟังก์ชันทดสอบหน่วยว่าง ลองใช้อันนั้นกัน207 ). ป> ใช้งานร่างกายภายใน ป>
สร้างพารามิเตอร์ที่ขาดหายไปโดยใช้ไดอะล็อก IDE ป>
219 ตัวแปรภายใน Repository ป> class `UserProfileViewModel is tested for` {
init {
Dispatchers.setMain(Dispatchers.Unconfined)
}
val mockUserRepository = mockk<UserRepository>()
@Test
fun `Font Ratio is fetched from data source`() = runTest {
// Given
val expectedRatio = 2.0f
every { mockUserRepository.fontRatio } returns flowOf(FontRatioUiModel(expectedRatio))
val viewModel = UserProfileViewModel(userRepository = mockUserRepository)
// When
viewModel.fontRatio.test {
val fromDataSource = expectItem()
// Then
assertEquals(/* expected = */ expectedRatio, /* actual = */ fromDataSource.fontRatio)
}
}
...
}
224 ภายใน 234 . ป> class UserProfileViewModel(
userRepository: UserRepository
) : ViewModel() {
val fontRatio: StateFlow<FontRatioUiModel> = userRepository.fontRatio.stateIn(
initialValue = FontRatioUiModel(DEFAULT_FONT_RATIO),
scope = viewModelScope,
started = SharingStarted.Lazily
)
companion object {
private const val DEFAULT_FONT_RATIO = 1.0f
}
}
ผ่านการทดสอบหลังจากใช้งานโค้ดหลักเพียงเล็กน้อย ป>
246 ไปใช้งาน ส่วนประกอบ แต่เฉพาะส่วนที่จำเป็นเท่านั้น ทำเช่นเดียวกันกับกรณีทดสอบที่เหลือ ป> ซอร์สโค้ด
บทสรุป
258 คลาสเพื่อจัดการการคำนวณขนาดตัวอักษรตามความเร็วในการเลื่อนอัตโนมัติ (ตรวจสอบ repo โปรเจ็กต์)