Test Doubles — ชนิดของวัตถุทดแทนและการประยุกต์ใช้

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-04-10 เวลาอ่าน: 9 นาที

Test Doubles คือวัตถุทดแทนที่ใช้ในการทดสอบหน่วยแทนการพึ่งพาจริง คำนี้ถูกนำเสนอโดย Gerard Meszaros ในหนังสือ “xUnit Test Patterns” (2007) เป็นแนวคิดทั่วไปสำหรับ Mock, Stub, Fake, Spy และ Dummy ตามข้อมูลจาก Martin Fowler (2024) Test Doubles ช่วยให้แยกคอมโพเนนต์ที่กำลังทดสอบออกจากสภาพแวดล้อม ทำให้การทดสอบเป็นแบบกำหนดได้ รวดเร็ว และเป็นอิสระจากบริการภายนอก

ประเด็นสำคัญ

  • Test Doubles — คำทั่วไปสำหรับวัตถุทดแทนทุกประเภทในการทดสอบ
  • Mock ตรวจสอบปฏิสัมพันธ์: เมธอดใดถูกเรียกและด้วยอาร์กิวเมนต์ใด
  • Stub คืนค่าที่กำหนดไว้ล่วงหน้าโดยไม่ต้องตรวจสอบการเรียก
  • Fake — การใช้งานที่เรียบง่ายแต่ทำงานได้ (เช่น ฐานข้อมูลในหน่วยความจำ)
  • Spy บันทึกการเรียกเพื่อตรวจสอบภายหลัง Dummy เติมพารามิเตอร์

Test Doubles คืออะไร?

Test Doubles เป็นคำที่มาจากอุตสาหกรรมยานยนต์ (สตันต์ดับเบิล) ถูกนำมาใช้ในการพัฒนาซอฟต์แวร์ เช่นเดียวกับสตันต์ดับเบิลที่แทนที่นักแสดงในฉากอันตราย Test Double ก็แทนที่คอมโพเนนต์จริงในสถานการณ์ทดสอบ ซึ่งจำเป็นเมื่อการพึ่งพาจริงไม่พร้อมใช้งาน ช้า ไม่แน่นอน หรือมีผลข้างเคียง

แนวคิดของ Test Double ครอบคลุม ห้าประเภทเฉพาะ แต่ละประเภทแก้ไขงานของตัวเอง การจำแนกประเภทของ Meszaros เป็นมาตรฐานและใช้ในคู่มือการทดสอบสมัยใหม่ทั้งหมด ความแตกต่างระหว่างประเภทอยู่ที่ระดับการควบคุมและการตรวจสอบ: ตั้งแต่การเติมพารามิเตอร์อย่างง่าย (Dummy) ไปจนถึงการตรวจสอบลำดับการเรียกอย่างสมบูรณ์ (Mock)

ทำไมต้องใช้ Test Doubles

วัตถุประสงค์หลักของ Test Doubles คือการแยกโมดูลที่กำลังทดสอบ ในการพัฒนาแอปมือถือ การพึ่งพาจริงประกอบด้วยเซิร์ฟเวอร์ API ฐานข้อมูล ระบบไฟล์ เซนเซอร์อุปกรณ์ และบริการระบบ (LocationManager, Camera, Bluetooth) การใช้คอมโพเนนต์เหล่านี้โดยตรงทำให้การทดสอบช้า เปราะบาง และขึ้นอยู่กับสภาพแวดล้อม ตาม Google Testing Blog (2023) การทดสอบหน่วยที่แยกได้ดีจะทำงานภายใน มิลลิวินาที ในขณะที่การทดสอบแบบบูรณาการจะทำงานภายในวินาทีและนาที

Test Doubles ห้าประเภท

การจำแนกประเภทของ Gerard Meszaros ประกอบด้วย Test Doubles ห้าประเภท ซึ่งแตกต่างกันในพฤติกรรมและวัตถุประสงค์ การเข้าใจความแตกต่างระหว่างประเภทเหล่านี้เป็นพื้นฐานของการทดสอบหน่วยที่มีประสิทธิภาพ

Dummy

Dummy คือวัตถุที่ถูกส่งไปยังเมธอดที่กำลังทดสอบแต่ไม่เคยถูกใช้ Dummy จำเป็นเพียงเพื่อให้ตรงกับลายเซ็นของเมธอด ใน Kotlin ซึ่งมักจะเป็น null, emptyList() หรือวัตถุที่มีสตับ Dummy ไม่ควรมีตรรกะใดๆ — หากถูกเรียก การทดสอบควรล้มเหลว

Fake

Fake คือการทำงานที่เรียบง่ายแต่ใช้งานได้ของอินเทอร์เฟซ ต่างจาก Mock และ Stub ตรงที่ Fake มีตรรกะทางธุรกิจจริง แต่อยู่ในรูปแบบที่เรียบง่าย ตัวอย่างคลาสสิกคือ InMemoryUserRepository ซึ่งเก็บข้อมูลใน HashMap แทนฐานข้อมูล Fake ใช้เมื่อคุณต้องการทดสอบตรรกะที่ขึ้นอยู่กับสถานะ แต่ไม่มีค่าใช้จ่ายของโครงสร้างพื้นฐานจริง

ประเภทวัตถุประสงค์ตัวอย่าง
Dummyเติมพารามิเตอร์null, วัตถุว่าง
Fakeการทำงานที่เรียบง่ายที่ใช้งานได้InMemoryRepository
Stubคืนค่าคงที่when(api.getUser()).thenReturn(user)
Spyบันทึกการเรียกเพื่อตรวจสอบverify(spy).save(user)
Mockตรวจสอบปฏิสัมพันธ์verify(mock).sendEmail(email)

Stub

Stub คืนค่าที่กำหนดไว้ล่วงหน้าสำหรับการเรียกเฉพาะ Stub ไม่ตรวจสอบว่าถูกเรียกหรือไม่ — มันเพียงให้ข้อมูล ใน Mockito Stub ถูกสร้างผ่าน when(method).thenReturn(value) Stub เหมาะสำหรับการทดสอบเมื่อคุณต้องการให้การพึ่งพาคืนค่าเฉพาะ แต่การเรียกนั้นไม่สำคัญ

Spy

Spy คือตัวห่อรอบวัตถุจริงที่บันทึกการเรียกทั้งหมดเพื่อตรวจสอบภายหลัง ต่างจาก Mock ตรงที่ Spy ส่งต่อการเรียกไปยังวัตถุจริงแต่อนุญาตให้ตรวจสอบว่าเกิดขึ้นหรือไม่ ใน Mockito Spy ถูกสร้างผ่าน spy(realObject) Spy มีประโยชน์สำหรับการ mock บางส่วน เมื่อคุณต้องการใช้วัตถุจริงแต่ตรวจสอบการเรียกบางส่วน

Mock

Mock คือวัตถุที่มีความคาดหวังการเรียกที่กำหนดไว้ล่วงหน้า Mock ตรวจสอบว่าเมธอดเฉพาะถูกเรียกด้วยอาร์กิวเมนต์เฉพาะและในลำดับเฉพาะ ต่างจาก Stub ตรงที่ Mock มุ่งเน้นที่ การตรวจสอบพฤติกรรม มากกว่าการคืนข้อมูล Mock เป็นประเภท Test Double ที่ทรงพลังที่สุดและใช้บ่อยที่สุดในการพัฒนาแอปมือถือ

Mock กับ Stub: ความแตกต่างหลัก

ความแตกต่างระหว่าง Mock และ Stub มักทำให้เกิดความสับสนแม้ในหมู่นักพัฒนาที่มีประสบการณ์ ความแตกต่างหลักอยู่ที่วัตถุประสงค์: Stub ตรวจสอบสถานะ (state verification) Mock ตรวจสอบพฤติกรรม (behavior verification)

Stub ตอบคำถาม: “โค้ดคืนผลลัพธ์ที่ถูกต้องหรือไม่?” Mock ตอบคำถาม: “โค้ดเรียกเมธอดที่ถูกต้องด้วยอาร์กิวเมนต์ที่ถูกต้องหรือไม่?” ในการพัฒนาแอปมือถือ Stub ใช้เมื่อผลลัพธ์สำคัญ (เช่น ข้อมูลจากที่เก็บ) ในขณะที่ Mock ใช้เมื่อผลข้างเคียงสำคัญ (เช่น การส่งอีเมล การเขียนฐานข้อมูล)

kotlin
// Stub: การตรวจสอบสถานะ
every { repository.getUsers() } returns listOf(user)
val result = useCase.getUsers()
assertEquals(1, result.size)

// Mock: การตรวจสอบพฤติกรรม
every { analytics.logEvent("purchase") } returns Unit
useCase.purchase(item)
verify { analytics.logEvent("purchase") }

ตัวอย่าง Test Doubles ใน Kotlin

ตัวอย่างเชิงปฏิบัติของ Test Doubles ทั้งห้าประเภทใน Kotlin โดยใช้ MockK — ไลบรารี mocking ที่ได้รับความนิยมมากที่สุดสำหรับโปรเจกต์ Android

Fake: InMemoryUserRepository

kotlin
class InMemoryUserRepository : UserRepository {
    private val store = mutableMapOf<String, User>()

    override fun save(user: User) {
        store[user.email] = user
    }

    override fun findByEmail(email: String): User? {
        return store[email]
    }
}

Stub + Mock: ทดสอบ UseCase

kotlin
class RegisterUseCaseTest {
    private val api = mockk<AuthApi>()
    private val repo = spyk(InMemoryUserRepository())
    private val useCase = RegisterUseCase(api, repo)

    fun `register user successfully`() = runTest {
        // Stub: คืนการตอบสนอง API คงที่
        coEvery { api.register("test@test.com") } returns AuthResult.Success("token123")

        val result = useCase.execute("test@test.com")

        // Verify: ตรวจสอบว่าผู้ใช้ถูกบันทึกแล้ว
        verify { repo.save(any()) }
        assertTrue(result.isSuccess())
    }
}

Dummy: ทดสอบด้วยพารามิเตอร์ที่ไม่ได้ใช้

kotlin
data class Logger(val appContext: Context, val format: FormatType)

fun `test logger with dummy context`() {
    // Dummy: Context ไม่ได้ใช้ภายใน Logger
    val dummyContext = mockk<Context>()
    val logger = Logger(dummyContext, FormatType.JSON)
    assertEquals(FormatType.JSON, logger.format)
}

เมื่อใดควรใช้แต่ละประเภทในการพัฒนาแอปมือถือ

การเลือกประเภท Test Double ขึ้นอยู่กับสิ่งที่กำลังทดสอบ: สถานะ พฤติกรรม หรือการบูรณาการ ในการพัฒนาแอปมือถือ Android และ iOS ได้มีการกำหนดคำแนะนำดังต่อไปนี้

สำหรับ ViewModel และ UseCase

เมื่อทดสอบ ViewModel ให้ใช้ Mock สำหรับการพึ่งพาที่สร้างผลข้างเคียง (ที่เก็บข้อมูล, การวิเคราะห์, การนำทาง) และ Stub สำหรับการพึ่งพาที่คืนข้อมูล (ไคลเอ็นต์ API, ContentProvider) ซึ่งช่วยให้ตรวจสอบว่า ViewModel จัดการทั้งสถานการณ์สำเร็จและข้อผิดพลาดได้อย่างถูกต้อง

สำหรับ Repository และชั้นข้อมูล

ในระดับ Repository ให้เลือกใช้ Fake (การใช้งานฐานข้อมูลในหน่วยความจำ) และ Stub (การตอบสนอง API คงที่) Fake ช่วยให้ทดสอบตรรกะแคชและโหมดออฟไลน์โดยไม่ต้องตั้งค่า SQLite Stub จำลองสถานะ HTTP ต่างๆ: 200, 404, 500, หมดเวลา

  • การทดสอบหน่วยของตรรกะทางธุรกิจ — Mock สำหรับการพึ่งพาภายนอกทั้งหมด, Dummy สำหรับพารามิเตอร์ที่ไม่ได้ใช้
  • การทดสอบแบบบูรณาการ — Fake แทน Mock (ตรวจสอบว่าคอมโพเนนต์ทำงานร่วมกัน)
  • การทดสอบ UI — Stub สำหรับการตอบสนอง API (ผ่าน MockWebServer หรือ WireMock)
  • การทดสอบแคช — Fake สำหรับฐานข้อมูล (ในหน่วยความจำแทน Room/SQLite)
  • การทดสอบอะซิงโครนัส — Mock ที่รองรับคอร์รูทีน (MockK + Turbine สำหรับ Flow)

ข้อผิดพลาดทั่วไปเมื่อใช้วัตถุทดแทน

การใช้ Test Doubles อย่างไม่ถูกต้องเป็นหนึ่งในสาเหตุที่พบบ่อยที่สุดของการทดสอบที่เปราะบางซึ่งพังทุกครั้งที่มีการปรับโครงสร้างโค้ด

การ Mock มากเกินไป: การใช้ Mock มากเกินไป

ข้อผิดพลาดที่พบบ่อยที่สุดคือการ mock ทุกอย่าง หากทุกการพึ่งพาในการทดสอบถูกแทนที่ด้วย Mock การทดสอบจะหยุดตรวจสอบพฤติกรรมจริง Mock ควรใช้สำหรับการพึ่งพาภายนอกเท่านั้น (เครือข่าย, ฐานข้อมูล, ระบบไฟล์, บริการระบบ) คอมโพเนนต์ภายในแอปพลิเคชัน (Value Object, data class, ยูทิลิตี้ธรรมดา) ไม่ควรถูกแทนที่

การระบุไม่เพียงพอ: ข้อมูลจำเพาะไม่เพียงพอ

ข้อผิดพลาดที่สองคือการสร้าง Mock โดยไม่กำหนดความคาดหวัง หากเมธอดถูกเรียกโดยไม่มี every / when Mock จะคืนค่าเริ่มต้น (null, 0, false) ซึ่งอาจนำไปสู่การทดสอบผลบวกลวง ที่ Mock คืนค่า null อย่างเงียบๆ และการทดสอบตีความว่าเป็นพฤติกรรมที่ถูกต้อง

การตรวจสอบมากเกินไป: การใช้ Verify มากเกินไป

ข้อผิดพลาดที่สามคือการตรวจสอบทุกการเรียกของทุก Mock Verify ควรใช้เฉพาะสำหรับการเรียกที่สำคัญอย่างยิ่งจากมุมมองของตรรกะทางธุรกิจ การตรวจสอบมากเกินไปทำให้การทดสอบเปราะบาง: การเปลี่ยนลำดับการเรียกในโค้ดการผลิตทำให้การทดสอบพังโดยไม่เปลี่ยนพฤติกรรม

คำถามที่พบบ่อย

ความแตกต่างระหว่าง Mock และ Stub คืออะไร?

Stub คืนข้อมูลและตรวจสอบสถานะ (สิ่งที่ถูกคืน) ในขณะที่ Mock ตรวจสอบพฤติกรรม (เมธอดใดถูกเรียก) Stub = “คืน X”, Mock = “ตรวจสอบว่า Y ถูกเรียกด้วยอาร์กิวเมนต์ Z” ในการทดสอบจริง วัตถุหนึ่งๆ มักทำหน้าที่เป็นทั้ง Stub และ Mock พร้อมกัน

เมื่อใดควรใช้ Fake แทน Mock?

Fake ดีกว่า Mock เมื่อทดสอบตรรกะที่ขึ้นอยู่กับสถานะ: แคช, โหมดออฟไลน์, ธุรกรรม Fake (การทำงานในหน่วยความจำ) ช่วยให้ทดสอบสถานการณ์เหล่านี้ได้โดยไม่ต้องใช้การเรียก verify ที่เปราะบาง Mock เหมาะสมกว่าสำหรับตรวจสอบการส่งข้อมูล: การวิเคราะห์, push, อีเมล

ไลบรารี Test Doubles ใดดีที่สุดสำหรับ Android?

สำหรับโปรเจกต์ Android ใน Kotlin แนะนำ MockK รองรับคอร์รูทีน, ฟังก์ชัน suspend, คลาส sealed และฟังก์ชันส่วนขยายโดยไม่ต้องกำหนดค่าเพิ่มเติม สำหรับโปรเจกต์ Java Mockito ยังคงเป็นมาตรฐาน — ไลบรารียอดนิยมที่มีเอกสารครอบคลุม

วิธีทดสอบ Kotlin Flow ด้วย Test Doubles?

ในการทดสอบ Kotlin Flow ให้ใช้ไลบรารี Turbine ร่วมกับ MockK Turbine ช่วยให้การตรวจสอบการปล่อย Flow ง่ายขึ้น: คุณสามารถตรวจสอบลำดับของค่า การสิ้นสุดสตรีม และข้อยกเว้น Stub สำหรับ Flow คืน flowOf(value) Mock ตรวจสอบว่า Flow ถูกรวบรวม

การใช้ Test Doubles ในการทดสอบ UI เป็นที่ยอมรับหรือไม่?

ได้ แต่ในระดับ การตอบสนอง API ไม่ใช่คอมโพเนนต์ UI ไลบรารี MockWebServer (OkHttp) และ WireMock ช่วยให้จำลองการตอบสนอง HTTP ในการทดสอบ UI คอมโพเนนต์ UI เอง (Compose, SwiftUI Views) ไม่ควรถูกแทนที่ — พฤติกรรมของพวกมันถูกทดสอบผ่านการทดสอบภาพหน้าจอและ Espresso

สรุป

  • Test Doubles — คำทั่วไปสำหรับวัตถุทดแทนห้าประเภท: Mock, Stub, Fake, Spy, Dummy
  • Mock ตรวจสอบพฤติกรรม (verify), Stub คืนข้อมูล (thenReturn), Fake ทำหน้าที่เป็นการใช้งานจริงที่เรียบง่าย
  • Spy ห่อวัตถุจริงและบันทึกการเรียก, Dummy เติมพารามิเตอร์ที่ไม่ได้ใช้
  • การจำแนกประเภทของ Gerard Meszaros เป็นการจำแนกมาตรฐานที่ใช้ในเฟรมเวิร์ก mocking สมัยใหม่ทั้งหมด
  • สำหรับโปรเจกต์ Kotlin แนะนำ MockK, สำหรับ Java — Mockito, สำหรับ iOS — Cuckoo หรือ OHHTTPStubs
  • ข้อผิดพลาดทั่วไป: การ mock มากเกินไป (แทนที่ทุกอย่าง), การระบุไม่เพียงพอ (ความคาดหวังไม่ชัดเจน), การตรวจสอบมากเกินไป (verify มากไป)
  • Fake ดีกว่า Mock เมื่อทดสอบตรรกะที่มีสถานะ — แคช, โหมดออฟไลน์ และธุรกรรม

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม