{"id":17867153,"url":"https://github.com/caarmen/roblectricinstrumentationsharedtestdemo","last_synced_at":"2025-04-02T22:13:34.995Z","repository":{"id":213776170,"uuid":"734905603","full_name":"caarmen/RoblectricInstrumentationSharedTestDemo","owner":"caarmen","description":"Demo of running one UI test file on both robolectric and instrumented devices","archived":false,"fork":false,"pushed_at":"2024-03-19T22:22:31.000Z","size":128,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-08T12:30:44.786Z","etag":null,"topics":["android-testing","robolectric"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/caarmen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-23T01:20:15.000Z","updated_at":"2023-12-23T01:37:21.000Z","dependencies_parsed_at":"2023-12-23T02:45:25.166Z","dependency_job_id":"18e5dc71-688a-4130-b877-8b4ef2f0147f","html_url":"https://github.com/caarmen/RoblectricInstrumentationSharedTestDemo","commit_stats":null,"previous_names":["caarmen/roblectricinstrumentationsharedtestdemo"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caarmen%2FRoblectricInstrumentationSharedTestDemo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caarmen%2FRoblectricInstrumentationSharedTestDemo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caarmen%2FRoblectricInstrumentationSharedTestDemo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/caarmen%2FRoblectricInstrumentationSharedTestDemo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/caarmen","download_url":"https://codeload.github.com/caarmen/RoblectricInstrumentationSharedTestDemo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246899666,"owners_count":20851898,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android-testing","robolectric"],"created_at":"2024-10-28T09:44:34.807Z","updated_at":"2025-04-02T22:13:34.972Z","avatar_url":"https://github.com/caarmen.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Demo app of unified Robolectric/Instrumentation test\n\n## New Github repository\n✨ **A more complete project is in a new repository**:\nhttps://github.com/caarmen/RobolectricInstrumentationWorkshop\n\nThe new repo has a simpler shadow implementation, in kotlin instead of java.\n\n\n---\n\nThis project provides a basic example of a UI test that can be run both on\nRobolectric and on an emulator or device.\n\nThe app's UI is in compose.\n\nThe app's structure is like this:\n\n```\n┌───────────────┐                       ┌───────────────┐                  ┌──────────────┐\n│ MainActivity  │                       │ SecondActivity│                  │MainActivity  │\n│               │                       │               │                  │              │\n│ Label         │                       │ Text input    │                  │Label         │\n│ \"\"            │                       │ \"Hello\"       │                  │\"Hello\"       │\n│               │                       │               │                  │              │\n│               │                       │               │                  │              │\n│               │                       │               │                  │              │\n│ Button        │      startActivity    │ Button        │     finish       │Button        │\n│ \"Click me\"    ├──────────────────────►│ \"Close\"       ├─────────────────►│\"Click me\"    │\n│               │       ForResult       │               │                  │              │\n│               │                       │               │                  │              │\n└───────────────┘                       └───────────────┘                  └──────────────┘\n```\n\nThe `MainActivity` has a label (initially blank) and a button \"Click me\".\nWhen the user clicks on \"Click me\", `MainActivity` launches `SecondActivity` with\n`startActivityForResult().`\n\nIn `SecondActivity`, the user can type some text in a text input. When they click\nthe button \"Close\", `SecondActivity` sets the result with an `Intent` containing\nthe contents of the entered text in an intent extra. `SecondActivity` finishes.\n\nWhen the control returns to `MainActivity`, its activity result callback extracts\nthe text from the intent extras, and sets the label with this text.\n\n## Tests\n\nThere is one test `SharedUnitTest`, in `src/sharedTest/java`. It uses\n[AndroidComposeTestRule](https://developer.android.com/jetpack/compose/testing#componentactivity)\nto launch `MainActivity` and complete the flow described above.\n\nThis type of test works by default when run as an instrumented test on an emulator or a device.\n\nOn Robolectric however, by default the test would fail: After clicking on the \"Click me\" button\nin `MainActivity`, Robolectric doesn't actually launch `SecondActivity`. Any interactions in tests\nwith views in `SecondActivity` would therefore fail.\n\n### Solution (workaround) for Robolectric\nA basic custom Robolectric Shadow class, `ShadowActivity`, inside `src/test/java`, provides an\nexample workaround. It does the following:\n* In `startActivityForResult`, actually launches the next activity with the `ActivityScenario` api.\n* In `finish()` (of the next activity), sends its result back to the calling activity.\n* In `reset()`, clears static state. This is called at the end of each test.\n\nThis custom `ShadowActivity` is defined in `app/src/test/resources/robolectric.properties`.\n\n## Running tests\n\nTests can be run in a few ways.\n### On the command-line\n`./gradlew testDebugUnitTest connectedDebugAndroidTest`\n\nThis produces reports in `app/build/reports/tests` (subfolders `tests` and `androidTests`)\n\n### In Android Studio\nClicking the green triangle next to a test may not work 😢.\n\nInstead, you can do the following:\nRun -\u003e Edit Configurations... -\u003e Gradle\n* For a robolectric test: Put the following for \"Run\":\n    ```\n    :app:testDebugUnitTest --tests \"com.example.simpledemo.SharedUnitTest\"\n    ```\n    You should be able to run the test in debug mode as well, with breakpoints.\n* For an instrumentation test: Put the following for \"Run\":\n    ```\n    :app:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=\"com.example.simpledemo.SharedUnitTest\"\n    ```\n    If you run the test in debug mode, breakpoints won't be hit. 😥 Don't dispair, there's\n    a workaround (we've got lots of them here!). You can temporarily comment-out this part of\n    `app/build.gradle.kts`:\n    ```kotlin\n    sourceSets.named(\"test\") {\n        //java.srcDirs(\"src/sharedTest/java\")\n    }\n    ```\n    Android Studio apparently doesn't like the same file being used by multiple source sets,\n    even though it works fine on the command line. With this modification, you can run the\n    test by clicking the triangle, and it will run on a connected emulator/device, with\n    breakpoint debugging possible.\n\n## Limitations\nIn addition to the limitations with launching tests from Andrdoid Studio, mentioned above,\na few caveats are worth mentioning.\n\nThis has only been tested on the very basic example app here.\n\nTo support launching activities \"normally\" (not \"for result\"), `startActivity` should be\nimplemented in a similar way.\n\n### Why java for the `ShadowActivity`?\nThe shadow class is in Java, not Kotlin. This is due to an issue with the cleanup function `reset()`.\nThis function must be public and static. The robolectric processor looks for `@Resetter` annotated methods\nwhich are `public static void`, on shadow classes, so it can know to invoke them at the end\nof tests. Normally, this could be done in Kotlin, by defining a method annotated with `@JvmStatic`,\ninside a `companion object`, and by using kapt instead of annoationProcessor. However, when doing this,\nthe generated Kotlin stub file doesn't have the `reset` method generated correctly: it's defined\nas a non-static method inside a static inner `companion` class. It's as if `@JvmStatic` were ignored.\n\n```java\nclass ShadowActivity {\n    //...\n    public static class companion {\n        public void reset() { // not static, not on `ShadowActivity`.\n            // cleanup\n        }\n    }\n}\n```\n\n### Disclaimers\nThe compose code in the activities is most certainly not following best practices! 😅\nThe idea is to have the simplest code possible (not necessarily the most robust),\njust for demonstration purposes.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaarmen%2Froblectricinstrumentationsharedtestdemo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcaarmen%2Froblectricinstrumentationsharedtestdemo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcaarmen%2Froblectricinstrumentationsharedtestdemo/lists"}