Unit testování v mobilním vývoji: co to je, metody a frameworky

Autor: IT Sectr Publikováno: 2026-04-06 Doba čtení: 9 min

Unit testování je metoda kontroly softwaru, při níž se testuje správnost fungování jednotlivých modulů nebo funkcí kódu v izolaci od zbytku systému. Podle údajů Martin Fowler, 2026 jsou unit testy základem CI/CD a refactoringu, poskytují rychlou zpětnou vazbu o funkčnosti kódu. Modulové testování pomáhá odhalovat chyby v raných fázích vývoje a snižuje náklady na jejich opravu desetinásobně.

Hlavní body

  • Unit test — kontrola jednoho modulu (funkce, metody, třídy) v izolaci od externích závislostí
  • Principy FIRST — Fast, Isolated, Repeatable, Self-validating, Timely — základ kvalitních testů
  • Mocky a staby — náhrady externích závislostí (DB, API, souborový systém) zajišťující izolaci testu
  • TDD (Test-Driven Development) — metodika vývoje řízeného testy: red-green-refactor
  • Testovací pyramida — unit testy tvoří 70% pyramidy a poskytují rychlou zpětnou vazbu při každém commitu

Co je unit testování?

Unit testování je proces kontroly jednotlivých jednotek (unit) zdrojového kódu — funkcí, metod, tříd — v izolaci od zbytku programu. Každý test spouští konkrétní scénář použití modulu a ověřuje, zda výsledek odpovídá očekávání. Unit testy se píší ve stejném programovacím jazyce jako hlavní kód a spouštějí se automaticky ve vývojovém prostředí nebo v CI/CD pipeline. Na rozdíl od integračních testů unit testy neinteragují s reálnými databázemi, souborovým systémem nebo síťovými službami.

Proč jsou unit testy potřeba?

Hlavním cílem je rychlá zpětná vazba o správnosti kódu po změnách. Pokud vývojář refaktoruje metodu, sada unit testů potvrdí, že chování není narušeno. Podle Google Testing Blog (2025) mají projekty s pokrytím unit testy nad 60% 2,5krát méně produkčních incidentů. Další výhody: dokumentace kódu (testy ukazují, jak používat API), zjednodušení refactoringu (implementaci lze měnit při zachování chování) a rychlá diagnostika regresí.

Co se považuje za unit test?

Ne každý automatický test je unit test. Kritéria: testuje se jeden modul (třída nebo funkce), externí závislosti jsou nahrazeny mocky nebo staby, test běží v milisekundách, nevyžaduje spuštění serveru nebo databáze. Test, který přistupuje k reálné databázi, je integrační test. Test, který otevírá prohlížeč, je E2E test. Porozumění hranicím mezi typy testů je důležité pro správné rozložení úsilí v testovací pyramidě.

Principy FIRST a struktura AAA

Kvalitní unit testy se řídí principy FIRST, formulovanými Robertem C. Martinem. Každý test by měl být Fast (rychlý — milisekundy), Isolated (izolovaný — nezávislý na jiných testech), Repeatable (opakovatelný — stejný výsledek na jakémkoli stroji), Self-validating (samokontrolní — výsledek „passed“ nebo „failed“, bez ruční kontroly) a Timely (včasný — napsaný před kódem nebo současně s ním). Porušení jakéhokoli principu snižuje hodnotu testu.

Struktura AAA (Arrange-Act-Assert)

Standardní šablona pro psaní unit testů. Arrange — příprava dat a závislostí: vytvoření objektů, nastavení mocků, zadání vstupních parametrů. Act — provedení testované akce: volání metody nebo funkce. Assert — kontrola výsledku: porovnání skutečné hodnoty s očekávanou. Rozdělení do tří bloků činí test čitelným a srozumitelným. Pokud blok Assert vyžaduje složitou logiku, test pravděpodobně kontroluje příliš mnoho věcí najednou.

kotlin
// Příklad unit testu podle šablony AAA v Kotlinu s JUnit 5
class CalculatorTest {

    private lateinit var calculator: Calculator

    @BeforeEach
    fun setUp() {
        // ARRANGE — vytvoříme testovaný objekt
        calculator = Calculator()
    }

    @Test
    fun addition_shouldReturnCorrectSum() {
        // ACT — provedeme akci
        val result = calculator.add(2, 3)

        // ASSERT — zkontrolujeme výsledek
        Assertions.assertEquals(5, result)
    }
}

Pojmenovávání testů

Název testu by měl popisovat, co se kontroluje a jaký výsledek se očekává. Formát: [methodName]_[scenario]_[expectedResult]. Příklad: calculateTotal_whenDiscountApplied_shouldReturnDiscountedTotal. Dobrý název testu nahrazuje komentář a při selhání okamžitě indikuje, která funkčnost je narušena. Vyhněte se názvům jako test1, checkSomething nebo verify — nenesou informaci a ztěžují diagnostiku.

Mocky, staby a fakes: co a kdy používat

Pro izolaci testovaného modulu od externích závislostí se používají testovací duble (test doubles). Hlavní typy: mocky (mocks) — kontrolují, zda byla určitá metoda volána se správnými parametry; staby (stubs) — vracejí předdefinované hodnoty při volání metody; fakes — zjednodušená implementace reálné komponenty (např. InMemoryUserRepository místo UserRepository pracujícího s DB). Výběr závisí na tom, co je třeba zkontrolovat: stav (stub) nebo interakci (mock).

DoubleCo kontrolujePříklad
MockVolání metody se správnými parametryuserRepository.save(user) byl zavolán přesně 1krát
StubVracená hodnotarepository.findById(1) vrací User(id=1, name=„Test“)
FakeLogika přes zjednodušenou implementaciInMemoryMapUserRepository s HashMap místo DB
SpyČástečné mockování reálného objektuspy(repo).when(findById).thenReturn(user)

Mockito: příklad mockování v Java/Kotlin

Mockito je nejoblíbenější framework pro mockování v Javě a Kotlinu. Umožňuje vytvářet mocky přes mock(), nastavovat návratové hodnoty přes when().thenReturn() a kontrolovat volání přes verify(). Moderní verze Mockito (5.x) podporují statické mocky (mockStatic) a zjednodušenou syntaxi přes BDDMockito (given-willReturn). Důležité pravidlo: nemockujte to, co není vaše — nevytvářejte mocky pro value objekty a standardní knihovny.

kotlin
// Příklad unit testu s Mockito v Kotlinu
class OrderServiceTest {

    @Mock
    private lateinit var paymentGateway: PaymentGateway

    @Mock
    private lateinit var userRepository: UserRepository

    private lateinit var orderService: OrderService

    @BeforeEach
    fun init() {
        MockitoAnnotations.openMocks(this)
        orderService = OrderService(paymentGateway, userRepository)
    }

    @Test
    fun processOrder_whenPaymentFails_shouldThrowException() {
        // given
        val user = User(id = 1, balance = 100.0)
        val order = Order(amount = 200.0)
        Mockito.`when`(paymentGateway.charge(any())).thenReturn(false)
        Mockito.`when`(userRepository.findById(1)).thenReturn(user)

        // when & then
        assert Throws<PaymentException> {
            orderService.processOrder(user.id, order)
        }

        // verify
        Mockito.verify(paymentGateway).charge(any())
    }
}

TDD: vývoj řízený testy

TDD (Test-Driven Development) je metodika, při níž se test píše před implementací kódu. Cyklus „Red-Green-Refactor“: napište test, který selže (Red), napište minimální kód pro průchod testu (Green), vylepšete kód beze změny chování (Refactor). TDD zaručuje, že veškerý kód je pokryt testy (coverage = 100% pro napsanou funkcionalitu) a že kód je testovatelný — pokud je kód obtížné testovat, znamená to, že architektura vyžaduje zlepšení.

Výhody TDD

Podle longitudinální studie IBM (2006-2026) mají týmy používající TDD o 40-80% méně defektů v produkci ve srovnání s týmy, které píší testy až po kódu. TDD také zlepšuje architekturu: vývojář je nucen přemýšlet o návrhu API před implementací, což vede k volnému propojení (loose coupling) a vysoké soudržnosti (high cohesion). Dalším efektem je dokumentace živým kódem: testy slouží jako specifikace chování modulu, která je vždy aktuální.

Kdy TDD není vhodné?

TDD není vždy optimální. UI komponenty je obtížné testovat v izolaci — pro ně jsou efektivnější snapshot testy nebo vizuální regresní testování (Percy, Chromatic). Prototypování a výzkum (spike solutions) nevyžadují testy. Legacy kód bez testů je obtížné pokrýt TDD — zde jsou nejprve potřeba characterization testy (testy zaznamenávající aktuální chování před refactoringem). V těchto případech TDD není zcela zrušeno, ale přizpůsobuje se — píší se testy na měněnou funkcionalitu, ne na celý legacy kód.

Unit testování v mobilních aplikacích

Mobilní vývoj má svá specifika: business logika je často smíšena s UI kódem (Activity, ViewController, ViewModel), což komplikuje unit testování. Nejlepší praxí jsou tenké View, tlusté ViewModely: přeneste veškerou logiku z UI komponent do samostatných tříd (UseCase, Repository, ViewModel), které lze snadno testovat bez emulátoru. Pro Android a iOS existují nativní frameworky unit testování fungující na JVM/Native bez spuštění zařízení.

Unit testy na Androidu (JUnit + Mockito/Robolectric)

Android unit testy běží na lokální JVM bez emulátoru, což zajišťuje rychlost provádění — typický test trvá méně než 100ms. JUnit 5 je hlavní runner. Pro ViewModel testy použijte kotlinx-coroutines-test pro testování korutin a Turbine pro testování StateFlow. Robolectric umožňuje testovat Android-závislé komponenty (Context, Resources) bez emulátoru pomocí shadow tříd. Pro Compose testy použijte Compose UI Test — ale to už jsou UI testy, ne unit.

Unit testy na iOS (XCTest + Quick/Nimble)

iOS unit testy se píší ve Swift s XCTest (vestavěným v Xcode). Quick + Nimble — BDD frameworky pro čitelnější testy (describe/context/it). Pro mockování použijte Cuckoo (generování mocků) nebo SwiftyMocky. Swift podporuje protokoly a dependency injection, což usnadňuje výměnu závislostí. Důležitý bod: iOS unit testy běží na macOS simulátoru, ne na reálném zařízení. Testy vyžadující hardwarové funkce (kamera, Bluetooth) jsou integrační testy.

Unit testy na Flutter (flutter_test + Mockito)

Flutter unit testy používají balíček flutter_test a běží na Dart VM bez emulátoru. Pro mockování — balíček mockito s generátorem kódu (build_runner). Widget testy (ve stejném balíčku) testují jednotlivé widgety, ale vyžadují rendering a jsou pomalejší — používejte je pouze pro kontrolu UI logiky. Čistá Dart logika (modely, repozitáře, bloky) se testuje jako běžné Dart testy bez importu flutter_test.

dart
// Příklad unit testu na Flutter s mockito
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:mockito/annotations.dart';

@GenerateMocks([ApiClient])
import 'user_repository_test.mocks.dart';

void main() {
    late MockApiClient mockApi;
    late UserRepository repository;

    setUp(() {
        mockApi = MockApiClient();
        repository = UserRepository(mockApi);
    });

    test('fetchUser returns user when API succeeds', () async {
        // Příprava
        final expectedUser = User(id: 1, name: 'Test');
        when(mockApi.getUser(1))
            .thenAnswer((_) async => expectedUser);

        // Provedení
        final result = await repository.fetchUser(1);

        // Kontrola
        expect(result, expectedUser);
        verify(mockApi.getUser(1)).called(1);
    });
}

Nejlepší postupy a typické chyby

Efektivní unit testování vyžaduje disciplínu. Hlavní pravidlo: testujte chování, ne implementaci. Test by neměl vědět, jak je modul uvnitř implementován (jaké privátní metody se volají, v jakém pořadí). Pokud je test vázán na implementaci, při každém refactoringu se rozbije a ztrácí hodnotu. Test kontroluje kontrakt: při vstupu X musí být výstup Y. Výjimkou jsou testy pro algoritmy s kritickým výkonem, kde je důležité pořadí volání.

  • Jedna kontrola na test — jeden assert nebo jedna skupina souvisejících assertů na jednu logickou kontrolu
  • Vyhněte se duplicitě — používejte @BeforeEach / setUp pro společnou inicializaci, parametrizované testy pro různé vstupní údaje
  • Netestujte privátní metody — testujte přes veřejné API. Pokud privátní metoda není pokryta, znamená to, že její logika není zvenčí viditelná
  • Pokrývejte okrajové případy — prázdné kolekce, null/undefined, záporná čísla, maximální hodnoty
  • Nepoužívejte Thread.sleep v testech — zpomaluje testy a činí je nestabilními. Používejte testovací timeouty a korutiny

Jaké pokrytí považovat za dostatečné?

100% coverage je nedosažitelný a zbytečný cíl. Podle Google Testing Blog (2025) je optimální úroveň pokrytí pro unit testy 70-80% řádků kódu. 100% coverage je často dosahováno testováním getterů, setterů a konstruktorů, což nepřináší hodnotu. Soustřeďte se na kritickou business logiku: složité výpočty, validaci, zpracování chyb, okrajové případy. Používejte JaCoCo (Java), Coverage.py (Python), Istanbul (JS) pro měření a nastavte práh v CI — selhání sestavení při coverage pod 60%.

CI/CD a unit testy

Unit testy jsou první fází každého CI/CD pipeline. Spouštějí se při každém pushi do repozitáře, před sestavením a nasazením. Průměrná doba běhu unit testů v projektu by neměla přesáhnout 5 minut — pokud je delší, testy přestávají být „rychlé“ a vývojáři je přestávají spouštět lokálně. Rozdělte testy na rychlé (unit) a pomalé (integration) a spouštějte je v různých fázích pipeline. Používejte paralelní provádění a fail-fast pro zrychlení.

Často kladené otázky

Čím se liší unit test od integračního?

Unit test kontroluje jeden modul v izolaci, nahrazuje externí závislosti mocky. Integrační test kontroluje interakci mezi několika reálnými komponentami (DB, API, souborový systém). Unit testy běží v milisekundách, integrační v sekundách. V testovací pyramidě tvoří unit testy 70%.

Který framework zvolit pro unit testy?

Volba závisí na platformě: JUnit 5 pro Java/Kotlin, XCTest pro iOS/Swift, pytest pro Python, Jest/Vitest pro JavaScript/TypeScript, flutter_test pro Flutter. Pro mockování použijte Mockito (Java), Cuckoo (iOS), unittest.mock (Python) nebo vitest.mock (JS). Všechny moderní frameworky podporují parametrizované testy, vestavěné asserty a paralelní spouštění.

Co jsou principy F.I.R.S.T. v testování?

Fast — test běží v milisekundách. Isolated — nezávisí na jiných testech a externích systémech. Repeatable — dává stejný výsledek na jakémkoli stroji. Self-validating — automaticky kontroluje výsledek. Timely — napsán před kódem nebo současně s ním. Porušení byť jen jednoho principu snižuje efektivitu testování.

Je potřeba psát unit testy pro ViewModel v Android/iOS?

Ano, rozhodně. ViewModel obsahuje business logiku — zpracování událostí, transformaci dat, správu stavu. Na Androidu použijte kotlinx-coroutines-test pro korutiny a Turbine pro testování StateFlow. Na iOS testujte Combine Publishers nebo async/await ve ViewModel. ViewModel testy jsou čisté unit testy běžící na JVM/macOS bez emulátoru.

Jak testovat kód se síťovými požadavky?

Síťové požadavky se v unit testech neprovádějí — nahrazují se mocky HTTP klienta. Na Androidu použijte MockWebServer (OkHttp) — spouští lokální HTTP server, což je výhodnější než mocky, protože reprodukuje skutečnou síťovou interakci. MockWebServer poskytuje izolaci bez ztráty realističnosti. Pro iOS — OHHTTPStubs nebo URLProtocol pro zachycení a náhradu odpovědí.

Shrnutí

  • Unit testování — kontrola jednotlivých modulů v izolaci od externích závislostí s rychlou zpětnou vazbou
  • Struktura AAA — Arrange (příprava), Act (akce), Assert (kontrola) — standardní šablona testu
  • Mocky a staby — testovací duble pro izolaci: mocky kontrolují volání, staby vracejí hodnoty
  • TDD — vývoj řízený testy (Red-Green-Refactor) snižuje počet defektů o 40-80%
  • Principy FIRST — Fast, Isolated, Repeatable, Self-validating, Timely — základ kvalitního testu
  • Platformní nástroje — JUnit 5 (Android), XCTest (iOS), flutter_test (Flutter), Jest (React Native)
  • Pokrytí 70-80% — optimální úroveň pro kritickou business logiku, gettry a settery nevyžadují testy

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také