Az egységtesztelés a szoftverellenőrzés egy olyan módszere, amelynél az egyes modulok vagy kódfüggvények helyes működését a rendszer többi részétől elkülönítve tesztelik. A Martin Fowler, 2026 adatai szerint a modultesztek a CI/CD és a refaktorálás alapját képezik, gyors visszajelzést biztosítva a kód működőképességéről. A modultesztelés segít a hibák korai felismerésében a fejlesztés során, tízszeresére csökkentve a javítási költségeket.
Fontos tudnivalók
Az egységtesztelés a forráskód egyes egységeinek (unit) — függvények, metódusok, osztályok — ellenőrzésének folyamata a program többi részétől elkülönítve. Minden teszt egy adott modulhasználati forgatókönyvet futtat le, és ellenőrzi, hogy az eredmény megfelel-e a vártnak. Az egységtesztek ugyanazon a programozási nyelven íródnak, mint az alapkód, és automatikusan futnak a fejlesztői környezetben vagy a CI/CD pipeline-ban. Az integrációs tesztekkel ellentétben a modultesztek nem lépnek interakcióba valós adatbázisokkal, fájlrendszerrel vagy hálózati szolgáltatásokkal.
A fő cél a gyors visszacsatolás a kód helyességéről a változtatások után. Ha egy fejlesztő refaktorál egy metódust, az egységtesztek halmaza megerősíti, hogy a viselkedés nem tört el. A Google Testing Blog (2025) adatai szerint a 60% feletti modultesztekkel rendelkező projektekben 2,5-szer kevesebb production-incidens fordul elő. További előnyök: kód dokumentáció (a tesztek megmutatják, hogyan kell használni az API-t), a refaktorálás egyszerűsítése (a megvalósítás változtatható a viselkedés megőrzése mellett) és a regressziók gyors diagnosztizálása.
Nem minden automatikus teszt egységteszt. Kritériumok: egy modult (osztály vagy függvény) tesztelünk, a külső függőségeket mockokkal vagy stábokkal helyettesítjük, a teszt ezredmásodpercek alatt fut, nem igényel szerver vagy adatbázis elindítását. Az a teszt, amely valós adatbázishoz fordul — integrációs teszt. Az a teszt, amely böngészőt nyit — E2E-teszt. A teszttípusok közötti határok megértése fontos a tevékenységek helyes elosztásához a tesztpiramisban.
A minőségi egységtesztek a Robert C. Martin által megfogalmazott FIRST elveket követik. Minden tesztnek Fast (gyors — ezredmásodpercek), Isolated (elkülönített — nem függ más tesztektől), Repeatable (ismételhető — azonos eredmény bármely gépen), Self-validating (önellenőrző — az eredmény „passed" vagy „failed", kézi ellenőrzés nélkül) és Timely (időszerű — a kód előtt vagy azzal egy időben íródott) kell lennie. Bármely elv megsértése csökkenti a teszt értékét.
Az egységtesztek írásának szabványos mintája. Arrange — az adatok és függőségek előkészítése: objektumok létrehozása, mockok beállítása, bemeneti paraméterek megadása. Act — a tesztelendő művelet végrehajtása: metódus vagy függvény meghívása. Assert — az eredmény ellenőrzése: a tényleges érték összehasonlítása a várt értékkel. A három blokkra bontás olvashatóvá és érthetővé teszi a tesztet. Ha az Assert blokk összetett logikát igényel, a teszt valószínűleg túl sokat ellenőriz egyszerre.
// Példa egységtesztre AAA minta szerint Kotlinban JUnit 5-tel
class CalculatorTest {
private lateinit var calculator: Calculator
@BeforeEach
fun setUp() {
// ARRANGE — létrehozzuk a tesztelendő objektumot
calculator = Calculator()
}
@Test
fun addition_shouldReturnCorrectSum() {
// ACT — végrehajtjuk a műveletet
val result = calculator.add(2, 3)
// ASSERT — ellenőrizzük az eredményt
Assertions.assertEquals(5, result)
}
}
A teszt nevének le kell írnia, hogy mit ellenőriz és milyen eredmény várható. Formátum: [methodName]_[scenario]_[expectedResult]. Példa: calculateTotal_whenDiscountApplied_shouldReturnDiscountedTotal. Egy jó tesztnév helyettesíti a megjegyzést, és hiba esetén azonnal jelzi, hogy melyik funkcionalitás sérült. Kerülje az olyan neveket, mint test1, checkSomething vagy verify — ezek nem hordoznak információt és megnehezítik a diagnosztizálást.
A tesztelendő modul külső függőségektől való elkülönítéséhez tesztduplánsokat (test doubles) használunk. Fő típusok: mockok (mocks) — ellenőrzik, hogy egy adott metódust a megfelelő paraméterekkel hívtak-e meg; stábok (stubs) — előre meghatározott értékeket adnak vissza a metódus meghívásakor; fékek (fakes) — egy valós komponens egyszerűsített megvalósítása (például InMemoryUserRepository a UserRepository helyett, amely adatbázissal dolgozik). A választás attól függ, hogy mit kell ellenőrizni: állapotot (stáb) vagy interakciót (mock).
| Dupláns | Mit ellenőriz | Példa |
|---|---|---|
| Mock | Metódushívás helyes paraméterekkel | userRepository.save(user) pontosan 1-szer lett meghívva |
| Stáb | Visszaadott érték | repository.findById(1) visszaadja a User(id=1, name=„Teszt") értéket |
| Fék | Logika egyszerűsített megvalósításon keresztül | InMemoryMapUserRepository HashMap-mel adatbázis helyett |
| Spy | Valós objektum részleges mockolása | spy(repo).when(findById).thenReturn(user) |
A Mockito a legnépszerűbb mockoló keretrendszer Java és Kotlin nyelven. Lehetővé teszi mockok létrehozását a mock() segítségével, visszatérési értékek beállítását a when().thenReturn()-on keresztül, és hívások ellenőrzését a verify()-val. A Mockito modern verziói (5.x) támogatják a statikus mockolást (mockStatic) és az egyszerűsített szintaxist a BDDMockito-n keresztül (given-willReturn). Fontos szabály: ne mockoljon olyasmit, ami nem az Öné — ne hozzon létre mockokat értékobjektumokhoz és szabványos könyvtárakhoz.
// Példa egységtesztre Mockito-val Kotlinban
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())
}
}
A TDD (Test-Driven Development) olyan módszertan, amelynél a tesztet a kód megvalósítása előtt írják meg. A „Piros-Zöld-Refaktor" ciklus: írjon egy tesztet, amely elbukik (Red), írja meg a minimális kódot a teszt átmenetéhez (Green), majd javítsa a kódot a viselkedés megváltoztatása nélkül (Refactor). A TDD garantálja, hogy a teljes kód lefedett tesztekkel (coverage = 100% az írott funkciókhoz), és hogy a kód tesztelhető — ha a kódot nehéz tesztelni, az azt jelenti, hogy az architektúra fejlesztésre szorul.
Az IBM (2006-2026, longitudinális tanulmány) adatai szerint a TDD-t használó csapatok 40-80%-kal kevesebb hibát követnek el a production környezetben a teszteket utólag író csapatokhoz képest. A TDD javítja az architektúrát is: a fejlesztő kénytelen az API tervezésén gondolkodni a megvalósítás előtt, ami gyenge csatoláshoz (loose coupling) és magas kohézióhoz (high cohesion) vezet. További hatás: az élő kóddal történő dokumentáció — a tesztek a modul viselkedésének specifikációjaként szolgálnak, amely mindig naprakész.
A TDD nem mindig optimális. A UI-komponenseket nehéz elkülönítve tesztelni — számukra hatékonyabbak a snapshot-tesztek vagy a vizuális regressziós tesztelés (Percy, Chromatic). A prototípuskészítés és kutatás (spike solutions) nem igényel teszteket. A tesztek nélküli örökölt kódot (legacy) nehéz TDD-vel lefedni — itt először jellemző tesztekre (characterization tests) van szükség, amelyek rögzítik az aktuális viselkedést a refaktorálás előtt. Ezekben az esetekben a TDD nem teljesen elvetendő, hanem alkalmazkodni kell — teszteket írunk a változtatandó funkciókra, nem a teljes örökölt kódra.
A mobilfejlesztésnek megvannak a sajátosságai: az üzleti logika gyakran keveredik a UI-kóddal (Activity, ViewController, ViewModel), ami megnehezíti az egységtesztelést. A legjobb gyakorlat a vékony View, vastag ViewModel: minden logikát vonjon ki a UI-összetevőkből külön osztályokba (UseCase, Repository, ViewModel), amelyek könnyen tesztelhetők emulátor nélkül. Android és iOS esetében léteznek natív egységtesztelési keretrendszerek, amelyek JVM/Native környezetben működnek az eszköz elindítása nélkül.
Az Android egységtesztek a helyi JVM-en futnak emulátor nélkül, ami gyors végrehajtást biztosít — egy tipikus teszt kevesebb mint 100 ms-ot vesz igénybe. A JUnit 5 az alapvető futtatókeretrendszer. ViewModel-tesztekhez használja a kotlinx-coroutines-test-et a korutinok teszteléséhez és a Turbine-t a StateFlow teszteléséhez. A Robolectric lehetővé teszi az Android-függő komponensek (Context, Resources) tesztelését emulátor nélkül, shadow-osztályok betöltésével. Compose-tesztekhez használja a Compose UI Test-et — de ez már UI-teszt, nem egységteszt.
Az iOS egységtesztek Swift nyelven íródnak az XCTest segítségével (beépítve az Xcode-ba). A Quick + Nimble BDD-keretrendszerek az olvashatóbb tesztekhez (describe/context/it). Mockoláshoz használja a Cuckoo-t (mockgenerálás) vagy a SwiftyMocky-t. A Swift támogatja a protokollokat és a dependency injection-t, ami megkönnyíti a függőségek cseréjét. Fontos szempont: az iOS egységtesztek a macOS szimulátoron futnak, nem valós eszközön. A hardveres funkciókat (kamera, Bluetooth) igénylő tesztek integrációs tesztek.
A Flutter egységtesztek a flutter_test csomagot használják, és a Dart VM-en futnak emulátor nélkül. Mockoláshoz a mockito csomagot kódgenerátorral (build_runner). A widget-tesztek (ugyanabban a csomagban) egyes widgeteket tesztelnek, de renderelést igényelnek és lassabbak — csak UI-logika ellenőrzéséhez használja őket. A tiszta Dart-logika (modellek, repozitóriumok, blokkok) szokásos Dart-tesztekként tesztelhető a flutter_test importálása nélkül.
// Példa egységtesztre Flutterben mockito-val
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 {
// Előkészítés
final expectedUser = User(id: 1, name: 'Test');
when(mockApi.getUser(1))
.thenAnswer((_) async => expectedUser);
// Végrehajtás
final result = await repository.fetchUser(1);
// Ellenőrzés
expect(result, expectedUser);
verify(mockApi.getUser(1)).called(1);
});
}
A hatékony egységtesztelés fegyelmet igényel. A fő szabály: tesztelje a viselkedést, ne a megvalósítást. A tesztnek nem szabad tudnia, hogy a modul hogyan van belül megvalósítva (milyen privát metódusok hívódnak meg, milyen sorrendben). Ha a teszt a megvalósításhoz kötődik, minden refaktorálásnál eltörik, és elveszíti az értékét. A teszt a szerződést ellenőrzi: X bemenetre Y kimenetnek kell következnie. Kivételt képeznek a kritikus teljesítményű algoritmusok tesztjei, ahol a hívások sorrendje fontos.
A 100%-os coverage elérhetetlen és nem is szükséges cél. A Google Testing Blog (2025) adatai szerint az optimális lefedettségi szint az egységtesztekhez a kódsorok 70-80%-a. A 100%-os coverage gyakran getterek, setterek és konstruktorok tesztelésével érhető el, ami nem hoz értéket. Összpontosítson a kritikus üzleti logikára: összetett számítások, validáció, hibakezelés, határesetek. Használja a JaCoCo-t (Java), Coverage.py-t (Python), Istanbul-t (JS) a méréshez, és állítson be küszöbértéket a CI-ben — build megszakítás 60% alatti coverage esetén.
Az egységtesztek bármely CI/CD pipeline első szakasza. Minden push-nál lefutnak a repozitóriumba, az építés és telepítés előtt. Az egységtesztek átlagos futási ideje egy projektben nem haladhatja meg az 5 percet — ha hosszabb, a tesztek megszűnnek „gyorsnak" lenni, és a fejlesztők nem futtatják őket lokálisan. Ossza szét a teszteket gyors (unit) és lassú (integration) kategóriákra, és futtassa őket a pipeline különböző szakaszaiban. Használjon párhuzamos végrehajtást és fail-fast-et a gyorsítás érdekében.
Gyakran ismételt kérdések
Az egységteszt egy modult ellenőriz elkülönítve, a külső függőségeket mockokkal helyettesítve. Az integrációs teszt több valós komponens (adatbázis, API, fájlrendszer) közötti interakciót ellenőrzi. Az egységtesztek ezredmásodpercek alatt futnak, az integrációs tesztek másodpercek alatt. A tesztpiramisban az egységtesztek 70%-ot tesznek ki.
A választás a platformtól függ: JUnit 5 Java/Kotlin, XCTest iOS/Swift, pytest Python, Jest/Vitest JavaScript/TypeScript, flutter_test Flutter esetén. Mockoláshoz használja a Mockito-t (Java), Cuckoo-t (iOS), unittest.mock-ot (Python) vagy vitest.mock-ot (JS). Minden modern keretrendszer támogatja a paraméterezett teszteket, a beépített asserteket és a párhuzamos futtatást.
Fast — a teszt ezredmásodpercek alatt fut. Isolated — nem függ más tesztektől és külső rendszerektől. Repeatable — azonos eredményt ad bármely gépen. Self-validating — automatikusan ellenőrzi az eredményt. Timely — a kód előtt vagy azzal egy időben íródott. Bármely elv megsértése csökkenti a tesztelés hatékonyságát.
Igen, mindenképpen. A ViewModel üzleti logikát tartalmaz — események feldolgozását, adatok transzformációját, állapotkezelést. Androidon használja a kotlinx-coroutines-test-et a korutinokhoz és a Turbine-t a StateFlow teszteléséhez. iOS-en tesztelje a Combine Publishers vagy async/await használatát a ViewModel-ben. A ViewModel-tesztek tiszta egységtesztek, amelyek a JVM/macOS környezetben futnak emulátor nélkül.
A hálózati kérések egységtesztekben nem hajtódnak végre — helyette a HTTP-klienst mockolják. Androidon használja a MockWebServer-t (OkHttp) — ez egy helyi HTTP-szervert indít, ami előnyösebb a mockoknál, mert reprodukálja a valós hálózati interakciót. A MockWebServer elkülönítést biztosít a realizmus elvesztése nélkül. iOS-en használja az OHHTTPStubs vagy URLProtocol segítségét a válaszok elfogásához és helyettesítéséhez.
Összegzés
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is