Fake (peyk) — isang gumaganang pinasimpleng implementasyon ng dependency na kumikilos tulad ng isang tunay na component, ngunit gumagamit ng in-memory storage o iba pang magaan na mekanismo sa halip na production infrastructure. Hindi tulad ng stub, ang fake ay naglalaman ng tunay na business logic — pag-uuri, pag-filter, pag-aggregate — basta walang panlabas na epekto. In-memory database sa halip na Room o HashMap sa halip na SharedPreferences — mga klasikong halimbawa. Higit pa sa klasipikasyon ng test doubles ni Martin Fowler.
Mga Pangunahing Punto
Fake — isang kumpleto ngunit magaan na implementasyon ng isang interface, angkop para sa pagsubok. Ang termino ay ipinakilala ni Gerard Meszaros (2007) sa aklat na «xUnit Test Patterns». Hindi tulad ng stub na nagbabalik ng mahigpit na paunang natukoy na mga sagot, ang fake ay naglalaman ng executable code: maaari itong mag-uri ng listahan, mag-filter ayon sa kondisyon, magbilang ng mga record. Ang tanging pagkakaiba mula sa production implementasyon — ang fake ay gumagawa gamit ang in-memory data at hindi nagsasagawa ng tunay na I/O operations.
Pangunahing bentahe — bilis. Ang mga pagsubok na may fake ay isinasagawa sa millisecond, dahil walang access sa disk, network, o database. Ang in-memory HashMap ay gumagana nang 100-1000 beses na mas mabilis kaysa Room o CoreData. Kasabay nito, sinusuri ng fake ang tunay na business logic: pag-uuri, pag-filter, pag-aggregate — lahat ng hindi maaaring suriin ng stub, dahil ang stub ay nagbabalik lamang ng sinabi dito. Ang fake ay nagbibigay ng kumpiyansa na ang code ay wastong nagproseso ng data, hindi lamang tumatanggap ng paunang natukoy na sagot.
Mas gusto ang Fake kaysa Stub — kung ang component na sinusuri ay nagsasagawa ng maraming operasyon sa data (kinuha, na-filter, na-uri, na-save), ang stub ay mangangailangan ng configuration ng bawat tawag nang hiwalay. Ang fake ay naglalaman ng lohika sa loob — ang pagsubok ay tumatawag lamang ng mga pamamaraan at sinusuri ang resulta. Sa IT Sectr ginagamit namin ang fake para sa lahat ng repository sa unit tests: ang fake-repository na may HashMap ay sumasaklaw sa 90% ng mga senaryo nang walang configuration ng Mockito o MockK.
Pamantayan sa pagpili — tukuyin kung ano ang sinusuri ng pagsubok: estado o interaksyon. Kung sinusuri ng pagsubok ang estado (resulta ng trabaho) at gumagamit ng lohika — kailangan ang fake. Kung ang pagsubok ay nangangailangan lamang ng input data nang walang lohika — sapat na ang stub. Kung sinusuri ng pagsubok ang katotohanan ng pagtawag ng pamamaraan — kailangan ang mock. Ang paghahalo ng mga uri ng test doubles sa isang pagsubok ay nagpapahirap sa pag-unawa at nagpapataas ng hina.
| Pamantayan | Fake | Stub | Mock |
|---|---|---|---|
| Pagkakaroon ng lohika | Oo (pinasimple) | Hindi | Hindi |
| Bilis | Mataas | Pinakamataas | Mataas |
| Pagsusuri ng pag-uugali | Hindi direkta | Hindi | Oo (verify) |
| Pagpapanatili | Isang klase bawat interface | Configuration bawat pagsubok | Configuration bawat pagsubok |
| Realismo | Mataas (gumagana ang code) | Mababa (nakapirming data) | Katamtaman |
| Panganib ng false positive | Mababa | Katamtaman | Mataas (marupok na pagsubok) |
Anti-pattern: Fake na hindi fake — isang karaniwang pagkakamali kapag tinawag ng developer na fake ang isang bagay na sa totoo ay stub o mock. Kung ang iyong InMemoryUserRepository ay hindi naglalaman ng lohika (pag-filter, pag-uuri) — ito ay hindi fake, kundi stub na may in-memory storage. Ang fake ay naiiba sa stub dahil sa pagkakaroon ng executable na lohika. Kung ang fake-repository ay nagbabalik lamang ng inilagay dito at hindi nagproseso ng data — gumamit ng mock o stub.
Praktikal na rekomendasyon — magsimula sa fake para sa bawat repository o serbisyo. Kung ang fake ay mas kumplikado kaysa 50 linya — hatiin sa maraming klase. Kung ang fake ay hindi talaga kailangan (ang pagsubok ay sumusuri lamang ng isang senaryo na may nakapirming data) — gumamit ng stub. Kung sinusuri ng pagsubok kung ang isang pamamaraan ay tinawag na may tiyak na mga parameter — gumamit ng mock. Huwag i-optimize ang pagpili nang maaga: sumulat ng fake, at kung ito ay naging kalabisan, palitan ito ng stub sa partikular na pagsubok.
Fake-repository para sa Room — tipikal na halimbawa ng fake sa Android. Ang production implementasyon ng UserRepository ay gumagamit ng Room DAO na may SQLite queries. Ang fake na bersyon ay nag-iimbak ng data sa MutableList o HashMap at nagpapatupad ng parehong mga pamamaraan: getUser(id), saveUser(user), deleteUser(id). Ang fake ay naglalaman ng lohika ng paghahanap, pag-filter at pag-uuri — pareho sa production repository, ngunit walang SQL. Ito ay nagbibigay-daan sa pagsubok ng ViewModel at UseCase nang walang configuration ng Room database.
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 para sa Retrofit API — sa halip na MockWebServer (na stub, hindi fake) maaaring gumawa ng implementasyon ng ApiService na nagbabalik ng data mula sa in-memory collection. Pagkakaiba: ang MockWebServer ay humarang sa HTTP at nagbabalik ng JSON, habang ang fake-ApiService ay gumagana sa antas ng Kotlin interface nang walang serialization. Ang fake ay mas mabilis (walang JSON parsing) at mas simple i-debug (gumagana sa parehong proseso, naka-type). Angkop para sa mga pagsubok kung saan hindi mahalaga ang HTTP semantics (status code, headers).
Fake sa Swift — binuo sa pamamagitan ng mga protocol. Ang production class ay nagpapatupad ng protocol na may tunay na lohika (CoreData, URLSession). Ang fake structure ay nagpapatupad ng parehong protocol na may in-memory storage at pinasimple na lohika. Ang Swift ay isang wika na may value semantics, kaya ang mga fake structure ay immutable at ligtas sa multi-threaded na pagsubok. Ito ay nagbibigay ng kalamangan kaysa Android analog: ang pag-access sa in-memory data ay hindi kailangang i-synchronize.
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 para sa CoreData — sa iOS projects maaaring gumawa ng in-memory NSPersistentContainer sa pamamagitan ng pag-set ng description.type = NSInMemoryStoreType. Ito ay isang buong CoreData stack na gumagana sa memorya. Ang ganitong fake ay nagbibigay-daan sa pagsubok ng NSFetchRequest, predicates at pag-uuri nang hindi gumagawa ng SQLite file. Bilis: ang mga pagsubok sa in-memory CoreData ay isinasagawa nang 5-10 beses na mas mabilis kaysa sa disk analog. Kakulangan: kailangan i-configure ang NSManagedObjectModel sa bawat pagkakataon.
FakeURLProtocol — isang subclass ng URLProtocol para sa pagharang ng network requests sa iOS. Nagrerehistro sa pamamagitan ng URLProtocol.registerClass(fakeProtocol). Sa loob ay naglalaman ng in-memory dictionary URL -> Data at nagbabalik ng data nang walang tunay na request. Pagkakaiba sa stub: ang FakeURLProtocol ay maaaring suriin ang body ng request, headers at magbalik ng iba't ibang response depende sa input data. Ito ay fake dahil naglalaman ito ng request routing logic.
Fake bilang Test Fixture — ilipat ang mga fake class sa isang shared test module (androidTest/sharedTest o TestSupport). Lahat ng pagsubok sa project ay gumagamit ng parehong InMemoryUserRepository. Ito ay nag-aalis ng pagdoble ng configuration ng mock objects sa bawat pagsubok at ginagarantiyahan ang pare-parehong pag-uugali. Ang pagbabago ng fake logic ay nag-a-update ng lahat ng pagsubok nang sabay-sabay. Sa IT Sectr, iniimbak namin ang mga fake class sa sharedTest/java/com/itSectr/fake/ at ikinokonekta sa pamamagitan ng implementation project(:sharedTest).
Fake na may paunang itinakdang data — madalas ang mga pagsubok ay nangangailangan ng repository na naglalaman na ng ilang record. Solusyon: factory method na fakeWithData(vararg items) o built-in na method na addDefaultData(). Ang factory ay lumikha ng fake, pinupuno ito ng tipikal na data at nagbabalik ng object na handa nang gamitin. Ito ay nagbabawas ng boilerplate sa mga pagsubok: sa halip na i-configure ang mga mock call, ang pagsubok ay tumatawag lamang ng FakeUserRepository.withUsers(alice, bob).
Fake na may pagbibilang ng tawag — minsan kailangan suriin hindi lamang ang estado, kundi pati ang bilang ng mga tawag. Ang fake ay maaaring maglaman ng mga counter: saveCallCount, getUserCallCount. Sinusuri ng pagsubok ang counter pagkatapos ng execution. Ito ay isang kompromiso sa pagitan ng purong fake (pagsusuri ng estado) at mock (pagsusuri ng interaksyon). Ang mga counter ay hindi sumusuri ng mga argumento at pagkakasunud-sunod ng tawag — bilang lamang. Para sa pagsusuri ng argumento gumamit ng mock.
Fake na may Callback — para sa pagsubok ng asynchronous na senaryo, ang fake ay maaaring tumanggap ng callback sa bawat tawag: beforeGetUser, afterSaveUser. Ito ay nagbibigay-daan sa pag-simulate ng pagkaantala, error o pagsusuri ng intermediate na estado. Ang ganitong approach ay kapaki-pakinabang para sa pagsubok ng UI loading states: ang fake ay huminto nang 100 ms, at sinusuri ng pagsubok kung ang screen ay nagpapakita ng loader. Sa production, ang callback ay wala — ito ay purong testing functionality.
Mga Madalas Itanong
Fake ay naglalaman ng gumaganang lohika — nagfa-filter, nag-uuri, nagbibilang. Ang Stub ay nagbabalik lamang ng paunang natukoy na mga sagot nang walang lohika. Kung ang object ay may branching (if/else, when) — ito ay fake. Kung ito ay naglalaman lamang ng return values — ito ay stub. Ang fake ay mas mahal sa pagpapanatili, ngunit nagbibigay ng mas makatotohanang pagsubok.
Kapag ang fake logic ay hindi tumutugma sa production logic. Halimbawa, ang FakeUserRepository ay gumagamit ng case-sensitive na paghahanap, habang ang production ay case-insensitive. Ang pagsubok ay pumasa, ngunit sa katotohanan ay may bug. Solusyon: subukan ang fake logic nang hiwalay o gumamit lamang ng fakes para sa mga interface na may simpleng lohika (CRUD operations). Para sa kumplikadong lohika, sumulat ng integration test na may tunay na database.
In-memory database — isa sa mga variant ng fake. Ang Room.inMemoryDatabaseBuilder() ay lumikha ng in-memory SQLite na kumikilos tulad ng production database. Ito ay isang ganap na fake. Ngunit ang fake ay maaari ding nasa repository level (walang SQL) at network level (FakeApiService). Ang in-memory database ay isang partikular na kaso ng fake kung saan ang lohika ay pinakamalapit sa tunay.
Oo, ngunit nang may pag-iingat. Fake para sa repository (data), Mock para sa AnalyticsTracker (beripikasyon ng kaganapan). Paghihiwalay ayon sa layer: fake para sa data layer, mock para sa analytics/logging layer. Huwag gumawa ng isang object na parehong fake at mock — ito ay lumalabag sa Single Responsibility Principle at nakakalito sa pagsubok.
Subukan ang fake gamit ang parehong pagsubok gaya ng production implementasyon. Kung mayroon kang UserRepositoryTest na sumusuri ng save, get, delete — patakbuhin ito nang dalawang beses: gamit ang FakeUserRepository at gamit ang RealUserRepository. Ginagarantiyahan nito na ang fake ay umuulit ng pag-uugali ng production class. Kung ang fake ay magsimulang kumilos nang iba — ang pagsubok ay mabibigo sa parehong implementasyon.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din