Stub: มันคืออะไร ประเภทและการใช้งานในการทดสอบ

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

Stub (สตับ, ตัวแทน) — ออบเจกต์ทดสอบที่ส่งคืนการตอบสนองที่กำหนดไว้ล่วงหน้าสำหรับการเรียกเมธอดแทนการทำงานจริง ในการพัฒนาแอปมือถือ สตับแยกโมดูลที่กำลังทดสอบออกจากคำขอเครือข่าย ฐานข้อมูล และระบบไฟล์ ทำให้สามารถตรวจสอบตรรกะได้โดยไม่ต้องตั้งค่าสภาพแวดล้อม ต่างจาก mock สตับไม่ได้ตรวจสอบพฤติกรรม — มันเพียงให้ข้อมูลเท่านั้น รายละเอียดเพิ่มเติม — ใน บทความของ Martin Fowler เกี่ยวกับ test doubles.

สาระสำคัญ

  • Stub — สตับส่งคืนค่าที่กำหนดไว้ล่วงหน้าสำหรับการเรียกเมธอดโดยไม่มีตรรกะ
  • การแยก — สตับปิดการพึ่งพาจริง: API, DB, ไฟล์, เซนเซอร์
  • ต่างจาก Mock — สตับไม่ตรวจสอบการเรียก มันเพียงแทนที่การตอบสนอง
  • Android — MockWebServer (OkHttp) เป็นสตับสำหรับ HTTP, MockK.constantAnswer สำหรับ Kotlin
  • iOS — OCMock และโปรโตคอล Swift ที่มี implementation สำหรับทดสอบเป็นสตับ

Stub คืออะไรและต่างจาก test doubles อื่นอย่างไร?

Stub — คือออบเจกต์ตัวแทนที่แทนที่การพึ่งพาจริงในการทดสอบและส่งคืนค่าที่กำหนดไว้ล่วงหน้าสำหรับการเรียกเฉพาะ คำศัพท์นี้ถูกนำเสนอในการจำแนกของ Gerard Meszaros (2007) ในหนังสือ “xUnit Test Patterns” Stub อยู่ในหมวด test doubles — ออบเจกต์ที่แทนที่คอมโพเนนต์จริงระหว่างการทดสอบ วัตถุประสงค์หลักของสตับคือการให้ข้อมูลที่คาดเดาได้แก่บล็อกที่กำลังทดสอบ โดยขจัดความไม่แน่นอนของระบบภายนอก

หลักการทำงาน — การทดสอบกำหนดค่าสตับก่อนดำเนินการ: “เมื่อเรียกเมธอด getUsers() ให้ส่งคืนรายชื่อผู้ใช้ชุดนี้” Stub ไม่มีตรรกะทางธุรกิจ ไม่ตรวจสอบลำดับการเรียก และไม่บันทึกประวัติการเข้าถึง มันเพียงยืนอยู่แทนคอมโพเนนต์จริงและส่งออกสิ่งที่ถูกสั่งให้ทำ ในบริบทของการทดสอบ Android หมายความว่า: OkHttp-client ไม่ส่งคำขอจริงไปยังเซิร์ฟเวอร์ แต่รับการตอบสนองจาก MockWebServer ที่กำหนดค่าเป็นสตับ

  • Stub — ส่งคืนข้อมูล ไม่ตรวจสอบการเรียก
  • Mock — ส่งคืนข้อมูลและตรวจสอบพฤติกรรม (verify)
  • Fake — implementation แบบง่ายที่ทำงานได้พร้อมตรรกะจริง
  • Spy — ห่อหุ้มออบเจกต์จริง บันทึกการเรียก
  • Dummy — ถูกส่งผ่านแต่ไม่ถูกใช้ (null, ออบเจกต์ว่าง)

ควรใช้เมื่อใด — สตับเหมาะสมที่สุดสำหรับการทดสอบเลเยอร์ UI (ViewModel, Presenter) และตรรกะทางธุรกิจ (UseCase, Interactor) ซึ่งต้องตรวจสอบปฏิกิริยาต่อข้อมูลเฉพาะ: รายการว่าง, เซิร์ฟเวอร์ส่งคืนข้อผิดพลาด 500, โทเคนหมดอายุ เคสใดก็ตามที่การทดสอบต้องการสถานะอินพุตที่แน่นอน — นั่นคืองานของสตับ สำหรับแต่ละสถานการณ์ทดสอบจะสร้างการกำหนดค่าสตับของตัวเอง ทำให้การทดสอบอ่านง่ายและคาดเดาได้

การจำแนก test doubles ตาม Meszaros

Gerard Meszaros (2007) ในหนังสือ “xUnit Test Patterns” ได้ระบุ test doubles ห้าประเภท: dummy, stub, spy, mock, fake แต่ละประเภทแก้ไขงานของตัวเอง Dummy — ถูกส่งผ่านแต่ไม่ถูกใช้ Stub — ส่งคืนข้อมูล Spy — บันทึกการเรียก Mock — ตรวจสอบพฤติกรรม Fake — มีตรรกะแบบย่อ ความเข้าใจในการจำแนกนี้ช่วยให้นักพัฒนาเลือกเครื่องมือที่ถูกต้องสำหรับแต่ละสถานการณ์ทดสอบ

ตำแหน่งที่ใช้สตับในการทดสอบแอปพลิเคชันมือถือ

สตับสำหรับคำขอเครือข่าย

คำขอเครือข่าย — สถานการณ์การใช้สตับที่พบบ่อยที่สุด แอปพลิเคชันทำการเรียก HTTP ไปยัง API และในการทดสอบต้องตรวจสอบปฏิกิริยาต่อการตอบสนองต่าง ๆ: JSON ที่สำเร็จ, ข้อผิดพลาด 401 (ไม่ได้รับอนุญาต), หมดเวลา, อาร์เรย์ว่าง MockWebServer (OkHttp) บน Android และ URLProtocol (iOS) ทำหน้าที่เป็นสตับ โดยส่งคืนการตอบสนอง HTTP ที่กำหนดไว้ล่วงหน้าโดยไม่ต้องเชื่อมต่อกับเซิร์ฟเวอร์จริง ทำให้การทดสอบเร็วขึ้นจากวินาทีเป็นมิลลิวินาที

ฐานข้อมูล — Room (Android) และ CoreData (iOS) มีเวอร์ชัน in-memory แต่การตั้งค่ายังคงใช้เวลา Stub แทน repository ส่งคืนรายการ Entity ที่เตรียมไว้ล่วงหน้าโดยไม่แตะต้อง DB โดยเฉพาะอย่างยิ่งมีประสิทธิภาพสำหรับการทดสอบ ViewModel ซึ่งต้องตรวจสอบการเรียงลำดับ การกรอง หรือการแปลงข้อมูล การทดสอบทำงานภายในมิลลิวินาทีโดยไม่คำนึงถึงปริมาณข้อมูล

บริการระบบ — LocationManager, SensorManager, SharedPreferences ต้องใช้อุปกรณ์จริงหรืออีมูเลเตอร์ Stub สำหรับ LocationProvider ส่งคืนพิกัดที่กำหนด สำหรับ SensorManager — ค่าอะคเซเลอโรมิเตอร์คงที่ บน iOS สิ่งที่เทียบเท่าคือ CLLocationManager พร้อม implementation ของ delegate สำหรับทดสอบ หากไม่มีสตับ การทดสอบเหล่านี้ต้องใช้อุปกรณ์กายภาพที่มีเงื่อนไขเฉพาะ

ระบบไฟล์และแคช — การโหลดรูปภาพ การแคชการตอบสนอง การทำงานกับไฟล์การกำหนดค่า — การดำเนินการทั้งหมดนี้ขึ้นอยู่กับสถานะของดิสก์ Stub สำหรับ FileManager หรือ ImageCache ส่งคืนความสำเร็จ/ข้อผิดพลาดโดยไม่ต้องอ่านไฟล์จริง ซึ่งช่วยกำจัดการพังปลอมของการทดสอบเนื่องจากเส้นทางหรือสิทธิ์การเข้าถึงไม่ตรงกันบนเครื่องของนักพัฒนาที่ต่างกัน

Stub vs Mock vs Fake: ความแตกต่างที่สำคัญ

การแบ่งความรับผิดชอบ — test doubles สามประเภทแก้ไขงานที่แตกต่างกัน Stub: “ให้ข้อมูลฉัน”. Mock: “ตรวจสอบว่าฉันถูกเรียก”. Fake: “ฉันทำงานเหมือนของจริงเพียงง่ายกว่า” ความแตกต่างสำคัญอย่างยิ่งต่อความอ่านง่ายของการทดสอบ: หากการทดสอบใช้ mock ในที่ที่ต้องการ stub การทดสอบจะถูกถ่วงด้วยการเรียก verify ที่ไม่เกี่ยวข้องกับสถานการณ์ที่ตรวจสอบ

คุณลักษณะStubMockFake
วัตถุประสงค์ให้ข้อมูลตรวจสอบปฏิสัมพันธ์implementation แบบย่อ
ตรรกะไม่มีไม่มีมี (แต่แบบย่อ)
การตรวจสอบไม่มีมี (verify)ทางอ้อม (ผ่านสถานะ)
ความยืดหยุ่นต่ำ — การตอบสนองตายตัวปานกลางสูง — ตรรกะปรับตัวได้
ความเร็วสูงสุดสูงปานกลาง
ตัวอย่างMockWebServer ส่งคืน JSONMockito.verify(repository).save()InMemoryRepository ด้วย HashMap

กฎเชิงปฏิบัติ — หากการทดสอบตรวจสอบว่าคอมโพเนนต์ที่กำลังทดสอบได้รับข้อมูลใด — ใช้ stub หากการทดสอบตรวจสอบว่าคอมโพเนนต์เรียกเมธอดของการพึ่งพาด้วยอาร์กิวเมนต์ที่ถูกต้องหรือไม่ — ใช้ mock หากคุณเพียงต้องการแทนที่ DB ด้วยตารางแฮช — นั่นคือ fake การผสมประเภทในการทดสอบเดียวทำให้การทดสอบเปราะ: เมื่อเปลี่ยน implementation จะต้องเขียนทั้ง stub และตรรกะ verify ใหม่

ปฏิยาธิปไตย: Stub กับ verify

Stub กับ verify — ข้อผิดพลาดที่พบบ่อยเมื่อนักพัฒนากำหนดค่า stub แล้วเพิ่ม verify(stub).method() ตามนิยาม สตับไม่ควรถูกตรวจสอบ — สำหรับการตรวจสอบมี mock หากคุณต้องตรวจสอบว่าเมธอดถูกเรียกด้วยอาร์กิวเมนต์เฉพาะ ใช้ Mockito.mock() แทน Mockito.stub() การแยกนี้ทำให้เจตนาของการทดสอบชัดเจนสำหรับนักพัฒนาคนอื่น

การสร้างสตับบน Android ด้วย MockWebServer และ MockK

MockWebServer — ไลบรารี OkHttp สำหรับสร้าง HTTP-สตับบน Android และ JVM มันรันเซิร์ฟเวอร์ HTTP ภายในบนพอร์ตที่ระบุ ซึ่งสกัดกั้นคำขอของ OkHttp-client และส่งคืนการตอบสนองที่กำหนดไว้ล่วงหน้า การตั้งค่าใช้เพียงสามบรรทัด: สร้างเซิร์ฟเวอร์, enqueue การตอบสนอง, เริ่มรัน การทดสอบสามารถ enqueue หลายการตอบสนองตามลำดับสำหรับสถานการณ์ที่มี pagination หรือ retry

kotlin
class UserRepositoryTest {

    private val server = MockWebServer()

    fun setup() {
        server.start(8080)
        val client = OkHttpClient.Builder()
            .readTimeout(1, TimeUnit.SECONDS)
            .build()
    }

    fun test_user_list_success() {
        val json = "[{\"id\":1,\"name\":\"Alice\"}]"
        server.enqueue(MockResponse()
            .setBody(json)
            .setResponseCode(200)
        )
        val result = repository.getUsers()
        assertEquals(1, result.size)
    }

    fun teardown() {
        server.shutdown()
    }
}

MockK — ทางเลือกแทน Mockito สำหรับ Kotlin ที่รองรับ coroutine, extension-function และ sealed-class แบบ first-class สตับใน MockK สร้างผ่าน coEvery (สำหรับ suspend-function) และ every (สำหรับฟังก์ชันปกติ) ต่างจาก MockWebServer MockK สร้างสตับสำหรับเมธอดการพึ่งพาแต่ละเมธอด ไม่ใช่ทั้งเลเยอร์ HTTP สะดวกสำหรับ unit-test ของ UseCase หรือ Interactor ซึ่งการพึ่งพาคือ abstraction ของ repository

kotlin
interface UserRepository {
    suspend fun getUsers(): List<User>
}

class GetUsersUseCaseTest {

    private val repo = mockk<UserRepository>()

    private val useCase = GetUsersUseCase(repo)

    fun test_empty_list() = runTest {
        coEvery { repo.getUsers() } returns emptyList()

        val result = useCase.invoke()

        assertTrue(result.isEmpty())
        coVerify(exactly = 1) { repo.getUsers() }
    }
}

Best practice — สำหรับ integration test ใช้ MockWebServer (สกัดกั้น HTTP จริง) สำหรับ unit-test — MockK (สร้างสตับสำหรับ interface) อย่าสร้างสตับสำหรับสิ่งที่คุณไม่ได้ทดสอบ: หากการทดสอบตรวจสอบ Repository อย่าสร้างสตับสำหรับ OkHttp-client ข้างใน — ใช้ MockWebServer จริงในระดับ HTTP กฎนี้ทำให้การทดสอบเกี่ยวข้องและลดความเปราะเมื่อ refactoring

การสร้างสตับบน iOS ด้วย OCMock และโปรโตคอล

โปรโตคอล Swift เป็นสตับ — ในแนวทาง iOS-native สตับถูกสร้างโดยการแทนที่โครงสร้างสำหรับทดสอบที่สอดคล้องกับโปรโตคอลของการพึ่งพา แทน NetworkService จริง การทดสอบได้รับ StubNetworkService ซึ่งส่งคืนข้อมูลคงที่ Swift เป็นภาษาที่มีชนิดแบบสถิต ดังนั้นสตับต้องสอดคล้องกับโปรโตคอลเดียวกับบริการจริง คอมไพเลอร์รับประกันว่าสตับ implement เมธอดที่จำเป็นทั้งหมด

swift
protocol NetworkServiceProtocol {
    func fetchUsers() async throws -> [User]
}

struct StubNetworkService: NetworkServiceProtocol {
    let result: Result<[User], Error>

    func fetchUsers() async throws -> [User] {
        try result.get()
    }
}

final class UsersViewModelTests: XCTestCase {
    func test_success_state() async {
        let stub = StubNetworkService(
            result: .success([User(name: "Alice")])
        )
        let vm = UsersViewModel(service: stub)
        await vm.load()
        XCTAssertEqual(vm.users.count, 1)
    }
}

OCMock สำหรับ Objective-C — ไลบรารีสำหรับสร้างสตับและม็อกในโปรเจกต์ iOS legacy OCMock รองรับ stub-method ที่มีอาร์กิวเมนต์และค่าส่งคืน โปรเจกต์ Swift สมัยใหม่นิยมแนวทางที่ใช้โปรโตคอลกับสตับที่เขียนเอง — ให้การควบคุมทุกเมธอดและไม่ต้องมีการพึ่งพาภายนอก OCMock ยังคงเป็นทางเลือกสำหรับโปรเจกต์ที่การทำโปรโตคอลทุกการพึ่งพาไม่คุ้มค่า

URLProtocol สำหรับ HTTP-สตับ — กลไกระบบของ iOS สำหรับสกัดกั้นคำขอเครือข่ายผ่านคลาสย่อยของ URLProtocol การทดสอบลงทะเบียน URLProtocol ที่กำหนดเอง ซึ่งสกัดกั้น URLSession และส่งคืน stub-response ข้อดีเหนือสตับที่เขียนเอง: ไม่ต้องเปลี่ยนสถาปัตยกรรมของแอป — URLSession ยังคงจริง แต่ข้อมูลถูกแทนที่ในระดับโปรโตคอล ข้อเสีย: ดีบักยากกว่าบริการสตับที่ชัดเจน

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

Stub ต่างจาก Mock อย่างไร?

Stub ส่งคืนข้อมูลที่กำหนดไว้ล่วงหน้าและไม่ตรวจสอบข้อเท็จจริงของการเรียก Mock เพิ่มการตรวจสอบว่าเมธอดถูกเรียกด้วยอาร์กิวเมนต์ที่ถูกต้อง (verify) Stub ตอบคำถาม “ส่งคืนอะไร”, Mock — ตอบคำถาม “มีการเรียกหรือไม่” ใช้สตับเพื่อตรวจสอบสถานะ ใช้ mock เพื่อตรวจสอบปฏิสัมพันธ์

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

Fake จำเป็นเมื่อการทดสอบต้องการ implementation ที่ทำงานได้ (แม้แบบย่อ) — เช่น ฐานข้อมูล in-memory แทน Room Stub เหมาะสำหรับสถานการณ์เดี่ยวที่มีข้อมูลที่กำหนดไว้ล่วงหน้า หากคุณทำซ้ำสตับเดิมใน 10 การทดสอบ — มีแนวโน้มว่าคุณต้องการ Fake Fake ลดการซ้ำซ้อนเพราะตรรกะอยู่ในคลาสเดียว

สตับเมธอดแบบ static ได้ไหม?

บน Android — MockK สำหรับออบเจกต์ Kotlin (object) รองรับ mockkObject() รวมถึงเมธอด static ของคลาส Java ผ่าน mockkStatic() บน iOS — เมธอด static ของ Swift ไม่สามารถสตับได้โดยตรง; ใช้โปรโตคอลและ DI เพื่อแทนที่การเรียก static ด้วยเมธอด instance ของโปรโตคอล สตับแบบ static คือหนี้ทางเทคนิค ควรหลีกเลี่ยงในโค้ดใหม่

วิธีสตับคำขอเครือข่ายบน Android?

ใช้ MockWebServer (OkHttp) — มันทำงานเหมือนเซิร์ฟเวอร์ HTTP ภายในที่ enqueue การตอบสนอง สำหรับ Retrofit เพียงเปลี่ยน base URL เป็น localhost:8080 สำหรับ Ktor ใช้ MockEngine — กลไกในตัวสำหรับแทนที่ HttpStatement ทั้งสองวิธีทำงานโดยไม่ต้องใช้อินเทอร์เน็ตจริงและให้การควบคุมรหัสสถานะ เนื้อหา และส่วนหัวของการตอบสนองอย่างเต็มที่

Stub vs Spy — ต่างกันตรงไหน?

Spy ห่อหุ้มออบเจกต์จริงและบันทึกการเรียก ในขณะที่ Stub แทนที่ออบเจกต์ทั้งหมดด้วยการตอบสนองคงที่ Spy อนุญาตให้ใช้ implementation จริงบางส่วน (เมธอดอื่นทำงานตามเดิม) แต่สตับไม่ หากคุณต้องตรวจสอบว่าเมธอดถูกเรียก แต่บางส่วนของตรรกะควรทำงาน — ใช้ spy ไม่ใช่ stub

สรุป

  • Stub — ออบเจกต์ตัวแทนที่ส่งคืนการตอบสนองที่กำหนดไว้ล่วงหน้าสำหรับการเรียกเมธอดระหว่างการทดสอบ
  • การแยกการพึ่งพา — สตับแทนที่คำขอเครือข่าย DB บริการระบบ และระบบไฟล์
  • ต่างจาก Mock — สตับไม่ตรวจสอบการเรียก มันเพียงส่งคืนข้อมูลโดยไม่ตรวจสอบพฤติกรรม
  • เครื่องมือ Android — MockWebServer สำหรับ HTTP, MockK สำหรับ interface Kotlin ที่รองรับ coroutine
  • เครื่องมือ iOS — สตับแบบโปรโตคอลบน Swift, URLProtocol สำหรับ HTTP, OCMock สำหรับ Objective-C
  • อย่าผสมบทบาท — อย่าเพิ่ม verify ให้กับสตับ ใช้ mock สำหรับการตรวจสอบการเรียก
  • Stub + MockWebServer — แนวทางมาตรฐานสำหรับ integration test โดยไม่ต้องมีเซิร์ฟเวอร์จริง

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

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

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

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