Stub (stubb, ersättare) — ett testobjekt som returnerar fördefinierade svar på metodanrop istället för verklig implementering. I mobil utveckling isolerar stubbar den testade modulen från nätverksförfrågningar, databas och filsystem, vilket gör det möjligt att kontrollera logik utan miljökonfiguration. Till skillnad från mock verifierar stubb inte beteende — den tillhandahåller bara data. Mer i Martin Fowlers artikel om test doubles.
Huvudpunkter
Stub — är ett ersättningsobjekt som ersätter det verkliga beroendet i testet och returnerar förutbestämda värden på specifika anrop. Termen introducerades i klassificeringen av Gerard Meszaros (2007) i boken ”xUnit Test Patterns”. Stubb tillhör kategorin test doubles — objekt som ersätter verkliga komponenter under testning. Huvudsyftet med stubb är att förse det testade blocket med förutsägbara data och eliminera osäkerheten i externa system.
Funktionsprincip — testet konfigurerar stubben före körning: ”när metoden getUsers() anropas, returnera denna användarlista”. Stubb innehåller ingen affärslogik, kontrollerar inte anropsordningen och registrerar inte åtkomsthistorik. Den står helt enkelt på den verkliga komponentens plats och returnerar vad den har blivit tillsagd. I Android-testning innebär detta att OkHttp-klienten inte skickar en verklig begäran till servern, utan får svar från MockWebServer konfigurerad som stubb.
När ska man använda — stubbar är optimala för testning av UI-lager (ViewModel, Presenter) och affärslogik (UseCase, Interactor), där reaktion på specifik data behöver kontrolleras: tom lista, servern returnerade fel 500, token har löpt ut. Varje fall där testet kräver ett specifikt ingångstillstånd är en uppgift för stubb. För varje testscenario skapas en egen stubbkonfiguration, vilket gör tester läsbara och förutsägbara.
Gerard Meszaros (2007) identifierade i boken ”xUnit Test Patterns” fem typer av test doubles: dummy, stub, spy, mock, fake. Varje typ löser sin egen uppgift. Dummy — skickas men används inte. Stub — returnerar data. Spy — registrerar anrop. Mock — verifierar beteende. Fake — innehåller förenklad logik. Förståelse av denna klassificering hjälper utvecklaren att välja rätt verktyg för varje testscenario.
Nätverksförfrågningar — det vanligaste scenariot för stubbar. Applikationen gör HTTP-anrop till API:et och i testet måste reaktion på olika svar kontrolleras: lyckad JSON, fel 401 (obehörig), timeout, tom array. MockWebServer (OkHttp) på Android och URLProtocol (iOS) fungerar som stubbar och returnerar fördefinierade HTTP-svar utan verklig anslutning till servern. Detta snabbar upp tester från sekunder till millisekunder.
Databas — Room (Android) och CoreData (iOS) har in-memory-varianter, men deras konfiguration tar fortfarande tid. Stubb istället för repository returnerar förberedda Entity-listor utan att röra databasen. Detta är särskilt effektivt för testning av ViewModel, där sortering, filtrering eller datatransformation behöver kontrolleras. Testet körs på millisekunder oavsett datamängd.
Systemtjänster — LocationManager, SensorManager, SharedPreferences kräver verklig enhet eller emulator. Stubb för LocationProvider returnerar angivna koordinater, för SensorManager — fasta accelerometervärden. På iOS är analogen CLLocationManager med testimplementering av delegaten. Utan stubbar kräver sådana tester en fysisk enhet med specifika förhållanden.
Filsystem och cache — laddning av bilder, cachning av svar, arbete med konfigurationsfiler — alla dessa operationer beror på diskens tillstånd. Stubb för FileManager eller ImageCache returnerar framgång/fel utan att läsa verkliga filer. Detta eliminerar falska testfel på grund av icke-matchande sökvägar eller behörigheter på olika utvecklardatorer.
Ansvarfördelning — tre typer av test doubles löser olika uppgifter. Stub: ”ge mig data”. Mock: ”kontrollera om jag anropades”. Fake: ”jag fungerar som en riktig, bara enklare”. Skillnaden är kritisk för testläsbarhet: om ett test använder mock där stubb behövs, överbelastas det med verify-anrop som inte är relaterade till det testade scenariot.
| Egenskap | Stub | Mock | Fake |
|---|---|---|---|
| Syfte | Tillhandahålla data | Verifiera interaktion | Förenklad implementering |
| Logik | Nej | Nej | Ja (men förenklad) |
| Verifiering | Nej | Ja (verify) | Indirekt (via tillstånd) |
| Flexibilitet | Låg — fasta svar | Medel | Hög — logik anpassar sig |
| Hastighet | Maximal | Hög | Medel |
| Exempel | MockWebServer returnerar JSON | Mockito.verify(repository).save() | InMemoryRepository med HashMap |
Praktisk regel — om testet kontrollerar vilken data den testade komponenten fick — använd stubb. Om testet kontrollerar om komponenten anropade beroendemetoden med rätt argument — använd mock. Om du bara vill ersätta databasen med en hashtabell — är detta fake. Att blanda typer i ett test gör det bräckligt: vid implementeringsändring måste både stubben och verify-logiken skrivas om.
Stubb med verify — ett vanligt misstag när utvecklaren konfigurerar stubben och sedan lägger till verify(stub).method(). Stubb per definition bör inte verifieras — för verifiering finns mock. Om du behöver kontrollera att en metod anropades med specifika argument, använd Mockito.mock() istället för Mockito.stub(). Denna uppdelning håller testets avsikt tydlig för andra utvecklare.
MockWebServer — OkHttp-bibliotek för att skapa HTTP-stubbar på Android och JVM. Det startar en lokal HTTP-server på angiven port som fångar upp OkHttp-klientens förfrågningar och returnerar fördefinierade svar. Konfiguration tar tre rader: skapa server, köa svar (enqueue), starta. Testet kan sekventiellt köa flera svar för scenarier med paginering eller återförsök.
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 — alternativ till Mockito för Kotlin med first-class-stöd för korutiner, tilläggsfunktioner och sealed-klasser. Stubbar i MockK skapas via coEvery (för suspend-funktioner) och every (för vanliga funktioner). Till skillnad från MockWebServer ersätter MockK individuella beroendemetoder, inte hela HTTP-lagret. Detta är praktiskt för enhetstester av UseCase eller Interactor, där beroenden är abstraktioner av repositories.
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 — för integrationstester använd MockWebServer (fångar upp verklig HTTP), för enhetstester — MockK (ersätter gränssnitt). Ersätt inte det du inte testar: om testet kontrollerar Repository, ersätt inte OkHttp-klienten inuti det — använd verklig MockWebServer på HTTP-nivå. Denna regel håller tester relevanta och minskar bräcklighet vid refaktorering.
Swift-protokoll som stubbar — i iOS-native metoden implementeras stubb genom att placera en teststruktur som överensstämmer med beroendeprotokollet. Istället för verklig NetworkService får testet StubNetworkService som returnerar fast data. Swift är ett språk med statisk typning, så stubben måste överensstämma med samma protokoll som den verkliga tjänsten. Kompilatorn garanterar att stubben implementerar alla nödvändiga metoder.
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 för Objective-C — bibliotek för att skapa stubbar och mockar i äldre iOS-projekt. OCMock stöder stub-metoder med argument och returvärden. Moderna projekt i Swift föredrar protokollbaserad metod med manuella stubbar — detta ger kontroll över varje metod och kräver inga externa beroenden. OCMock förblir ett alternativ för projekt där protokollering av alla beroenden inte är ekonomiskt motiverat.
URLProtocol för HTTP-stubbar — iOS-systemmekanism för att fånga upp nätverksförfrågningar via en URLProtocol-underklass. Testet registrerar en anpassad URLProtocol som fångar upp URLSession och returnerar stubbsvar. Fördel jämfört med manuella stubbar: applikationens arkitektur behöver inte ändras — URLSession förblir verklig, men data ersätts på protokollnivå. Nackdel: svårare att felsöka än en explicit stubb-tjänst.
Vanliga frågor
Stub returnerar fördefinierad data och kontrollerar inte anropsfaktum. Mock verifierar dessutom att metoden anropades med rätt argument (verify). Stubb svarar på frågan ”vad ska returneras”, Mock — på frågan ”utfördes anropet”. Använd stubb för tillståndskontroll, mock — för interaktionsverifiering.
Fake behövs när testet kräver en fungerande (om än förenklad) implementering — till exempel en in-memory-databas istället för Room. Stubb är lämplig för enstaka scenarier med fördefinierad data. Om du upprepar samma stubb i 10 tester — behöver du förmodligen Fake. Fake minskar duplicering eftersom logiken lever i en klass.
På Android — MockK för Kotlin-objekt (object) stöder mockkObject(), inklusive statiska metoder i Java-klasser via mockkStatic(). På iOS — statiska Swift-metoder kan inte stubbas direkt; använd protokoll och DI för att ersätta static-anrop med en instansmetod i protokollet. Statiska stubbar är teknisk skuld och bör undvikas i ny kod.
Använd MockWebServer (OkHttp) — det fungerar som en lokal HTTP-server som köar (enqueue) svar. För Retrofit räcker det att ändra bas-URL till localhost:8080. För Ktor, använd MockEngine — den inbyggda mekanismen för att ersätta HttpStatement. Båda metoderna fungerar utan verkligt internet och ger full kontroll över statuskod, kropp och rubriker i svaret.
Spy omsluter det verkliga objektet och registrerar anrop, medan Stubb helt ersätter objektet med fasta svar. Spy tillåter delvis användning av den verkliga implementeringen (andra metoder fungerar som tidigare), medan stubb inte gör det. Om du behöver kontrollera att en metod anropades men en del av logiken ska utföras — använd spy, inte stubb.
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å