android-unit-test
Эксперт Android тестирования. Используй для JUnit, Espresso и Android test patterns.
Best use case
android-unit-test is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Эксперт Android тестирования. Используй для JUnit, Espresso и Android test patterns.
Teams using android-unit-test should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/android-unit-test/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How android-unit-test Compares
| Feature / Agent | android-unit-test | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Эксперт Android тестирования. Используй для JUnit, Espresso и Android test patterns.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Android Unit Testing Expert
Эксперт по тестированию Android приложений с использованием JUnit 5, Mockito и Kotlin.
## Настройка зависимостей
```groovy
// build.gradle (app)
dependencies {
// Unit testing
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.3'
testImplementation 'org.mockito:mockito-core:5.3.1'
testImplementation 'org.mockito.kotlin:mockito-kotlin:5.0.0'
testImplementation 'io.mockk:mockk:1.13.5'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1'
testImplementation 'app.cash.turbine:turbine:1.0.0'
// Instrumentation testing
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test:rules:1.5.0'
}
```
## AAA Pattern (Arrange, Act, Assert)
```kotlin
class UserRepositoryTest {
@Test
fun `getUser returns user when found`() {
// Arrange
val userId = "user_123"
val expectedUser = User(userId, "John Doe", "john@example.com")
val mockDataSource = mock<UserDataSource> {
on { getUser(userId) } doReturn expectedUser
}
val repository = UserRepository(mockDataSource)
// Act
val result = repository.getUser(userId)
// Assert
assertEquals(expectedUser, result)
verify(mockDataSource).getUser(userId)
}
}
```
## ViewModel Testing
```kotlin
@OptIn(ExperimentalCoroutinesApi::class)
class UserViewModelTest {
@get:Rule
val mainDispatcherRule = MainDispatcherRule()
private lateinit var viewModel: UserViewModel
private lateinit var repository: UserRepository
@Before
fun setup() {
repository = mock()
viewModel = UserViewModel(repository)
}
@Test
fun `loadUser updates state to success when repository returns user`() = runTest {
// Arrange
val user = User("1", "John", "john@example.com")
whenever(repository.getUser("1")).thenReturn(Result.success(user))
// Act
viewModel.loadUser("1")
// Assert
val state = viewModel.uiState.value
assertTrue(state is UserUiState.Success)
assertEquals(user, (state as UserUiState.Success).user)
}
@Test
fun `loadUser updates state to error when repository fails`() = runTest {
// Arrange
val exception = IOException("Network error")
whenever(repository.getUser("1")).thenReturn(Result.failure(exception))
// Act
viewModel.loadUser("1")
// Assert
val state = viewModel.uiState.value
assertTrue(state is UserUiState.Error)
assertEquals("Network error", (state as UserUiState.Error).message)
}
}
```
## MainDispatcherRule
```kotlin
@OptIn(ExperimentalCoroutinesApi::class)
class MainDispatcherRule(
private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
) : TestWatcher() {
override fun starting(description: Description) {
Dispatchers.setMain(testDispatcher)
}
override fun finished(description: Description) {
Dispatchers.resetMain()
}
}
```
## Repository Testing
```kotlin
class UserRepositoryTest {
private lateinit var repository: UserRepository
private lateinit var remoteDataSource: UserRemoteDataSource
private lateinit var localDataSource: UserLocalDataSource
@Before
fun setup() {
remoteDataSource = mock()
localDataSource = mock()
repository = UserRepository(remoteDataSource, localDataSource)
}
@Test
fun `getUsers returns cached data when available`() = runTest {
// Arrange
val cachedUsers = listOf(User("1", "John", "john@example.com"))
whenever(localDataSource.getUsers()).thenReturn(cachedUsers)
// Act
val result = repository.getUsers()
// Assert
assertEquals(cachedUsers, result)
verify(localDataSource).getUsers()
verifyNoInteractions(remoteDataSource)
}
@Test
fun `getUsers fetches from remote when cache is empty`() = runTest {
// Arrange
val remoteUsers = listOf(User("1", "John", "john@example.com"))
whenever(localDataSource.getUsers()).thenReturn(emptyList())
whenever(remoteDataSource.getUsers()).thenReturn(remoteUsers)
// Act
val result = repository.getUsers()
// Assert
assertEquals(remoteUsers, result)
verify(localDataSource).saveUsers(remoteUsers)
}
}
```
## Flow Testing с Turbine
```kotlin
@OptIn(ExperimentalCoroutinesApi::class)
class FlowTestExample {
@Test
fun `userFlow emits expected values`() = runTest {
val repository = UserRepository()
repository.userFlow.test {
// Initial state
assertEquals(UserState.Loading, awaitItem())
// After loading
assertEquals(UserState.Success(user), awaitItem())
// Cancel and ensure no more emissions
cancelAndIgnoreRemainingEvents()
}
}
@Test
fun `searchFlow debounces and emits results`() = runTest {
val viewModel = SearchViewModel()
viewModel.searchResults.test {
viewModel.onSearchQueryChanged("test")
advanceTimeBy(300) // Debounce time
val result = awaitItem()
assertTrue(result.isNotEmpty())
cancelAndIgnoreRemainingEvents()
}
}
}
```
## Параметризованные тесты
```kotlin
class CalculatorTest {
@ParameterizedTest
@CsvSource(
"1, 1, 2",
"2, 3, 5",
"10, -5, 5",
"0, 0, 0"
)
fun `add returns correct sum`(a: Int, b: Int, expected: Int) {
val calculator = Calculator()
assertEquals(expected, calculator.add(a, b))
}
@ParameterizedTest
@MethodSource("divisionTestData")
fun `divide handles edge cases`(a: Int, b: Int, expected: Result<Int>) {
val calculator = Calculator()
assertEquals(expected, calculator.divide(a, b))
}
companion object {
@JvmStatic
fun divisionTestData() = listOf(
Arguments.of(10, 2, Result.success(5)),
Arguments.of(9, 3, Result.success(3)),
Arguments.of(5, 0, Result.failure<Int>(ArithmeticException()))
)
}
}
```
## MockK для Kotlin
```kotlin
class UserServiceTest {
@MockK
private lateinit var api: UserApi
@MockK
private lateinit var cache: UserCache
private lateinit var service: UserService
@Before
fun setup() {
MockKAnnotations.init(this)
service = UserService(api, cache)
}
@Test
fun `getUser uses coEvery for suspend functions`() = runTest {
// Arrange
val user = User("1", "John", "john@example.com")
coEvery { api.getUser("1") } returns user
coEvery { cache.save(any()) } just Runs
// Act
val result = service.getUser("1")
// Assert
assertEquals(user, result)
coVerify { cache.save(user) }
}
@Test
fun `verify call order`() = runTest {
val user = User("1", "John", "john@example.com")
coEvery { api.getUser("1") } returns user
coEvery { cache.save(any()) } just Runs
service.getUser("1")
coVerifyOrder {
api.getUser("1")
cache.save(user)
}
}
}
```
## Test Data Factories
```kotlin
object UserFactory {
fun create(
id: String = "user_${UUID.randomUUID()}",
name: String = "Test User",
email: String = "test@example.com",
isActive: Boolean = true
) = User(id, name, email, isActive)
fun createList(count: Int = 5) = (1..count).map {
create(id = "user_$it", name = "User $it")
}
}
// Usage in tests
@Test
fun `test with factory`() {
val user = UserFactory.create(name = "Custom Name")
val users = UserFactory.createList(10)
}
```
## Custom Assertions
```kotlin
fun <T> Result<T>.shouldBeSuccess(): T {
assertTrue(this.isSuccess, "Expected success but was failure: ${this.exceptionOrNull()}")
return this.getOrThrow()
}
fun <T> Result<T>.shouldBeFailure(): Throwable {
assertTrue(this.isFailure, "Expected failure but was success: ${this.getOrNull()}")
return this.exceptionOrNull()!!
}
// Usage
@Test
fun `repository returns success`() {
val result = repository.getUser("1")
val user = result.shouldBeSuccess()
assertEquals("John", user.name)
}
```
## Espresso UI Testing
```kotlin
@RunWith(AndroidJUnit4::class)
class LoginActivityTest {
@get:Rule
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
@Test
fun loginButton_isDisabled_whenEmailIsEmpty() {
onView(withId(R.id.emailInput)).perform(clearText())
onView(withId(R.id.passwordInput)).perform(typeText("password123"))
onView(withId(R.id.loginButton)).check(matches(not(isEnabled())))
}
@Test
fun successfulLogin_navigatesToHome() {
onView(withId(R.id.emailInput)).perform(typeText("test@example.com"))
onView(withId(R.id.passwordInput)).perform(typeText("password123"))
onView(withId(R.id.loginButton)).perform(click())
onView(withId(R.id.homeScreen)).check(matches(isDisplayed()))
}
}
```
## Лучшие практики
1. **Один тест = одно поведение** — каждый тест проверяет одну вещь
2. **Описательные имена** — используйте backticks для читаемых названий
3. **AAA паттерн** — Arrange, Act, Assert в каждом тесте
4. **Изолированность** — тесты не зависят друг от друга
5. **Быстрота** — unit тесты должны выполняться мгновенно
6. **Тестируйте edge cases** — null, пустые списки, ошибкиRelated Skills
Burp Suite Web Application Testing
This skill should be used when the user asks to "intercept HTTP traffic", "modify web requests", "use Burp Suite for testing", "perform web vulnerability scanning", "test with Burp Repeater", "analyze HTTP history", or "configure proxy for web testing". It provides comprehensive guidance for using Burp Suite's core features for web application security testing.
burp-suite-testing
This skill should be used when the user asks to "intercept HTTP traffic", "modify web requests", "use Burp Suite for testing", "perform web vulnerability scanning", "test with Burp ...
backtesting-frameworks
Build robust backtesting systems for trading strategies with proper handling of look-ahead bias, survivorship bias, and transaction costs. Use when developing trading algorithms, validating strateg...
axiom-ios-testing
Use when writing ANY test, debugging flaky tests, making tests faster, or asking about Swift Testing vs XCTest. Covers unit tests, UI tests, fast tests without simulator, async testing, test architecture.
asyncredux-testing-view-models
Test StoreConnector view-models in isolation. Covers creating view-models with `Vm.createFrom()`, testing view-model properties, testing callbacks that dispatch actions, and verifying state changes from callbacks.
asyncredux-testing-basics
Write unit tests for AsyncRedux actions using the Store directly. Covers creating test stores with initial state, using `dispatchAndWait()`, checking state after actions, verifying action errors via ActionStatus, and testing async actions.
astro-testing
Testing and QA gate for Astro lead gen sites. Manual + E2E + A11y + Performance. FAIL = no deploy.
aspire-integration-testing
Write integration tests using .NET Aspire's testing facilities with xUnit. Covers test fixtures, distributed application setup, endpoint discovery, and patterns for testing ASP.NET Core apps with real dependencies.
ark-dashboard-testing
Test Ark Dashboard with Playwright and create PRs with screenshots. Use when testing dashboard UI, taking screenshots for PRs, or reviewing dashboard changes.
Ark Dashboard Test
Test the Ark Dashboard UI with Playwright
arguments-test
Test skill for argument substitution
app-comprehensive-test-generator
Generate exhaustive user-flow and edge-case test scenarios from an app's codebase, produce scenario .md files, execute tests using connected or newly created MCPs, and produce an app.qa.report.md summarizing failures and suggested fixes.