Ответ
Имею опыт написания unit-тестов (JUnit, Mockito) и UI-тестов (Espresso, UI Automator).
Unit-тесты:
@Test
fun `validateEmail should return true for valid email`() {
val validator = EmailValidator()
assertTrue(validator.validateEmail("test@example.com"))
}
UI-тесты (Espresso):
@Test
fun loginButton_click_showsWelcomeMessage() {
onView(withId(R.id.loginButton)).perform(click())
onView(withText("Welcome!")).check(matches(isDisplayed()))
}
Также работал с тестированием ViewModel, LiveData, Coroutines и интеграционными тестами. Использую правило InstantTaskExecutorRule для тестирования асинхронных операций.
Ответ 18+ 🔞
А, ну ты про тесты спрашиваешь? Да, было дело, приходилось этим заниматься. Не то чтобы прям кайфовал, но понимаю, что без этого никуда — иначе потом прилетает такое, что волосы дыбом встают, ёпта.
С юнит-тестами, в принципе, всё понятно. Берёшь свой кусок кода, который не должен смотреть ни на базу, ни на сеть, ни на погоду за окном, и начинаешь его дрочить со всех сторон. Главное — изолировать его от всего мира, чтобы он был как монах в келье. Для этого Mockito — лучший друг, товарищ и брат. Хочешь, чтобы твой репозиторий возвращал пустой список? Пожалуйста — when(repository.getData()).thenReturn(emptyList()). Хочешь, чтобы он кидал исключение, как сосед сверху в три ночи? Легко. Это ж хитрая жопа, этот мокинг — можно подсунуть любой сценарий и посмотреть, не сломается ли твоя логика. Вот, например, простейший тест на валидацию email:
@Test
fun `validateEmail should return true for valid email`() {
val validator = EmailValidator()
assertTrue(validator.validateEmail("test@example.com"))
}
Выглядит просто, да? Но если не покрыть все кейсы — пустая строка, строка без собаки, с двумя собаками — потом получишь баг на продакшене, и будет тебе овердохуища проблем. Сам от себя охуеешь, когда поймёшь, что пропустил элементарщину.
А вот с UI-тестами — это уже ёперный театр. Espresso — мощная штука, но иногда такая капризная, ядрёна вошь. Написал тест, он на эмуляторе работает, а на реальном устройстве падает, потому что вьюха не успела прорисоваться. Приходится костылить с IdlingResource или sleep(), что само по себе пиздопроебибна. Но когда всё работает — красота. Кликнул на кнопку, проверил, что появился нужный текст. Прямо как пользователь, только автоматизированный и не спящий.
@Test
fun loginButton_click_showsWelcomeMessage() {
onView(withId(R.id.loginButton)).perform(click())
onView(withText("Welcome!")).check(matches(isDisplayed()))
}
С ViewModel и LiveData тоже своя специфика. Особенно когда корутины подключаются. Тут без InstantTaskExecutorRule и TestCoroutineDispatcher — просто пиши пропало. Иначе тесты будут гоняться за асинхронными задачами, как кот за лазерной указкой, и никогда не догонят. Надо всё залочить на одном потоке, чтобы было предсказуемо. Иначе волнение ебать — запустил десять раз, и три раза упало.
В общем, тестирование — это такая работа, где доверия ебать ноль. Не проверил — считай, сломал. Подозрение ебать чувствую к любому коду, который не покрыт. Хотя, конечно, иногда так лень этим заниматься, думаешь — да похуй, и так сработает. Но это путь в темный лес, чувак. Потом ночью разбудит алерт, и будешь искать, где же ты, блядь, не дописал тот самый assertFalse.