Fake — คืออะไร วัตถุประสงค์ และวิธีใช้ในการทดสอบ

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

Fake คือการใช้งานที่เรียบง่ายและทำงานได้ของการพึ่งพาที่ทำงานเหมือนส่วนประกอบจริง แต่ใช้พื้นที่จัดเก็บในหน่วยความจำหรือกลไกน้ำหนักเบาอื่นๆ แทนโครงสร้างพื้นฐานการผลิต แตกต่างจาก stub ตรงที่ fake มีตรรกะทางธุรกิจจริง — การเรียงลำดับ การกรอง การรวม — เพียงแต่ไม่มีผลกระทบภายนอก ฐานข้อมูลในหน่วยความจำแทน Room หรือ HashMap แทน SharedPreferences เป็นตัวอย่างคลาสสิก รายละเอียดเพิ่มเติมใน การจำแนกประเภท test doubles ของ Martin Fowler

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

  • Fake — การใช้งานที่เรียบง่ายและทำงานได้ด้วยตรรกะจริง แต่ไม่มีการพึ่งพาภายนอก
  • พื้นที่จัดเก็บในหน่วยความจำ — ที่เก็บ fake เก็บข้อมูลใน HashMap แทนฐานข้อมูล
  • ความแตกต่างจาก Stub — stub ส่งคืนข้อมูลคงที่ fake มีตรรกะที่สามารถทำงานได้
  • Android — InMemoryUserRepository เป็น Fake สำหรับทดสอบ ViewModel และ UseCase
  • iOS — FakeNetworkSession พร้อม URLProtocol และข้อมูลทดสอบแทนเซิร์ฟเวอร์จริง

Fake คืออะไรและทำไมจึงจำเป็นในการทดสอบ?

Fake คือการใช้งานที่สมบูรณ์แต่น้ำหนักเบาของอินเทอร์เฟซ เหมาะสำหรับการทดสอบ คำศัพท์นี้ถูกนำเสนอโดย Gerard Meszaros (2007) ในหนังสือ “xUnit Test Patterns” แตกต่างจาก stub ที่ส่งคืนคำตอบที่ถูกกำหนดไว้ล่วงหน้า fake มีโค้ดที่สามารถทำงานได้: สามารถเรียงลำดับรายการ กรองตามเงื่อนไข นับจำนวนเรคคอร์ด ข้อแตกต่างเพียงอย่างเดียวจากการใช้งานในการผลิตคือ fake ทำงานกับข้อมูลในหน่วยความจำและไม่ดำเนินการ I/O จริง

ข้อได้เปรียบหลัก คือความเร็ว การทดสอบด้วย fake ทำงานในหน่วยมิลลิวินาทีเนื่องจากไม่มีการเข้าถึงดิสก์ เครือข่าย หรือฐานข้อมูล HashMap ในหน่วยความจำทำงานเร็วกว่า Room หรือ CoreData 100–1000 เท่า ในขณะเดียวกัน fake ก็ทดสอบตรรกะทางธุรกิจจริง: การเรียงลำดับ การกรอง การรวม — ทุกสิ่งที่ stub ไม่สามารถทดสอบได้เพราะ stub ส่งคืนเฉพาะสิ่งที่บอกเท่านั้น Fake ให้ความมั่นใจว่าโค้ดประมวลผลข้อมูลอย่างถูกต้อง แทนที่จะรับคำตอบที่กำหนดไว้ล่วงหน้า

เมื่อใด Fake ดีกว่า Stub

Fake ดีกว่า Stub — หากส่วนประกอบที่กำลังทดสอบดำเนินการหลายอย่างกับข้อมูล (เรียกค้น กรอง เรียงลำดับ บันทึก) stub จะต้องกำหนดค่าการเรียกแต่ละครั้งแยกกัน Fake มีตรรกะอยู่ภายใน — การทดสอบเพียงเรียกเมธอดและตรวจสอบผลลัพธ์ ที่ IT Sectr เราใช้ fakes สำหรับที่เก็บทั้งหมดในการทดสอบหน่วย: ที่เก็บ fake ด้วย HashMap ครอบคลุม 90% ของสถานการณ์โดยไม่ต้องกำหนดค่า Mockito หรือ MockK

Fake vs Stub vs Mock: เมื่อใดควรเลือกอะไร

เกณฑ์การเลือก — กำหนดว่าการทดสอบตรวจสอบอะไร: สถานะหรือการโต้ตอบ หากการทดสอบตรวจสอบสถานะ (ผลลัพธ์ของการทำงาน) และใช้ตรรกะ — ใช้ fake หากการทดสอบต้องการเพียงข้อมูลนำเข้าโดยไม่มีตรรกะ — stub ก็เพียงพอ หากการทดสอบตรวจสอบว่ามีการเรียกเมธอด — ใช้ mock การผสมประเภท test doubles ในการทดสอบเดียวทำให้เข้าใจยากและเพิ่มความเปราะบาง

เกณฑ์FakeStubMock
มีตรรกะใช่ (เรียบง่าย)ไม่ไม่
ความเร็วสูงสูงสุดสูง
การตรวจสอบพฤติกรรมทางอ้อมไม่ใช่ (verify)
การบำรุงรักษาหนึ่งคลาสต่ออินเทอร์เฟซกำหนดค่าต่อการทดสอบกำหนดค่าต่อการทดสอบ
ความสมจริงสูง (โค้ดทำงาน)ต่ำ (ข้อมูลคงที่)ปานกลาง
ความเสี่ยงผลบวกลวงต่ำปานกลางสูง (การทดสอบเปราะบาง)

รูปแบบต้าน: Fake ที่ไม่ใช่ fake — ข้อผิดพลาดทั่วไปเมื่อนักพัฒนาเรียกวัตถุว่า fake ซึ่งจริงๆ แล้วเป็น stub หรือ mock ถ้า InMemoryUserRepository ของคุณไม่มีตรรกะ (การกรอง การเรียงลำดับ) — มันไม่ใช่ fake แต่เป็น stub ที่มีพื้นที่จัดเก็บในหน่วยความจำ Fake แตกต่างจาก stub ตรงที่มีตรรกะที่สามารถทำงานได้ ถ้าที่เก็บ fake เพียงส่งคืนสิ่งที่ถูกใส่เข้าไปและไม่ประมวลผลข้อมูล — ใช้ mock หรือ stub

กฎปฏิบัติในการเลือก Test Double

คำแนะนำเชิงปฏิบัติ — เริ่มต้นด้วย fake สำหรับทุกที่เก็บหรือบริการ ถ้า fake ซับซ้อนเกิน 50 บรรทัด — แบ่งเป็นหลายคลาส ถ้าไม่ต้องการ fake เลย (การทดสอบตรวจสอบเพียงสถานการณ์เดียวด้วยข้อมูลคงที่) — ใช้ stub ถ้าการทดสอบตรวจสอบว่าเมธอดถูกเรียกด้วยพารามิเตอร์เฉพาะ — ใช้ mock อย่าเพิ่มประสิทธิภาพการเลือกไว้ล่วงหน้า: เขียน fake และถ้ามันมากเกินไป ให้แทนที่ด้วย stub ในการทดสอบเฉพาะ

การสร้างวัตถุ Fake บน Android สำหรับ Room และ Retrofit

ที่เก็บ fake สำหรับ Room — ตัวอย่างทั่วไปของ fake บน Android การใช้งานการผลิตของ UserRepository ใช้ Room DAO กับคำสั่ง SQLite เวอร์ชัน fake เก็บข้อมูลใน MutableList หรือ HashMap และใช้เมธอดเดียวกัน: getUser(id), saveUser(user), deleteUser(id) fake มีตรรกะการค้นหา การกรอง และการเรียงลำดับ — เหมือนกับที่เก็บในการผลิต แต่ไม่มี SQL ซึ่งช่วยให้ทดสอบ ViewModel และ UseCase โดยไม่ต้องตั้งค่าฐานข้อมูล Room

kotlin
class FakeUserRepository : UserRepository {

    private val users = mutableListOf<User>()

    override suspend fun getUser(id: String): User? {
        return users.find { it.id == id }
    }

    override suspend fun saveUser(user: User) {
        val index = users.indexOfFirst { it.id == user.id }
        if (index >= 0) users[index] = user
        else users.add(user)
    }

    override suspend fun search(query: String): List<User> {
        return users.filter {
            it.name.contains(query, ignoreCase = true)
        }
    }
}

Fake สำหรับ Retrofit API — แทน MockWebServer (ซึ่งเป็น stub ไม่ใช่ fake) คุณสามารถสร้างการใช้งาน ApiService ที่ส่งคืนข้อมูลจากคอลเล็กชันในหน่วยความจำ ข้อแตกต่าง: MockWebServer สกัดกั้น HTTP และส่งคืน JSON ในขณะที่ fake ApiService ทำงานที่ระดับอินเทอร์เฟซ Kotlin โดยไม่ต้องซีเรียลไลซ์ Fake เร็วกว่า (ไม่มีการแยกวิเคราะห์ JSON) และแก้ไขจุดบกพร่องง่ายกว่า (ทำงานในโพรเซสเดียวกัน มีชนิด) เหมาะสำหรับการทดสอบที่ความหมาย HTTP (รหัสสถานะ ส่วนหัว) ไม่สำคัญ

FakeSharedPreferences สำหรับการทดสอบที่รวดเร็ว

— อีกสถานการณ์ทั่วไป SharedPreferences ในการผลิตเขียนลงดิสก์ผ่าน commit/apply เวอร์ชัน fake เก็บคู่คีย์-ค่าใน HashMap และส่งคืนข้อมูลทันที รองรับเมธอดเดียวกัน: getString, putString, getInt, putInt, clear สำหรับ Jetpack DataStore สิ่งที่เทียบเท่าคือ FakeDataStore พร้อมพื้นที่จัดเก็บในหน่วยความจำ fakes เหล่านี้เร่งการทดสอบหลายสิบเท่าเพราะไม่มีการดำเนินการเขียนดิสก์

การใช้งาน Fake บน iOS ด้วยพื้นที่จัดเก็บในหน่วยความจำ

Fake ใน Swift — สร้างผ่านโพรโทคอล คลาสการผลิตใช้โพรโทคอลด้วยตรรกะจริง (CoreData, URLSession) โครงสร้าง fake ใช้โพรโทคอลเดียวกันด้วยพื้นที่จัดเก็บในหน่วยความจำและตรรกะที่เรียบง่าย Swift เป็นภาษาที่มีความหมายเชิงค่า ดังนั้นโครงสร้าง fake จึงไม่เปลี่ยนแปลงและปลอดภัยในการทดสอบแบบหลายเธรด ซึ่งให้ข้อได้เปรียบเหนือสิ่งที่เทียบเท่า Android: ไม่จำเป็นต้องซิงโครไนซ์การเข้าถึงข้อมูลในหน่วยความจำ

swift
protocol UserRepositoryProtocol {
    func getUser(id: String) async -> User?
    func saveUser(user: User) async
}

final class FakeUserRepository: UserRepositoryProtocol {
    private var storage: [String: User] = [:]

    func getUser(id: String) async -> User? {
        return storage[id]
    }

    func saveUser(user: User) async {
        storage[user.id] = user
    }
}

final class UserViewModelTests: XCTestCase {
    func test_save_and_load() async {
        let fake = FakeUserRepository()
        let vm = UserViewModel(repository: fake)
        let user = User(id: "1", name: "Alice")

        await vm.saveUser(user)
        let loaded = await vm.getUser(id: "1")

        XCTAssertEqual(loaded?.name, "Alice")
    }
}

Fake สำหรับ CoreData — ในโปรเจกต์ iOS คุณสามารถสร้าง NSPersistentContainer ในหน่วยความจำโดยตั้งค่า description.type = NSInMemoryStoreType นี่คือสแต็ก CoreData ที่สมบูรณ์ แต่ทำงานในหน่วยความจำ fake ดังกล่าวช่วยให้ทดสอบ NSFetchRequest ภาคแสดง และการเรียงลำดับโดยไม่ต้องสร้างไฟล์ SQLite ความเร็ว: การทดสอบบน CoreData ในหน่วยความจำทำงานเร็วกว่าบนดิสก์ 5–10 เท่า ข้อเสีย: ต้องตั้งค่า NSManagedObjectModel ทุกครั้ง

FakeURLProtocol — คลาสย่อยของ URLProtocol สำหรับสกัดกั้นคำขอเครือข่ายบน iOS ลงทะเบียนผ่าน URLProtocol.registerClass(fakeProtocol) ภายในประกอบด้วยพจนานุกรม URL -> Data ในหน่วยความจำและส่งคืนข้อมูลโดยไม่มีคำขอจริง ข้อแตกต่างจาก stub: FakeURLProtocol สามารถตรวจสอบเนื้อหาคำขอ ส่วนหัว และส่งคืนการตอบสนองที่แตกต่างกันตามข้อมูลนำเข้า นี่คือ fake เพราะมีตรรกะการกำหนดเส้นทางคำขอ

รูปแบบการใช้ Fake ในโปรเจกต์มือถือ

Fake เป็น Test Fixture — วางคลาส fake ในโมดูลการทดสอบที่ใช้ร่วมกัน (androidTest/sharedTest หรือ TestSupport) การทดสอบทั้งหมดในโปรเจกต์ใช้ InMemoryUserRepository เดียวกัน ซึ่งช่วยลดความซ้ำซ้อนของการตั้งค่าวัตถุ mock ในการทดสอบแต่ละครั้งและรับประกันพฤติกรรมที่สม่ำเสมอ การเปลี่ยนตรรกะของ fake จะอัปเดตการทดสอบทั้งหมดพร้อมกัน ที่ IT Sectr เราเก็บคลาส fake ไว้ใน sharedTest/java/com/itSectr/fake/ และรวมผ่าน implementation project(:sharedTest)

Fake พร้อมข้อมูลที่กำหนดไว้ล่วงหน้า — การทดสอบมักต้องการที่เก็บที่มีเรคคอร์ดอยู่แล้ว วิธีแก้: เมธอดโรงงาน fakeWithData(vararg items) หรือเมธอดในตัว addDefaultData() โรงงานสร้าง fake เติมด้วยข้อมูลทั่วไปและส่งคืนวัตถุที่พร้อมใช้งาน ซึ่งลดโค้ดซ้ำซ้อนในการทดสอบ: แทนที่จะตั้งค่าการเรียก mock การทดสอบเพียงเรียก FakeUserRepository.withUsers(alice, bob)

Fake พร้อมการนับการเรียก — บางครั้งจำเป็นต้องตรวจสอบไม่เพียงแค่สถานะ แต่ยังรวมถึงจำนวนครั้งที่เรียกด้วย fake สามารถมีตัวนับ: saveCallCount, getUserCallCount การทดสอบตรวจสอบตัวนับหลังการทำงาน นี่คือการประนีประนอมระหว่าง fake บริสุทธิ์ (การตรวจสอบสถานะ) และ mock (การตรวจสอบการโต้ตอบ) ตัวนับไม่ตรวจสอบอาร์กิวเมนต์หรือลำดับการเรียก — เฉพาะจำนวน สำหรับการตรวจสอบอาร์กิวเมนต์ ให้ใช้ mock

Fake พร้อม Callback — สำหรับการทดสอบสถานการณ์อะซิงโครนัส fake สามารถรับ callback ในการเรียกแต่ละครั้ง: beforeGetUser, afterSaveUser ซึ่งช่วยให้จำลองความล่าช้า ข้อผิดพลาด หรือตรวจสอบสถานะกลาง วิธีการนี้มีประโยชน์สำหรับการทดสอบสถานะการโหลดของ UI: fake หยุดชั่วคราว 100 มิลลิวินาที และการทดสอบตรวจสอบว่าหน้าจอแสดงตัวโหลด Callback ไม่มีในการผลิต — นี่คือฟังก์ชันการทดสอบเท่านั้น

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

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

Fake มีตรรกะที่ทำงานได้ — กรอง เรียงลำดับ นับ Stub ส่งคืนเฉพาะคำตอบที่กำหนดไว้ล่วงหน้าโดยไม่มีตรรกะ ถ้าวัตถุมีการแตกกิ่ง (if/else, when) — มันคือ fake ถ้ามันมีเฉพาะค่าส่งคืน — มันคือ stub Fake มีค่าใช้จ่ายในการบำรุงรักษามากกว่าแต่ให้การทดสอบที่สมจริงกว่า

เมื่อใด fake อาจเป็นอันตราย?

เมื่อตรรกะของ fake ไม่ตรงกับตรรกะการผลิต ตัวอย่างเช่น FakeUserRepository ใช้การค้นหาที่คำนึงถึงตัวพิมพ์ใหญ่เล็ก ในขณะที่เวอร์ชันการผลิตไม่คำนึงถึงตัวพิมพ์ใหญ่เล็ก การทดสอบผ่าน แต่ในความเป็นจริงมีบั๊ก วิธีแก้: ทดสอบตรรกะของ fake แยกต่างหากหรือใช้ fakes สำหรับอินเทอร์เฟซที่มีตรรกะง่ายเท่านั้น (การดำเนินการ CRUD) สำหรับตรรกะที่ซับซ้อน ให้เขียนการทดสอบการรวมกับฐานข้อมูลจริง

Fake กับฐานข้อมูลในหน่วยความจำเป็นสิ่งเดียวกันหรือไม่?

ฐานข้อมูลในหน่วยความจำ เป็น fake ประเภทหนึ่ง Room.inMemoryDatabaseBuilder() สร้าง SQLite ในหน่วยความจำที่ทำงานเหมือนฐานข้อมูลการผลิต นี่คือ fake ที่สมบูรณ์ แต่ fake สามารถอยู่ที่ระดับที่เก็บ (ไม่มี SQL) และระดับเครือข่าย (FakeApiService) ฐานข้อมูลในหน่วยความจำเป็นกรณีพิเศษของ fake ที่ตรรกะใกล้เคียงกับความเป็นจริงมากที่สุด

สามารถรวม Fake และ Mock ในการทดสอบเดียวได้หรือไม่?

ได้ แต่ด้วยความระมัดระวัง Fake สำหรับที่เก็บ (ข้อมูล), Mock สำหรับ AnalyticsTracker (การตรวจสอบเหตุการณ์) การแบ่งตามชั้น: fake สำหรับชั้นข้อมูล, mock สำหรับชั้นการวิเคราะห์/การบันทึก อย่าทำให้วัตถุเดียวเป็นทั้ง fake และ mock ในเวลาเดียวกัน — นี่ละเมิดหลักการความรับผิดชอบเดียวและทำให้การทดสอบสับสน

จะทดสอบ Fake เองได้อย่างไร?

ทดสอบ fake ด้วยการทดสอบเดียวกับการใช้งานการผลิต ถ้าคุณมี UserRepositoryTest ที่ตรวจสอบ save, get, delete — เรียกใช้สองครั้ง: ด้วย FakeUserRepository และด้วย RealUserRepository ซึ่งรับประกันว่า fake ทำซ้ำพฤติกรรมของคลาสการผลิต ถ้า fake เริ่มทำงานแตกต่าง — การทดสอบจะล้มเหลวในการใช้งานทั้งสอง

สรุป

  • Fake — การใช้งานที่เรียบง่ายและทำงานได้ของการพึ่งพาด้วยตรรกะทางธุรกิจจริงและพื้นที่จัดเก็บในหน่วยความจำ
  • ความแตกต่างจาก Stub — fake มีตรรกะ (การกรอง การเรียงลำดับ), stub ส่งคืนเฉพาะข้อมูล
  • ความเร็ว — fake ทำงานเร็วกว่าการใช้งานการผลิต 100–1000 เท่าโดยไม่มีการดำเนินการ I/O
  • Android — InMemoryUserRepository, FakeDataStore, Room ในหน่วยความจำผ่าน Room.inMemoryDatabaseBuilder
  • iOS — fake ตามโพรโทคอล, CoreData ในหน่วยความจำ, FakeURLProtocol สำหรับสกัดกั้น HTTP
  • แนวปฏิบัติที่ดีที่สุด — วาง fakes ในโมดูลการทดสอบที่ใช้ร่วมกันและใช้ในการทดสอบทั้งหมดของโปรเจกต์
  • ทดสอบ fake — เรียกใช้การทดสอบเดียวกันบน fake และการใช้งานการผลิตเพื่อตรวจสอบความสอดคล้อง

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

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

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

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