0Pricing
Android Academy · Урок

Модульное тестирование с JUnit

Пишите быстрые и надёжные модульные тесты с JUnit 4. Используйте @Test, @Before, @After и проверки, тестируйте LiveData с InstantTaskExecutorRule, а корутины — с runTest.

«Модульное тестирование с JUnit» — бесплатный урок Android Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Android Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Android Academy содержит 4 уроков всего.

Зачем писать тесты?

Тесты — не роскошь, а ваша страховочная сетка:

  • Обнаруживайте ошибки раньше пользователей
  • Уверенно проводите рефакторинг (если тесты проходят, ничего не сломалось)
  • Документируйте предполагаемое поведение
  • Предотвращайте регрессии при добавлении функций

Хорошо протестированный код обычно лучше спроектирован: если его трудно тестировать, вероятно, его компоненты слишком тесно связаны.

Виды тестов Android

Две категории:

  • Модульные тесты (папка test/) — выполняются на JVM, устройство не требуется. Быстрые. Проверяют обычную логику Kotlin/Java.
  • Инструментальные тесты (папка androidTest/) — выполняются на устройстве или эмуляторе. Медленнее. Проверяют код, специфичный для Android (базу данных Room, интерфейс).

Предпочитайте модульные тесты для бизнес-логики. Используйте инструментальные тесты только при необходимости взаимодействовать с API Android.

Настройка JUnit 4

JUnit 4 по умолчанию включён в проекты Android. Добавьте эти зависимости в app/build.gradle:

// app/build.gradle:
dependencies {
    testImplementation 'junit:junit:4.13.2'
    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
    testImplementation 'androidx.arch.core:core-testing:2.2.0'  // for LiveData
}

Ваш первый тест JUnit

Создайте класс в src/test/java/ и пометьте методы тестирования аннотацией @Test:

import org.junit.Test
import org.junit.Assert.*

class CalculatorTest {

    private val calc = Calculator()

    @Test
    fun `addition returns correct sum`() {
        val result = calc.add(2, 3)
        assertEquals(5, result)
    }

    @Test
    fun `division by zero throws exception`() {
        assertThrows(ArithmeticException::class.java) {
            calc.divide(10, 0)
        }
    }
}

@Before и @After

Используйте аннотации жизненного цикла, чтобы настраивать и очищать состояние вокруг каждого теста:

class UserRepositoryTest {

    private lateinit var repo: UserRepository
    private lateinit var fakeDao: FakeUserDao

    @Before
    fun setUp() {
        fakeDao = FakeUserDao()          // fresh instance before each test
        repo = UserRepository(fakeDao)
    }

    @After
    fun tearDown() {
        fakeDao.clear()                  // clean up after each test
    }

    @Test
    fun `getUsers returns all users`() {
        fakeDao.insert(User(1, "Alice"))
        fakeDao.insert(User(2, "Bob"))
        assertEquals(2, repo.getUsers().size)
    }
}

Распространённые проверки

JUnit предоставляет методы проверок в Assert.*:

// Equality:
assertEquals(expected, actual)
assertNotEquals(unexpected, actual)

// Null:
assertNull(value)
assertNotNull(value)

// Boolean:
assertTrue(condition)
assertFalse(condition)

// Same object reference:
assertSame(expected, actual)

// Custom message on failure:
assertEquals("User ID should be 1", 1, user.id)

// Exception:
assertThrows(IllegalArgumentException::class.java) {
    User(id = -1, name = "")
}

Тестирование ViewModel

Чтобы тестировать LiveData, добавьте InstantTaskExecutorRule, чтобы LiveData публиковала значения синхронно:

class UserViewModelTest {

    @get:Rule
    val instantTaskRule = InstantTaskExecutorRule()

    private val fakeRepo = FakeUserRepository()
    private lateinit var viewModel: UserViewModel

    @Before
    fun setUp() {
        viewModel = UserViewModel(fakeRepo)
    }

    @Test
    fun `users are loaded on init`() {
        fakeRepo.usersToReturn = listOf(User(1, "Alice"), User(2, "Bob"))
        viewModel.loadUsers()

        val result = viewModel.users.value
        assertNotNull(result)
        assertEquals(2, result?.size)
    }
}

Тестирование сопрограмм

Используйте TestCoroutineDispatcher / StandardTestDispatcher, чтобы управлять выполнением сопрограмм в тестах:

class SyncWorkerTest {

    @get:Rule
    val instantTaskRule = InstantTaskExecutorRule()

    private val testDispatcher = StandardTestDispatcher()

    @Before
    fun setUp() {
        Dispatchers.setMain(testDispatcher)
    }

    @After
    fun tearDown() {
        Dispatchers.resetMain()
    }

    @Test
    fun `sync completes successfully`() = runTest {
        val repo = FakeRepository()
        val worker = SyncUseCase(repo, testDispatcher)
        val result = worker.run()
        assertTrue(result.isSuccess)
    }
}

Фиктивная реализация и имитация

Две стратегии замены настоящих зависимостей в тестах:

  • Фиктивная реализация — рабочая реализация, созданная для тестирования (например, список в памяти вместо настоящей базы данных). Её легко написать и читать, библиотека не нужна.
  • Имитация — автоматически созданная заглушка, которая записывает вызовы и позволяет проверять взаимодействия (Mockito, MockK). Она мощнее, но при чрезмерном использовании тесты могут стать хрупкими.

Для простых случаев предпочитайте фиктивные реализации; используйте имитации, когда нужно проверить вызовы методов.

Создание фиктивной реализации

Фиктивная реализация реализует тот же интерфейс, что и настоящий класс, но хранит данные в памяти:

interface UserDao {
    fun getAll(): List<User>
    fun insert(user: User)
}

class FakeUserDao : UserDao {
    private val storage = mutableListOf<User>()

    override fun getAll(): List<User> = storage.toList()
    override fun insert(user: User) { storage.add(user) }

    fun clear() { storage.clear() }
}

Соглашение об именовании тестов

Хорошие имена тестов описывают, что проверяется, и ожидаемый результат. Для понятных описаний в Kotlin используйте имена функций в обратных кавычках:

  • `getUsers returns empty list when no users exist`
  • `login throws exception when password is blank`
  • `add returns correct sum for negative numbers`

Шаблон: `subject does X when Y`

Быстрая проверка

Каково назначение @Before в классе тестов JUnit?

Итоги: модульное тестирование с JUnit

Надёжные приложения — это протестированные приложения:

  • Модульные тесты находятся в src/test/ и выполняются на JVM — быстро
  • Помечайте их аннотацией @Test, используйте assertEquals, assertTrue, assertThrows
  • @Before / @After — для настройки и очистки
  • InstantTaskExecutorRule — для синхронного тестирования LiveData
  • runTest + StandardTestDispatcher — для тестирования сопрограмм
  • Для простых замен предпочитайте фиктивные реализации имитациям

Далее: имитация с помощью Mockito для проверки взаимодействий.

Часто задаваемые вопросы

Урок «Модульное тестирование с JUnit» бесплатный?

Да — полный текст урока «Модульное тестирование с JUnit» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Android Academy, подпишись на CoddyKit PRO. Курс Android Academy содержит 4 уроков всего.

Чему я научусь в уроке «Модульное тестирование с JUnit»?

Пишите быстрые и надёжные модульные тесты с JUnit 4. Используйте @Test, @Before, @After и проверки, тестируйте LiveData с InstantTaskExecutorRule, а корутины — с runTest. Ты практикуешь Android Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Android Academy?

Предыдущий опыт не требуется. Android Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Модульное тестирование с JUnit»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Android Academy?

Да. Каждый урок Android Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Модульное тестирование с JUnit
  2. Имитация объектов с Mockito
  3. Тестирование интерфейса с Espresso
  4. Отладка и профилирование
← Назад к Android Academy