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 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.
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í.
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ě.
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.
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.
// 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)
}
}
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.
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).
| Double | Co kontroluje | Příklad |
|---|---|---|
| Mock | Volání metody se správnými parametry | userRepository.save(user) byl zavolán přesně 1krát |
| Stub | Vracená hodnota | repository.findById(1) vrací User(id=1, name=„Test“) |
| Fake | Logika přes zjednodušenou implementaci | InMemoryMapUserRepository s HashMap místo DB |
| Spy | Částečné mockování reálného objektu | spy(repo).when(findById).thenReturn(user) |
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.
// 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 (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í.
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í.
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.
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í.
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.
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.
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.
// 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);
});
}
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í.
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%.
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
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%.
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í.
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í.
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.
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í
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í.
Přečtěte si také