{"id":"d52f10c9-0f6c-450b-a121-4b9045ab4596","shortId":"GxHdhH","kind":"skill","title":"cometchat-android-v6-testing","tagline":"CometChat Android UIKit v6 testing — unit testing ViewModels, Compose UI testing, Espresso, Maestro, and CI configuration","description":"> **Companion skills:** cometchat-android-v6-extensions (ViewModel/DataSource architecture), cometchat-android-v6-compose-components, cometchat-android-v6-kotlin-components\n\n## Purpose\n\nTest CometChat UIKit v6 components and ViewModels — unit tests for the shared core module, Compose UI tests, Espresso tests for Views, and end-to-end tests with Maestro.\n\n## Use this skill when\n\n- Writing unit tests for CometChat ViewModels\n- Writing Compose UI tests for CometChat components\n- Writing Espresso tests for Kotlin Views components\n- Setting up Maestro flows for end-to-end testing\n- Configuring CI for CometChat test suites\n\n## Do not use this skill when\n\n- Debugging runtime issues (use `cometchat-android-v6-troubleshooting`)\n- Working with component APIs (use `cometchat-*-components`)\n\n## 1. Test Dependencies\n\nFrom `chatuikit-compose/build.gradle.kts`:\n\n```kotlin\ndependencies {\n    // Unit testing\n    testImplementation(\"junit:junit:4.13.2\")\n    testImplementation(\"io.kotest:kotest-runner-junit5:5.x\")\n    testImplementation(\"io.kotest:kotest-assertions-core:5.x\")\n    testImplementation(\"io.kotest:kotest-property:5.x\")\n    testImplementation(\"org.jetbrains.kotlinx:kotlinx-coroutines-test:1.x\")\n    testImplementation(\"androidx.arch.core:core-testing:2.x\")\n    testImplementation(\"androidx.lifecycle:lifecycle-runtime-testing:2.x\")\n    testImplementation(\"org.mockito:mockito-core:5.x\")\n    testImplementation(\"org.mockito.kotlin:mockito-kotlin:5.x\")\n\n    // Android instrumented testing\n    androidTestImplementation(\"androidx.test.ext:junit:1.x\")\n    androidTestImplementation(\"androidx.test.espresso:espresso-core:3.x\")\n    androidTestImplementation(\"androidx.compose.ui:ui-test-junit4\")\n    androidTestImplementation(platform(\"androidx.compose:compose-bom:2024.x.x\"))\n\n    // Debug-only Compose tooling\n    debugImplementation(\"androidx.compose.ui:ui-tooling\")\n    debugImplementation(\"androidx.compose.ui:ui-test-manifest\")\n}\n```\n\nEnable JUnit Platform for Kotest:\n\n```kotlin\nandroid {\n    testOptions {\n        unitTests.all {\n            it.useJUnitPlatform()\n        }\n    }\n}\n```\n\n## 2. Unit Testing ViewModels\n\nViewModels live in `chatuikit-core` and are shared across both stacks. Test them with coroutines-test and Mockito:\n\n```kotlin\nimport kotlinx.coroutines.test.runTest\nimport org.mockito.kotlin.mock\nimport org.mockito.kotlin.whenever\n\nclass CometChatConversationsViewModelTest {\n\n    @Test\n    fun `loadConversations emits success state`() = runTest {\n        val mockDataSource = mock<ConversationListDataSource>()\n        // Setup mock responses...\n\n        val viewModel = CometChatConversationsViewModel(/* inject mocks */)\n\n        // Collect state and assert\n        viewModel.uiState.test {\n            val state = awaitItem()\n            // Assert state is success with expected data\n        }\n    }\n}\n```\n\n### 2.1 Testing Events\n\n```kotlin\nimport com.cometchat.uikit.core.events.CometChatEvents\nimport com.cometchat.uikit.core.events.CometChatMessageEvent\n\n@Test\nfun `emitting message event is received by collector`() = runTest {\n    val events = mutableListOf<CometChatMessageEvent>()\n\n    val job = launch {\n        CometChatEvents.messageEvents.collect { events.add(it) }\n    }\n\n    CometChatEvents.emitMessageEventSync(\n        CometChatMessageEvent.MessageSent(mockMessage, MessageStatus.SUCCESS)\n    )\n\n    advanceUntilIdle()\n    assertEquals(1, events.size)\n    job.cancel()\n}\n```\n\n## 3. Compose UI Testing\n\n```kotlin\nimport androidx.compose.ui.test.junit4.createComposeRule\nimport androidx.compose.ui.test.onNodeWithText\nimport androidx.compose.ui.test.performClick\n\nclass CometChatConversationsTest {\n\n    @get:Rule\n    val composeTestRule = createComposeRule()\n\n    @Test\n    fun conversationsList_displaysTitle() {\n        composeTestRule.setContent {\n            CometChatTheme {\n                CometChatConversations()\n            }\n        }\n\n        // Assert UI elements\n        composeTestRule.onNodeWithText(\"Chats\").assertExists()\n    }\n\n    @Test\n    fun conversationItem_clickNavigates() {\n        var clickedConversation: Conversation? = null\n\n        composeTestRule.setContent {\n            CometChatTheme {\n                CometChatConversations(\n                    onItemClick = { clickedConversation = it }\n                )\n            }\n        }\n\n        // Interact and assert\n        // composeTestRule.onNodeWithText(\"User Name\").performClick()\n        // assertNotNull(clickedConversation)\n    }\n}\n```\n\n## 4. Espresso Testing (Kotlin Views)\n\n```kotlin\nimport androidx.test.espresso.Espresso.onView\nimport androidx.test.espresso.assertion.ViewAssertions.matches\nimport androidx.test.espresso.matcher.ViewMatchers.*\nimport androidx.test.ext.junit.rules.ActivityScenarioRule\n\nclass ConversationsActivityTest {\n\n    @get:Rule\n    val activityRule = ActivityScenarioRule(ConversationsActivity::class.java)\n\n    @Test\n    fun conversationsList_isDisplayed() {\n        onView(withId(R.id.conversations))\n            .check(matches(isDisplayed()))\n    }\n}\n```\n\n## 5. Property-Based Testing with Kotest\n\nThe project uses Kotest for property-based tests (e.g., `MessageAdapterPropertyTest.kt`):\n\n```kotlin\nimport io.kotest.core.spec.style.FunSpec\nimport io.kotest.property.forAll\nimport io.kotest.property.Arb\nimport io.kotest.property.arbitrary.string\n\nclass BubbleFactoryKeyTest : FunSpec({\n\n    test(\"factory key format is always category_type\") {\n        forAll(Arb.string(1..20), Arb.string(1..20)) { category, type ->\n            val key = BubbleFactory.getKey(category, type)\n            key == \"${category}_${type}\"\n        }\n    }\n})\n```\n\n## 6. Maestro End-to-End Testing\n\nThe `master-app-jetpack/ai-testing/` directory contains Maestro test configurations:\n\n```yaml\n# maestro/login_flow.yaml\nappId: com.example.jetpackuikit\n---\n- launchApp\n- tapOn: \"Login\"\n- inputText: \"superhero1\"\n- tapOn: \"Submit\"\n- assertVisible: \"Chats\"\n```\n\nRun with:\n\n```bash\nmaestro test maestro/login_flow.yaml\n```\n\n## 7. CI Configuration\n\n### 7.1 GitHub Actions\n\n```yaml\nname: Test\non: [push, pull_request]\njobs:\n  unit-tests:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-java@v4\n        with:\n          java-version: '17'\n          distribution: 'temurin'\n      - run: ./gradlew testDebugUnitTest\n\n  instrumented-tests:\n    runs-on: macos-latest\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-java@v4\n        with:\n          java-version: '17'\n          distribution: 'temurin'\n      - uses: reactivecircus/android-emulator-runner@v2\n        with:\n          api-level: 34\n          script: ./gradlew connectedDebugAndroidTest\n```\n\n## 8. Test Organization\n\n| Test Type | Location | Framework | What to Test |\n|---|---|---|---|\n| Unit tests | `chatuikit-core/src/test/` | JUnit + Kotest + Mockito | ViewModels, DataSources, Repositories, Events |\n| Unit tests | `chatuikit-compose/src/test/` | JUnit + Kotest | BubbleFactory, style resolution |\n| Compose UI tests | `chatuikit-compose/src/androidTest/` | Compose Test | Component rendering, interactions |\n| Views UI tests | `chatuikit-kotlin/src/androidTest/` | Espresso | Component rendering, interactions |\n| E2E tests | `master-app-*/ai-testing/` | Maestro | Full user flows |\n\n## Hard rules\n\n- ALWAYS use `CometChatTheme {}` wrapper in Compose test `setContent` blocks — components depend on CompositionLocal values\n- Use `runTest` from `kotlinx-coroutines-test` for testing coroutines and SharedFlow\n- Use `useJUnitPlatform()` in test options for Kotest compatibility\n- Mock DataSources and Repositories when unit testing ViewModels — do NOT make real SDK calls in tests\n- CometChat SDK must NOT be initialized in unit tests — mock all SDK interactions\n- For Compose UI tests, use `createComposeRule()` not `createAndroidComposeRule()` unless you need Activity context","tags":["cometchat","android","testing","skills","agent-skills","ai-agent","chat","claude-code","cursor","messaging","nextjs","react"],"capabilities":["skill","source-cometchat","skill-cometchat-android-v6-testing","topic-agent-skills","topic-ai-agent","topic-chat","topic-claude-code","topic-cometchat","topic-cursor","topic-messaging","topic-nextjs","topic-react","topic-react-native","topic-ui-kit"],"categories":["cometchat-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cometchat/cometchat-skills/cometchat-android-v6-testing","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cometchat/cometchat-skills","source_repo":"https://github.com/cometchat/cometchat-skills","install_from":"skills.sh"}},"qualityScore":"0.463","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 27 github stars · SKILL.md body (7,856 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:04:47.466Z","embedding":null,"createdAt":"2026-05-07T13:05:06.754Z","updatedAt":"2026-05-18T19:04:47.466Z","lastSeenAt":"2026-05-18T19:04:47.466Z","tsv":"'/ai-testing':522,683 '/build.gradle.kts':142 '/gradlew':585,619 '/src/androidtest':661,673 '/src/test':636,649 '1':135,180,217,365,495,498 '17':581,607 '2':187,195,266 '2.1':332 '20':496,499 '2024':238 '3':224,368 '34':617 '4':422 '4.13.2':150 '5':157,165,172,202,209,455 '6':510 '7':547 '7.1':550 '8':621 'across':279 'action':552 'actions/checkout':572,598 'actions/setup-java':575,601 'activ':764 'activityrul':441 'activityscenariorul':442 'advanceuntilidl':363 'alway':490,690 'android':3,7,26,33,39,125,211,262 'androidtestimplement':214,219,226,232 'androidx.arch.core':183 'androidx.compose':234 'androidx.compose.ui':227,246,251 'androidx.compose.ui.test.junit4.createcomposerule':374 'androidx.compose.ui.test.onnodewithtext':376 'androidx.compose.ui.test.performclick':378 'androidx.lifecycle':190 'androidx.test.espresso':220 'androidx.test.espresso.assertion.viewassertions.matches':431 'androidx.test.espresso.espresso.onview':429 'androidx.test.espresso.matcher.viewmatchers':433 'androidx.test.ext':215 'androidx.test.ext.junit.rules.activityscenariorule':435 'api':131,615 'api-level':614 'app':520,682 'appid':530 'arb.string':494,497 'architectur':30 'assert':163,320,325,393,415 'assertequ':364 'assertexist':398 'assertnotnul':420 'assertvis':539 'awaititem':324 'base':458,469 'bash':543 'block':698 'bom':237 'bubblefactori':652 'bubblefactory.getkey':504 'bubblefactorykeytest':483 'call':737 'categori':491,500,505,508 'chat':397,540 'chatuikit':140,274,634,647,659,671 'chatuikit-compos':139,646,658 'chatuikit-cor':273,633 'chatuikit-kotlin':670 'check':452 'ci':20,108,548 'class':297,379,436,482 'class.java':444 'clickedconvers':404,411,421 'clicknavig':402 'collect':317 'collector':348 'com.cometchat.uikit.core.events.cometchatevents':337 'com.cometchat.uikit.core.events.cometchatmessageevent':339 'com.example.jetpackuikit':531 'cometchat':2,6,25,32,38,45,81,88,110,124,133,740 'cometchat-android-v6-compose-components':31 'cometchat-android-v6-extensions':24 'cometchat-android-v6-kotlin-components':37 'cometchat-android-v6-testing':1 'cometchat-android-v6-troubleshooting':123 'cometchatconvers':392,409 'cometchatconversationstest':380 'cometchatconversationsviewmodel':314 'cometchatconversationsviewmodeltest':298 'cometchatevents.emitmessageeventsync':359 'cometchatevents.messageevents.collect':356 'cometchatmessageevent.messagesent':360 'cometchatthem':391,408,692 'companion':22 'compat':723 'compon':36,42,48,89,96,130,134,664,675,699 'compos':14,35,58,84,141,236,243,369,648,655,660,662,695,754 'compose-bom':235 'composetestrul':384 'composetestrule.onnodewithtext':396,416 'composetestrule.setcontent':390,407 'compositionloc':702 'configur':21,107,527,549 'connecteddebugandroidtest':620 'contain':524 'context':765 'convers':405 'conversationitem':401 'conversationsact':443 'conversationsactivitytest':437 'conversationslist':388,447 'core':56,164,185,201,223,275,635 'core-test':184 'coroutin':178,286,709,713 'coroutines-test':285 'createandroidcomposerul':760 'createcomposerul':385,758 'data':331 'datasourc':641,725 'debug':119,241 'debug-on':240 'debugimplement':245,250 'depend':137,144,700 'directori':523 'displaystitl':389 'distribut':582,608 'e.g':471 'e2e':678 'element':395 'emit':302,342 'enabl':256 'end':67,69,103,105,513,515 'end-to-end':66,102,512 'espresso':17,61,91,222,423,674 'espresso-cor':221 'event':334,344,351,643 'events.add':357 'events.size':366 'expect':330 'extens':28 'factori':486 'flow':100,687 'foral':493 'format':488 'framework':627 'full':685 'fun':300,341,387,400,446 'funspec':484 'get':381,438 'github':551 'hard':688 'import':291,293,295,336,338,373,375,377,428,430,432,434,474,476,478,480 'initi':745 'inject':315 'inputtext':535 'instrument':212,588 'instrumented-test':587 'interact':413,666,677,752 'io.kotest':152,160,168 'io.kotest.core.spec.style.funspec':475 'io.kotest.property.arb':479 'io.kotest.property.arbitrary.string':481 'io.kotest.property.forall':477 'isdisplay':448,454 'issu':121 'it.usejunitplatform':265 'java':579,605 'java-vers':578,604 'jetpack':521 'job':354,560 'job.cancel':367 'junit':148,149,216,257,637,650 'junit4':231 'junit5':156 'key':487,503,507 'kotest':154,162,170,260,461,465,638,651,722 'kotest-assertions-cor':161 'kotest-properti':169 'kotest-runner-junit5':153 'kotlin':41,94,143,208,261,290,335,372,425,427,473,672 'kotlinx':177,708 'kotlinx-coroutines-test':176,707 'kotlinx.coroutines.test.runtest':292 'latest':569,595 'launch':355 'launchapp':532 'level':616 'lifecycl':192 'lifecycle-runtime-test':191 'live':271 'loadconvers':301 'locat':626 'login':534 'maco':594 'macos-latest':593 'maestro':18,72,99,511,525,544,684 'maestro/login_flow.yaml':529,546 'make':734 'manifest':255 'master':519,681 'master-app':680 'master-app-jetpack':518 'match':453 'messag':343 'messageadapterpropertytest.kt':472 'messagestatus.success':362 'mock':308,310,316,724,749 'mockdatasourc':307 'mockito':200,207,289,639 'mockito-cor':199 'mockito-kotlin':206 'mockmessag':361 'modul':57 'must':742 'mutablelistof':352 'name':418,554 'need':763 'null':406 'onitemclick':410 'onview':449 'option':720 'org.jetbrains.kotlinx':175 'org.mockito':198 'org.mockito.kotlin':205 'org.mockito.kotlin.mock':294 'org.mockito.kotlin.whenever':296 'organ':623 'performclick':419 'platform':233,258 'project':463 'properti':171,457,468 'property-bas':456,467 'pull':558 'purpos':43 'push':557 'r.id.conversations':451 'reactivecircus/android-emulator-runner':611 'real':735 'receiv':346 'render':665,676 'repositori':642,727 'request':559 'resolut':654 'respons':311 'rule':382,439,689 'run':541,565,584,591 'runner':155 'runs-on':564,590 'runtest':305,349,705 'runtim':120,193 'script':618 'sdk':736,741,751 'set':97 'setcont':697 'setup':309 'share':55,278 'sharedflow':715 'skill':23,75,117 'skill-cometchat-android-v6-testing' 'source-cometchat' 'stack':281 'state':304,318,323,326 'step':570,596 'style':653 'submit':538 'success':303,328 'suit':112 'superhero1':536 'tapon':533,537 'temurin':583,609 'test':5,10,12,16,44,52,60,62,70,79,86,92,106,111,136,146,179,186,194,213,230,254,268,282,287,299,333,340,371,386,399,424,445,459,470,485,516,526,545,555,563,589,622,624,630,632,645,657,663,669,679,696,710,712,719,730,739,748,756 'testdebugunittest':586 'testimplement':147,151,159,167,174,182,189,197,204 'testopt':263 'tool':244,249 'topic-agent-skills' 'topic-ai-agent' 'topic-chat' 'topic-claude-code' 'topic-cometchat' 'topic-cursor' 'topic-messaging' 'topic-nextjs' 'topic-react' 'topic-react-native' 'topic-ui-kit' 'troubleshoot':127 'type':492,501,506,509,625 'ubuntu':568 'ubuntu-latest':567 'ui':15,59,85,229,248,253,370,394,656,668,755 'ui-test-junit4':228 'ui-test-manifest':252 'ui-tool':247 'uikit':8,46 'unit':11,51,78,145,267,562,631,644,729,747 'unit-test':561 'unittests.all':264 'unless':761 'use':73,115,122,132,464,571,574,597,600,610,691,704,716,757 'usejunitplatform':717 'user':417,686 'v2':612 'v4':573,576,599,602 'v6':4,9,27,34,40,47,126 'val':306,312,322,350,353,383,440,502 'valu':703 'var':403 'version':580,606 'view':64,95,426,667 'viewmodel':13,50,82,269,270,313,640,731 'viewmodel.uistate.test':321 'viewmodel/datasource':29 'withid':450 'work':128 'wrapper':693 'write':77,83,90 'x':158,166,173,181,188,196,203,210,218,225 'x.x':239 'yaml':528,553","prices":[{"id":"9021d13d-d2d0-453f-9386-96c62bed2e3a","listingId":"d52f10c9-0f6c-450b-a121-4b9045ab4596","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cometchat","category":"cometchat-skills","install_from":"skills.sh"},"createdAt":"2026-05-07T13:05:06.754Z"}],"sources":[{"listingId":"d52f10c9-0f6c-450b-a121-4b9045ab4596","source":"github","sourceId":"cometchat/cometchat-skills/cometchat-android-v6-testing","sourceUrl":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-android-v6-testing","isPrimary":false,"firstSeenAt":"2026-05-07T13:05:06.754Z","lastSeenAt":"2026-05-18T19:04:47.466Z"}],"details":{"listingId":"d52f10c9-0f6c-450b-a121-4b9045ab4596","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cometchat","slug":"cometchat-android-v6-testing","github":{"repo":"cometchat/cometchat-skills","stars":27,"topics":["agent-skills","ai-agent","chat","claude-code","cometchat","cursor","messaging","nextjs","react","react-native","ui-kit"],"license":null,"html_url":"https://github.com/cometchat/cometchat-skills","pushed_at":"2026-05-18T05:04:24Z","description":"Add CometChat chat to any React, Next.js, React Native, Angular, Android, iOS, or Flutter project through your AI coding agent. Works with Claude Code, Cursor, Codex, VS Code Copilot, Windsurf, Cline, Kiro, and 50+ more agents.","skill_md_sha":"f01f573672a3d8750bd48ccf8a7a893fc79e7405","skill_md_path":"skills/cometchat-android-v6-testing/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-android-v6-testing"},"layout":"multi","source":"github","category":"cometchat-skills","frontmatter":{"name":"cometchat-android-v6-testing","license":"MIT","description":"CometChat Android UIKit v6 testing — unit testing ViewModels, Compose UI testing, Espresso, Maestro, and CI configuration","compatibility":"Android 9.0+ (API 28); Kotlin 1.9+; com.cometchat:chatuikit-compose-android:6.x / com.cometchat:chatuikit-kotlin-android:6.x"},"skills_sh_url":"https://skills.sh/cometchat/cometchat-skills/cometchat-android-v6-testing"},"updatedAt":"2026-05-18T19:04:47.466Z"}}