Fake (fejk) — en fungerande förenklad implementering av ett beroende som beter sig som en verklig komponent, men använder in-memory lagring eller andra lätta mekanismer istället för produktionsinfrastruktur. Till skillnad från stub innehåller fake verklig affärslogik — sortering, filtrering, aggregering — bara utan externa effekter. In-memory databas istället för Room eller HashMap istället för SharedPreferences — klassiska exempel. Mer i Martin Fowler’s klassificering av test doubles.
Huvudpunkter
Fake — en fullständig men lätt implementering av ett gränssnitt, lämplig för testning. Termen introducerades av Gerard Meszaros (2007) i boken «xUnit Test Patterns». Till skillnad från stub, som returnerar strikt fördefinierade svar, innehåller fake körbar kod: den kan sortera en lista, filtrera efter villkor, räkna poster. Den enda skillnaden från produktionsimplementeringen — fake arbetar med in-memory data och utför inte verkliga I/O-operationer.
Främsta fördelen — hastighet. Tester med fake utförs på millisekunder, eftersom det inte finns någon åtkomst till disk, nätverk eller databas. In-memory HashMap fungerar 100-1000 gånger snabbare än Room eller CoreData. Samtidigt kontrollerar fake verklig affärslogik: sortering, filtrering, aggregering — allt som stub inte kan kontrollera, eftersom stub bara returnerar vad den blir tillsagd. Fake ger förtroende för att koden bearbetar data korrekt, inte bara tar emot ett fördefinierat svar.
Fake är att föredra framför Stub — om den testade komponenten utför flera operationer på data (hämtade, filtrerade, sorterade, sparade), kommer stub att kräva konfiguration av varje anrop separat. Fake innehåller logiken internt — testet anropar helt enkelt metoder och kontrollerar resultatet. Hos IT Sectr använder vi fake för alla repositories i enhetstester: fake-repository med HashMap täcker 90% av scenarierna utan konfiguration av Mockito eller MockK.
Urvalskriterium — bestäm vad testet kontrollerar: tillstånd eller interaktion. Om testet kontrollerar tillstånd (arbetsresultatet) och använder logik — behövs fake. Om testet bara behöver indata utan logik — räcker stub. Om testet kontrollerar att en metod har anropats — behövs mock. Att blanda test doubles-typer i ett test försvårar förståelsen och ökar skörheten.
| Kriterium | Fake | Stub | Mock |
|---|---|---|---|
| Förekomst av logik | Ja (förenklad) | Nej | Nej |
| Hastighet | Hög | Maximal | Hög |
| Beteendekontroll | Indirekt | Nej | Ja (verify) |
| Underhåll | En klass per gränssnitt | Konfiguration per test | Konfiguration per test |
| Realism | Hög (koden fungerar) | Låg (fast data) | Medel |
| Risk för falsklarm | Låg | Medel | Hög (sköra tester) |
Antimönster: Fake som inte är fake — ett vanligt misstag när utvecklaren kallar ett objekt fake som egentligen är stub eller mock. Om din InMemoryUserRepository inte innehåller logik (filtrering, sortering) — är detta inte fake, utan stub med in-memory lagring. Fake skiljer sig från stub just genom närvaron av körbar logik. Om fake-repository bara returnerar vad som lagts i det och inte bearbetar data — använd mock eller stub.
Praktisk rekommendation — börja med fake för varje repository eller tjänst. Om fake är mer komplex än 50 rader — dela upp i flera klasser. Om fake inte alls behövs (testet kontrollerar bara ett scenario med fast data) — använd stub. Om testet kontrollerar om en metod anropades med specifika parametrar — använd mock. Optimera inte valet i förväg: skriv fake, och om det visar sig vara överflödigt, ersätt det med stub i det specifika testet.
Fake-repository för Room — typiskt exempel på fake på Android. Produktionsimplementeringen av UserRepository använder Room DAO med SQLite-frågor. Fake-versionen lagrar data i MutableList eller HashMap och implementerar samma metoder: getUser(id), saveUser(user), deleteUser(id). Fake innehåller sök-, filtrerings- och sorteringslogik — samma som i produktionsrepositoryt, men utan SQL. Detta gör det möjligt att testa ViewModel och UseCase utan konfiguration av Room-databasen.
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 för Retrofit API — istället för MockWebServer (som är stub, inte fake) kan en implementering av ApiService skapas som returnerar data från en in-memory samling. Skillnad: MockWebServer fångar upp HTTP och returnerar JSON, medan fake-ApiService arbetar på Kotlin-gränssnittsnivå utan serialisering. Fake är snabbare (ingen JSON-tolkning) och enklare att felsöka (arbetar i samma process, typad). Lämplig för tester där HTTP-semantik (statuskoder, rubriker) inte är viktig.
Fake i Swift — byggs via protokoll. Produktionsklassen implementerar protokollet med verklig logik (CoreData, URLSession). Fake-strukturen implementerar samma protokoll med in-memory lagring och förenklad logik. Swift är ett språk med värdessemantik, så fake-strukturer är oföränderliga och säkra i flertrådade tester. Detta ger en fördel jämfört med Android-motsvarigheter: åtkomst till in-memory data behöver inte synkroniseras.
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 för CoreData — i iOS-projekt kan en in-memory NSPersistentContainer skapas genom att ställa in description.type = NSInMemoryStoreType. Detta är en fullständig CoreData-stack som arbetar i minnet. En sådan fake gör det möjligt att testa NSFetchRequest, predikat och sorteringar utan att skapa en SQLite-fil. Hastighet: tester på in-memory CoreData utförs 5-10 gånger snabbare än på disk-motsvarigheten. Nackdel: NSManagedObjectModel måste konfigureras varje gång.
FakeURLProtocol — en underklass av URLProtocol för att fånga upp nätverksförfrågningar på iOS. Registreras via URLProtocol.registerClass(fakeProtocol). Inuti innehåller den en in-memory ordbok URL -> Data och returnerar data utan verklig förfrågan. Skillnad från stub: FakeURLProtocol kan kontrollera förfrågans innehåll, rubriker och returnera olika svar beroende på indata. Detta är fake eftersom det innehåller logik för routning av förfrågningar.
Fake som Test Fixture — flytta fake-klasser till en gemensam testmodul (androidTest/sharedTest eller TestSupport). Alla tester i projektet använder samma InMemoryUserRepository. Detta eliminerar dubbelarbete med konfiguration av mock-objekt i varje test och garanterar enhetligt beteende. Ändring av fake-logik uppdaterar alla tester samtidigt. Hos IT Sectr lagrar vi fake-klasser i sharedTest/java/com/itSectr/fake/ och ansluter via implementation project(:sharedTest).
Fake med förinställd data — ofta behöver tester ett repository som redan innehåller vissa poster. Lösning: fabriksmetod fakeWithData(vararg items) eller inbyggd metod addDefaultData(). Fabriken skapar fake, fyller den med typisk data och returnerar ett redo-att-använda objekt. Detta minskar boilerplate i tester: istället för att konfigurera mock-anrop, anropar testet helt enkelt FakeUserRepository.withUsers(alice, bob).
Fake med anropsräkning — ibland måste man kontrollera inte bara tillståndet utan också antalet anrop. Fake kan innehålla räknare: saveCallCount, getUserCallCount. Testet kontrollerar räknaren efter exekvering. Detta är en kompromiss mellan ren fake (tillståndskontroll) och mock (interaktionskontroll). Räknare kontrollerar inte argument och anropsordning — bara antalet. För argumentkontroll, använd mock.
Fake med Callback — för testning av asynkrona scenarier kan fake acceptera en callback vid varje anrop: beforeGetUser, afterSaveUser. Detta gör det möjligt att simulera förseningar, fel eller kontrollera mellanliggande tillstånd. Detta tillvägagångssätt är användbart för testning av UI-laddningstillstånd: fake pausar i 100 ms, och testet kontrollerar om skärmen visar en laddningsindikator. I produktion finns callback inte — det är ren testfunktionalitet.
Vanliga frågor
Fake innehåller fungerande logik — filtrerar, sorterar, räknar. Stub returnerar bara fördefinierade svar utan logik. Om ett objekt har förgreningar (if/else, when) — är det fake. Om det bara innehåller return values — är det stub. Fake är dyrare att underhålla, men ger mer realistiska tester.
När fake-logiken inte överensstämmer med produktionslogiken. Till exempel använder FakeUserRepository case-sensitive sökning, medan produktionen använder case-insensitive. Testet godkänns, men i verkligheten finns en bugg. Lösning: testa fake-logiken separat eller använd fakes endast för gränssnitt med enkel logik (CRUD-operationer). För komplex logik, skriv integrationstester med en riktig databas.
In-memory database — en av varianterna av fake. Room.inMemoryDatabaseBuilder() skapar en in-memory SQLite som beter sig som en produktionsdatabas. Detta är en fullvärdig fake. Men fake kan också vara på repository-nivå (utan SQL) och på nätverksnivå (FakeApiService). In-memory databas är ett speciellt fall av fake där logiken är maximalt nära den verkliga.
Ja, men försiktigt. Fake för repository (data), Mock för AnalyticsTracker (händelseverifiering). Uppdelning per lager: fake för datalagret, mock för analys-/loggningslagret. Gör inte ett objekt samtidigt fake och mock — detta bryter mot principen om enskilt ansvar och förvirrar testet.
Testa fake med samma tester som produktionsimplementeringen. Om du har UserRepositoryTest som kontrollerar save, get, delete — kör det två gånger: med FakeUserRepository och med RealUserRepository. Detta garanterar att fake upprepar produktionsklassens beteende. Om fake börjar bete sig annorlunda — kommer testet att misslyckas på båda implementeringarna.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också