{"id":27304420,"url":"https://github.com/sergio-sastre/android-screenshot-testing-playground","last_synced_at":"2025-04-12T03:25:03.896Z","repository":{"id":39877332,"uuid":"394010429","full_name":"sergio-sastre/Android-screenshot-testing-playground","owner":"sergio-sastre","description":"A sample repo to introduce screenshot testing in Android with different libraries","archived":false,"fork":false,"pushed_at":"2025-02-13T16:36:10.000Z","size":2139,"stargazers_count":328,"open_issues_count":4,"forks_count":25,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-02-13T17:36:33.318Z","etag":null,"topics":["android","instrumented-tests","kotlin","screenshot-testing","snapshot-testing","testing"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sergio-sastre.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","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},"funding":{"github":["sergio-sastre"]}},"created_at":"2021-08-08T16:17:30.000Z","updated_at":"2025-02-12T06:55:57.000Z","dependencies_parsed_at":"2024-01-14T12:45:43.609Z","dependency_job_id":"1cd7cb69-d7e6-4578-9ffe-72594c02dab5","html_url":"https://github.com/sergio-sastre/Android-screenshot-testing-playground","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergio-sastre%2FAndroid-screenshot-testing-playground","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergio-sastre%2FAndroid-screenshot-testing-playground/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergio-sastre%2FAndroid-screenshot-testing-playground/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sergio-sastre%2FAndroid-screenshot-testing-playground/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sergio-sastre","download_url":"https://codeload.github.com/sergio-sastre/Android-screenshot-testing-playground/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248511124,"owners_count":21116357,"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","instrumented-tests","kotlin","screenshot-testing","snapshot-testing","testing"],"created_at":"2025-04-12T03:25:02.978Z","updated_at":"2025-04-12T03:25:03.876Z","avatar_url":"https://github.com/sergio-sastre.png","language":"Kotlin","funding_links":["https://github.com/sponsors/sergio-sastre"],"categories":[],"sub_categories":[],"readme":"part of blog posts\u003c/br\u003e\n\u003ca href=\"https://androidweekly.net/issues/issue-479\"\u003e\n\u003cimg src=\"https://androidweekly.net/issues/issue-479/badge\"\u003e\n\u003c/a\u003e\u003ca href=\"https://androidweekly.net/issues/issue-485\"\u003e\n\u003cimg src=\"https://androidweekly.net/issues/issue-485/badge\"\u003e\n\u003c/a\u003e\u003ca href=\"https://androidweekly.net/issues/issue-512\"\u003e\n\u003cimg src=\"https://androidweekly.net/issues/issue-512/badge\"\u003e\n\u003c/a\u003e\n\u003cimg src=\"https://androidweekly.net/issues/issue-558/badge\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://androidweekly.net/issues/issue-596\"\u003e\n\u003cimg src=\"https://androidweekly.net/issues/issue-596/badge\"\u003e\n\u003c/a\u003e\n\n# Android screenshot testing playground \u003c/br\u003e\n\n![snapshotVsUiTests](https://user-images.githubusercontent.com/6097181/144911921-bae6182b-dae7-4f59-9dba-c88c9052b9b7.gif)\n\nA sample repo to introduce screenshot testing in Android.\u003cbr/\u003e It contains a wide variety of\nexamples written with different screenshot testing libraries for a better comparison among them.\nThese examples include tests for screens like the one above (module `:recyclerviewscreen`)\n\n\u003e **Warning**\u003c/br\u003e\n\u003e It is configured with AGP 8.7.2, so it requires Android Studio Lady Bug or higher!\n\n## Awards\n\n\u003cp align=\"center\"\u003e\n   \u003cimg src=\"https://github.com/sergio-sastre/Android-screenshot-testing-playground/assets/6097181/2538e857-e3c4-4ad6-8737-954b3a3ab7b2\"\u003e\n\u003c/p\u003e\n\nAndroid-screenshot-testing-playground won a **[Google Open Source Peer Bonus](https://github.com/sergio-sastre/Android-screenshot-testing-playground/files/15421726/OSPB-2024-Sergio-Sastre-award-letter.pdf)\naward in 2024**, whose winners were listed in [Google Open Source blog](https://opensource.googleblog.com/2024/06/google-open-source-peer-bonus-program-first-group-2024-recipients.html). Thanks to everybody who has found this project insightful and helped get\nAndroid-screenshot-testing-playground where it is today.\n\u003cbr clear=\"left\"/\u003e\n\n## Sponsors\n\nThanks to [Screenshotbot](https://screenshotbot.io) for their support!\n[\u003cimg align=\"left\" width=\"100\" src=\"https://user-images.githubusercontent.com/6097181/192350235-b3b5dc63-e7e7-48da-bdb6-851a130aaf8d.png\"\u003e](https://screenshotbot.io)\n\nBy using Screenshotbot instead of the in-build record/verify modes provided by most screenshot\nlibraries, you'll give your colleages a better developer experience, since they will not be required\nto manually record screenshots after every run, instead getting notifications on their Pull\nRequests.\n\u003cbr clear=\"left\"/\u003e\n\u003cbr/\u003e\n\nThanks to [EmergeTools](https://www.emergetools.com) for their support!\n[\u003cimg align=\"left\" width=\"100\" src=\"https://www.emergetools.com/images/emergetoolsstandard.png\"\u003e](https://docs.emergetools.com/docs/snapshot-management-diffing)\n\nEmerge automatically generates and diffs snapshots on your behalf, eliminating complicated CI setup\nwith Emulators, file storage, and golden/diffing management.\n\u003cbr clear=\"left\"/\u003e\n\n# Introduction\n\nThis repo showcases how to snapshot test Dialogs, ViewHolders, RecyclerViews, Activities, Fragments, Jetpack Compose \u0026 **Composable @Previews**!\u003cbr/\u003e\nAnd even better: It is ready for you to add your own examples and try screenshot testing with many\nlibraries on your own!\n\nIn order to help find the desired examples, the app is modularized accordingly:\n\n1. `:dialogs`: Showcases how to screenshot test dialogs created with DialogBuilder from the android\n   View system. Examples for Compose dialogs will be added as well\n2. `:recyclerviewscreen`: Contains screenshot tests for Activities, Fragments, ViewHolders and\n   RecyclerViews.\n3. `:recyclerviewscreen-previews`: Includes screenshot tests examples generated from @Previews. This wrap a `ViewHolder` inside an `AndroidView` Composable to enable Previews for XML-based layouts under `:recyclerviewscreen`\n4. `:lazycolumnscreen`: Includes Jetpack Compose screenshot tests examples, as well as examples for\n   Activities \u0026 Fragments.\n5. `:lazycolumnscreen-previews`: Includes screenshot tests examples generated from @Previews of Composables in `:lazycolumnscreen` \n\nEach of these modules contains submodules. Each submodule name corresponds to a screenshot testing\nlibrary. You'll find screenshot test examples with that library in it.\n\nAs of June 2024, there are many screenshot testing libraries that facilitate automated screenshot\ntesting, namely:\n\n1. Google [Compose Preview Screenshot Testing tool](https://developer.android.com/studio/preview/compose-screenshot-testing)\n2. Cashapp [Paparazzi](https://github.com/cashapp/paparazzi)\n3. [Roborazzi from takahirom](https://github.com/takahirom/roborazzi)\n4. Dropbox [Dropshots](https://github.com/dropbox/dropshots)\n5. [Shot from pedrovgs](https://github.com/pedrovgs/Shot)\n6. Ndtp [Android-testify](https://github.com/ndtp/android-testify) \u003csup\u003e1\u003csup/\u003e\n7. Facebook [screenshot-tests-for-android](https://github.com/facebook/screenshot-tests-for-android) \n8. QuickBird [Snappy](https://github.com/QuickBirdEng/kotlin-snapshot-testing)\n\nAll of them have their own pros and cons.\nThe ultimate goal of this repo is to help you choose the libraries that better meet your project's\nneeds!\n\nIn order to do that, it contains the same/similar examples but written with different libraries:\n\n1. [Compose Preview Screenshot Testing tool](https://developer.android.com/studio/preview/compose-screenshot-testing) (check `:lazycolumnscreen-previews` and `:recyclerviewscreen-previews`)\n2. [Paparazzi](https://github.com/cashapp/paparazzi)\n3. [Roborazzi](https://github.com/takahirom/roborazzi)\n4. [Dropshots](https://github.com/dropbox/dropshots) (coming to `:lazycolumnscreen-previews` soon, but already available in `:recyclerviewscreen-previews`)\n5. [Shot](https://github.com/pedrovgs/Shot) (coming to `:lazycolumnscreen-previews` soon, but already available in `:recyclerviewscreen-previews`)\n6. [Android-testify](https://github.com/ndtp/android-testify) (coming to `:lazycolumnscreen-previews` soon, but already available in `:recyclerviewscreen-previews`)\n\n**BONUS**:\nIt also contains examples of **Cross-Library Screenshot Tests**: *the very same screenshot tests\nrunning with multiple libraries, namely: Paparazzi, Roborazzi, Shot, Dropshots \u0026 Android-Testify*.\nCompose Screenshot Testing Tool support coming soon.\nFor that it\nuses [Android UI Testing Utils 2.x.x](https://github.com/sergio-sastre/AndroidUiTestingUtils)\n\nYou can read more about it in this blog post series:\n\n1. [A World Beyond Libraries: Cross-Library Screenshot tests on android](https://sergiosastre.hashnode.dev/a-world-beyond-libraries-cross-library-screenshot-tests-on-android)\n2. [Write Once, Test Everywhere: Cross-Library Screenshot Testing with AndroidUiTestingUtils 2.0.0](https://sergiosastre.hashnode.dev/write-once-test-everywhere-cross-library-screenshot-testing-with-androiduitestingutils)\u003c/br\u003e\u003c/br\u003e\n   More screenshot test examples, as well as examples with other libraries will be continuously\n   added.\n\n\u003csup\u003e1\u003c/sup\u003e Android-testify was started at Shopify and changed to Ndtp in summer 2022.\n\n## Table of Contents\n\n- [Before you start...](#before-you-start)\n    - [The need for screenshot testing](#the-need-for-screenshot-testing)\n    - [Emulators](#emulators)\n        - [Animations](#animations)\n- [Comparing screenshot testing libraries](#comparing-screenshot-testing-libraries)\n    - [Paparazzi vs. on-device screenshot testing libraries](#paparazzi-vs-on-device-screenshot-testing-libraries)\n        - [Summary: Pros and Cons](#summary-pros-and-cons)\n- [Recording and verifying screenshots](#recording-and-verifying-screenshots)\n    - [On-device tests with Android Orchestrator](#on-device-tests-with-android-orchestrator)\n    - [Compose Preview Screenshot Testing tool](#compose-preview-screenshot-testing-tool)\n    - [Paparazzi](#paparazzi)\n    - [Roborazzi](#roborazzi)\n    - [Dropshots](#dropshots)\n    - [Shot](#shot)\n    - [Android-Testify](#android-testify)\n    - [Cross-library](#cross-library)\n- [Parameterized screenshot tests](#parameterized-screenshot-tests)\n- [Filtered parameterized screenshot tests](#filtered-parameterized-screenshot-tests)\n    - [Instrumented tests](#instrumented-tests)\n    - [Gradle tests](#gradle-tests)\n- [What is coming next](#what-is-coming-next)\n- [Code attribution](#code-attribution)\n- [Attribution of icons in the app](#attribution-of-icons-in-the-app)\n\n## Before you start...\n\n### The need for screenshot testing\n\nI strongly recommend to have a look at my blog series\non [snapshot testing](https://sergiosastre.hashnode.dev/an-introduction-to-snapshot-testing-on-android-in-2021)\n\nIf reading is not your thing, you can always watch my 2021 Droidcon tech-talks on the matter:\n\n1. [Droidcon Berlin 2021](https://www.droidcon.com/2021/11/10/an-introduction-to-effective-snapshot-testing-on-android/)\n2. [Droidcon London 2021](https://www.droidcon.com/2021/11/17/an-introduction-to-effective-snapshot-testing-on-android-2/)\n\n### Emulators\n\nFor instrumented screenshot testing (which excludes Paparazzi), I've been using emulators running\nAPI 27-31.\nMoreover, if you are running screenshot tests on a Windows machine, beware that Shot had\nsome [issues in the past](https://github.com/pedrovgs/Shot/issues/244), although they should be\nalready solved.\n\n#### Animations\n\nInstrumented screenshot tests might have issues due to running animations at the moment the snapshot\nare taken.\n\nIn order to keep animations disabled, this project uses `testOptions { animationsDisabled = true }`\nin the gradle file. Unfortunately, it does not work on all APIs (e.g. API 26 or lower). Therefore,\nif you\ncome across some issues, disable animations on the emulator *via settings* before running the\nscreenshot tests.\n\n## Comparing screenshot testing libraries\n\n### Paparazzi vs. on-device screenshot testing libraries\n\n**Need for emulators**\n\nGoogle's brand new Compose Screenshot Testing tool, as well as Roborazzi \u0026 Paparazzi let you run screenshot tests on the JVM, without emulators/devices.\nI'm still evaluating Roborazzi and Compose Screenshot Testing tool, so this section refers to Paparazzi only, and will be extended in\nthe future.\n\nAlthough running screenshot tests on the JVM comes with some speed wins, its main advantage is that\none doesn't have to deal with emulator and their problems, such as:\n\n1. Emulators eventually freezing/crashing (specially on CI)\n2. \"Insufficient storage\" exception\n3. \"Out of Memory\" exceptions\n\n**Rendering elevation in generated screenshots**\n\nPaparazzi uses PixelCopy to generate bitmaps out of Views.\nMost on-device screenshot testing frameworks use Canvas as default\u003csup\u003e1\u003c/sup\u003e to generate bitmaps,\nwhat ignores elevation.\n\nThis is specially noticeable in API 31:\n\n\u003cp align=\"center\"\u003e\n\u003cimg width=\"350\" src=\"https://user-images.githubusercontent.com/6097181/209678572-c4610c75-0122-41c4-bfae-304e8a633b2d.jpeg\"\u003e\n\u003c/p\u003e\n\nHowever, on-device screenshot testing libraries also accept bitmaps as arguments of their\ntake/verify screenshot methods.\n\n```kotlin\n// Shot\ncompareScreenshot(\n    bitmap = pixelCopyBitmapFromView\n)\n```\n\n```kotlin\n// Dropshots\ndropshots.assertSnapshot(\n    bitmap = pixelCopyBitmapFromView\n)\n```\n\nTherefore, they could render elevation by converting views to bitmaps using PixelCopy.\n[Android UI Testing Utils](https://github.com/sergio-sastre/AndroidUiTestingUtils) provides\nthe `drawToBitmapWithElevation()` method for that.\n\nAnd the resulting screenshot would render elevation\n\u003cp align=\"center\"\u003e\n\u003cimg width=\"350\" src=\"https://user-images.githubusercontent.com/6097181/209678214-9e4664b7-f898-4173-a9f7-36dfc764b035.png\"\u003e\n\u003c/p\u003e\n\nYou can find such examples in this repo, under the `bitmap` folder under any `:dropshots`, `:shot`\nand `:android-testify` module.\n\n\u003csup\u003e1\u003c/sup\u003e Shot doesn't use Canvas when using `compareScreenshot(composeRule)`, so those\nscreenshots draw elevation. It does use Canvas for Views, Activities, Fragments \u0026 Dialogs though.\n\n**Screenshot testing Activites and Fragments**\n\nActivityScenarios and FragmentScenarios are compatible with Robolectric, which stubs the Android\nframework to run instrumented tests on the JVM.\nTherefore, we could also use Robolectric to run Activity/Fragment screenshot tests on the JVM with\nPaparazzi, theoretically.\nHowever, it crashes in doing so with some Byte Buddy exception at runtime, likely due to some\nclashes with Robolectric.\n\nRoborazzi, on the other hand, is built on top of Robolectric, so ActivityScenarios and\nFragmentScenarios are compatible with it.\n\nThis means, only on-device screenshot testing frameworks \u0026 Roborazzi can be used for snapshoting\nActivites/Fragments. Only Paparazzi cannot.\n\n**Rendering problems**\n\nPaparazzi relies on layoutlib to record screenshots. That's a private library used to render the xml\nlayouts and Compose previews in Android Studio.\nThis comes with some limitations.\nFor example,\n\n1. [Composables using NavHost cannot be rendered](https://github.com/cashapp/paparazzi/issues/635)\n   in the @Preview, and therefore, Paparazzi cannot either, for now.\n2. Renders incorrectly UI elements that use multiple `View.animate()`\n   and/or `ObjectAnimator.ofPropertyValuesHolder()` for animations. You can check it out yourself by\n   running\n   `./gradlew :recyclerviewscreen:paparazzi:recordPaparazziDebug` in this repo. For instance:\n\n| View.animate()                                                                                                               |                                               View.animate() + ObjectAnimator                                                |\n|------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------:|\n| \u003cimg width=\"350\" src=\"https://user-images.githubusercontent.com/6097181/209678715-c7356e7b-7d4c-413a-942f-76e42e445d0b.png\"\u003e | \u003cimg width=\"350\" src=\"https://user-images.githubusercontent.com/6097181/209678760-b84bb060-03fb-4050-b283-ab2e28415df7.png\"\u003e |\n\nOn the other hand, on-device screenshot testing has its own issues as well. Most of them happen due\nto the **hardware accelerated drawing model**. This happens for example, when running the same test\non machines with different architectures\u003csup\u003e1\u003c/sup\u003e e.g. Macbook Pro with M1 chip vs. Intel x64. It\nmight cause issues related to:\n\n1. Shadows \u0026 elevation\n2. Font smoothing \u0026 anti-aliasing\n3. Image decompression and rendering\n4. Alpha-blending.\n\nNevertheless, you can solve/mitigate such issues as follows:\n\n1. Hardware acceleration can be enabled/disabled at different levels e.g. Activity, Window, View...\n   to reduce such issues. Keep in mind that this affects how the screenshots are rendered.\n2. Most libraries provide a \"tolerance/maxPixelDiff\" mechanism to set some error threshold when\n   verifying the screenshot. Although it isn't a real fix, you could use it to mitigate such issues.\n   There is an [interesting blog](https://ndtp.github.io/android-testify/blog/platform-differences)\n   on android-testify about this.\n3. The safest solution is to generate the screenshots only on the CI, then pushing them in a new\n   commit on your PRs. Same goes for \"verify\". This ensures that the screenshots are always\n   generated using the same hardware.\n\nRead more about hardware acceleration in\nthe [official Android documentation](https://developer.android.com/topic/performance/hardware-accel)\n\n\u003csup\u003e1\u003c/sup\u003e There is evidence of such problems in Paparazzi as well, when running tests on\ndifferent operating systems, as stated [here](https://github.com/cashapp/paparazzi/issues/311)\n\n**Stability: Android Gradle Plugin and Compose runtime updates**\n\nUnfortunately, any Android Gradle Plugin (a.k.a. AGP) or Compose update can make your working\nPaparazzi screenshot tests break... or fix those broken.\nFor instance:\n\n1. When updating to Compose runtime\n   1.4.x: [this issue](https://github.com/cashapp/paparazzi/issues/641) \u0026 [the corresponding fix](https://github.com/cashapp/paparazzi/pull/650/files)\n2. Before updating AGP to that required by Android Studio Dolphin:\n   [Compose Dialog rendering issue](https://github.com/cashapp/paparazzi/issues/619)\n\n#### Summary: Pros and Cons\n\nLet's summarize.\n\n**Pros**\n\n1. No emulators needed.\n    1. Faster\n    2. No emulator troubleshooting\n2. Uses PixelCopy by default to generate bitmaps out of the views. Thus, screenshots render UI\n   elements with elevation (e.g. shadows)\n\n**Cons**\n\n1. Cannot screenshot Activities or Fragments\n2. Rendering problems\n    1. Incorrect screenshots for UI components that call View.animate() or\n       ObjectAnimator.ofPropertyValuesHolder() several times.\n    2. Only renders what the Compose @Previews can display\n3. Fragile to AGP \u0026 Jetpack Compose updates\n\n## Recording and verifying screenshots\n\nFor screenshot testing, 2 tasks are required:\n\n1. **Record**. When executed, it generates a snapshot file for each test that will be taken as\n   reference.\n2. **Verify**. When executed, it generates a snapshot file for each test that will be compared,\n   pixel by pixel, with the one taken as reference when recording.\n\nAll Screenshot testing frameworks provide at least these 2 tasks.\n\n\u003e **Warning**\u003c/br\u003e\n\u003e All the commands in the description are for MacOS. You might need to adjust them depending on the\n\u003e operating system of your machine.\n\n### On-device tests with Android Orchestrator\n\nBy default, Android Orchestrator is disabled.\nThat's because it makes the screenshot test run much slower, while adding no value. Android\norchestrator might make\nsense when running screenshot test that might influence each other. For instance, when screenshot\ntesting Activities or Fragments\nwhere we fetch some data.\n\nFor screenshot test that inflate a view and populate it with some data before the snapshot, it makes\nlittle sense.\n\nHowever, you can run Dropshots, Shot and Android-testify screenshot tests with Android Orchestrator\nin this repo by adding the following to the corresponding command:\n`-PuseOrchestrator`\n\nFor instance, to record with Dropshots:\n`./gradlew :dialogs:dropshots:connectedAndroidTest -Pdropshots.record -PuseOrchestrator`\n\n### [Compose Preview Screenshot Testing tool](https://developer.android.com/studio/preview/compose-screenshot-testing)\nThe brand new Google's screenshot testing tool!\nNo emulators required. \n\nThe tests are auto-generated!\nJust place your previews in the new \"screenshotTest\" source.\nFor now, it cannot auto-generate tests from previews in the \"main\" source\n\nRun the following gradle tasks\n\n1. **Record**: `./gradlew :lazycolumnscreen-previews:compose-screenshot:recordPaparazziDebug`. \n2. **Verify**: `./gradlew :lazycolumnscreen-previews:compose-screenshot:verifyPaparazziDebug`.\n\n### [Paparazzi](https://github.com/cashapp/paparazzi)\n\nNo emulators required.\nRun the following gradle tasks depending on the module:\n\n1. **Record**: `./gradlew :module_name:paparazzi:recordPaparazziDebug`. For instance:\n    1. `./gradlew :dialogs::paparazzi:recordPaparazziDebug`\n    2. `./gradlew :recyclerviewscreen:paparazzi:recordPaparazziDebug`\n    3. `./gradlew :recyclerviewscreen-previews:paparazzi:recordPaparazziDebug`\n    4. `./gradlew :lazycolumnscreen:paparazzi:recordPaparazziDebug`\n    5. `./gradlew :lazycolumnscreen-previews:paparazzi:recordPaparazziDebug`\n2. **Verify**: `./gradlew :module_name:paparazzi:verifyPaparazziDebug`. For instance:\n    1. `./gradlew :dialogs:paparazzi:verifyPaparazziDebug`\n    2. `./gradlew :recyclerviewscreen:paparazzi:verifyPaparazziDebug`\n    3. `./gradlew :recyclerviewscreen-previews:paparazzi:verifyPaparazziDebug`\n    4. `./gradlew :lazycolumnscreen:paparazzi:verifyPaparazziDebug`\n    5. `./gradlew :lazycolumnscreen-previews:paparazzi:verifyPaparazziDebug`\n\n\u003e **Note**\u003c/br\u003e\n\u003e You can record/verify the tests in parallel with the gradle property -Pparallel e.g.\n\u003e `./gradlew :module_name:paparazzi:recordPaparazziDebug -Pparallel`\n\u003e Please note that running tests in parallel is only worthwhile when dealing with a large number of\n\u003e tests.\n\n### [Roborazzi](https://github.com/takahirom/roborazzi)\n\nNo emulators required.\nRun the following gradle tasks depending on the module:\n\n1. **Record**: `./gradlew :module_name:roborazzi:recordRoborazziDebug`. For instance:\n    1. `./gradlew :dialogs::roborazzi:recordRoborazziDebug`\n    2. `./gradlew :recyclerviewscreen:roborazzi:recordRoborazziDebug`\n    3. `./gradlew :recyclerviewscreen-previews:roborazzi:recordRoborazziDebug`\n    4. `./gradlew :lazycolumnscreen:roborazzi:recordRoborazziDebug`\n    5. `./gradlew :lazycolumnscreen-previews:roborazzi:recordRoborazziDebug`\n2. **Verify**: `./gradlew :module_name:roborazzi:verifyRoborazziDebug`. For instance:\n    1. `./gradlew :dialogs:roborazzi:verifyRoborazziDebug`\n    2. `./gradlew :recyclerviewscreen:roborazzi:verifyRoborazziDebug`\n    3. `./gradlew :recyclerviewscreen-previews:roborazzi:verifyRoborazziDebug`\n    4. `./gradlew :lazycolumnscreen:roborazzi:verifyRoborazziDebug`\n    5. `./gradlew :lazycolumnscreen-previews:roborazzi:verifyRoborazziDebug`\n\nIn order to see the screenshots in Android Studio, change the view from \"Android\" to \"Project\".\n\n\u003e **Note 1**\u003c/br\u003e\n\u003e Thanks to Roborazzi plugin, it is also possible to record/verify these tests directly from Android\n\u003e Studio.\n\u003e For recording, you need to add `roborazzi.test.record=true` in your `gradle.properties` file.\n\u003e For verifying, you need to add `roborazzi.test.verify=true` in your `gradle.properties` file.\n\u003e Remember to record before verifying, to have a reference to compare with.\n\n\u003e **Note 2**\u003c/br\u003e\n\u003e You can record/verify the tests in parallel with the gradle property -Pparallel e.g.\n\u003e `./gradlew :module_name:roborazzi:recordRoborazziDebug -Pparallel`\n\u003e Please note that running tests in parallel is only worthwhile when dealing with a large number of\n\u003e tests.\n\n### [Dropshots](https://github.com/dropbox/dropshots)\n\nStart the emulator.\nThen run the following gradle tasks depending on the module:\n\n1. **Record**: `./gradlew :module_name:dropshots:connectedAndroidTest -Pdropshots.record`. For instance:\n    1. `./gradlew :dialogs:dropshots:connectedAndroidTest -Pdropshots.record`\n    2. `./gradlew :recyclerviewscreen:dropshots:connectedAndroidTest -Pdropshots.record`\n    3. `./gradlew :recyclerviewscreen-previews:dropshots:connectedAndroidTest -Pdropshots.record`\n    4. `./gradlew :lazycolumnscreen:dropshots:connectedAndroidTest -Pdropshots.record`\n2. **Verify**: `./gradlew :module_name:dropshots:connectedAndroidTest`. For instance:\n    1. `./gradlew :dialogs:dropshots:connectedAndroidTest`\n    2. `./gradlew :recyclerviewscreen:dropshots:connectedAndroidTest`\n    3. `./gradlew :recyclerviewscreen-previews:dropshots:connectedAndroidTest`\n    4. `./gradlew :lazycolumnscreen:dropshots:connectedAndroidTest`\n\n\u003e **Note**\u003c/br\u003e\n\u003e Thanks to Dropshots plugin, it is also possible to record/verify these tests directly from Android\n\u003e Studio.\n\u003e By default, it will execute the verify task. For recording, you need to add `dropshots.record` in\n\u003e your `gradle.properties` file.\n\u003e Remember to record before verifying, to have a reference to compare with.\n\n### [Shot](https://github.com/pedrovgs/Shot)\n\nStart the emulator.\nThen run the following gradle tasks depending on the module:\n\n1. **Record**: `./gradlew :module_name:shot:executeScreenshotTests -Precord`. For instance:\n    1. `./gradlew :dialogs:shot:executeScreenshotTests -Precord`\n    2. `./gradlew :recyclerviewscreen:shot:executeScreenshotTests -Precord`\n    3. `./gradlew :recyclerviewscreen-previews:shot:executeScreenshotTests -Precord`\n    4. `./gradlew :lazycolumnscreen:shot:executeScreenshotTests -Precord`\n2. **Verify**: `./gradlew :module_name:shot:executeScreenshotTests`. For instance:\n    1. `./gradlew :dialogs:shot:executeScreenshotTests`\n    2. `./gradlew :recyclerviewscreen:shot:executeScreenshotTests`\n    3. `./gradlew :recyclerviewscreen-previews:shot:executeScreenshotTests`\n    4. `./gradlew :lazycolumnscreen:shot:executeScreenshotTests`\n\n\u003e **Note**\u003c/br\u003e\n\u003e The library says the record reports can be reviewed\n\u003e at `RoadToEffectiveSnapshotTesting/dialogs/shot/build/reports/shot/debug/index.html`\n\u003e However, it is wrong. The record reports can be reviewed\n\u003e at `RoadToEffectiveSnapshotTesting/dialogs/shot/build/reports/shot/debug/record/index.html`\n\u003e The path for the verification reports is right though.\n\n### [Android-Testify](https://github.com/ndtp/android-testify)\n\nCurrently this is the only library that supports Gradle Managed Devices.\nYou can do it by directly running the following command:\n\n1. **Record\n   **: `./gradlew :module_name:android-testify:pixel3api30DebugAndroidTest -PuseTestStorage -PrecordModeGmd`.\n   For instance:\n    1. `./gradlew :dialogs:android-testify:pixel3api30DebugAndroidTest -PuseTestStorage -PrecordModeGmd`\n    2. `./gradlew :recyclerviewscreen:android-testify:pixel3api30DebugAndroidTest -PuseTestStorage -PrecordModeGmd`\n    3. `./gradlew :recyclerviewscreen-previews:android-testify:pixel3api30DebugAndroidTest -PuseTestStorage -PrecordModeGmd`\n    4. `./gradlew :lazycolumnscreen:android-testify:pixel3api30DebugAndroidTest -PuseTestStorage -PrecordModeGmd`\n2. **Verify**: `./gradlew :module_name:android-testify:pixel3api30DebugAndroidTest`. For instance:\n    1. `./gradlew :dialogs:android-testify:pixel3api30DebugAndroidTest -PuseTestStorage`\n    2. `./gradlew :recyclerviewscreen:android-testify:pixel3api30DebugAndroidTest -PuseTestStorage`\n    3. `./gradlew :recyclerviewscreen-previews:android-testify:pixel3api30DebugAndroidTest -PuseTestStorage`\n    4. `./gradlew :lazycolumnscreen:android-testify:pixel3api30DebugAndroidTest -PuseTestStorage`\n\n\u003e **Note 1**\u003c/br\u003e\n\u003e You might also want to add the following gradle parameter to avoid verify failing for screenshots with elevation when executed on the same machine:\n\u003e -Pandroid.testoptions.manageddevices.emulator.gpu=host\n\n\u003e **Note 2**\u003c/br\u003e\n\u003e Before verifying, you need to copy the generated screenshots under the corresponding module's\n\u003e build/outputs/managed_device_android_test_additional_output/...) to the correct location, as\n\u003e specified here: https://ndtp.github.io/android-testify/docs/recipes/gmd\n\u003e For that you can run the following gradle task:\n\u003e `./gradlew :module_name:android-testify:copyScreenshots -Pdevices=pixel3api30`. For instance:\n\u003e  1. `./gradlew :dialogs:android-testify:copyScreenshots -Pdevices=pixel3api30`\n\u003e  2. `./gradlew :recyclerviewscreen:android-testify:copyScreenshots -Pdevices=pixel3api30`\n\u003e  3. `./gradlew :lazycolumnscreen:android-testify:copyScreenshots -Pdevices=pixel3api30`\n\nIf you do not want to use Gradle Managed Devices, do as below:\nFirst, start the emulator.\nThen run the following gradle tasks depending on the module:\n\n1. **Record**: `./gradlew :module_name:android-testify:screenshotRecord`. For instance:\n    1. `./gradlew :dialogs:android-testify:screenshotRecord`\n    2. `./gradlew :recyclerviewscreen:android-testify:screenshotRecord`\n    3. `./gradlew :recyclerviewscreen-previews:android-testify:screenshotRecord`\n    4. `./gradlew :lazycolumnscreen:android-testify:screenshotRecord`\n2. **Verify**: `./gradlew :module_name:android-testify:screenshotTest`. For instance:\n    1. `./gradlew :dialogs:android-testify:screenshotTest`\n    2. `./gradlew :recyclerviewscreen:android-testify:screenshotTest`\n    3. `./gradlew :recyclerviewscreen-previews:android-testify:screenshotTest`\n    4. `./gradlew :lazycolumnscreen:android-testify:screenshotTest`\n\n### Cross-Library\n\nRun the very same screenshot tests with the screenshot testing library of your choice, among\nPaparazzi, Roborazzi, Shot, Dropshots or Android-Testify.\u003c/br\u003e\nThe most common scenario is to use 1 on-device (e.g. Shot, Dropshots, Android-Testify) and 1 JVM (\ne.g. Paparazzi, Roborazzi) screenshot library.\nFor that, check the corresponding submodules\ne.g. `:android-testify+paparazzi`, `:shot+roborazzi`, `:dropshots+roborazzi`\u003c/br\u003e\nYou would execute such tests as you would do for the corresponding library\ne.g. `./gradlew :lazycolumnscreen:dropshots+paparazzi:recordPaparazziDebug` would record with\nPaparazzi.\u003c/br\u003e\n\nFor examples containing 2+ on-device and/or 2+ JVM screenshot libraries, check the\ncorresponding `:crosslibrary` modules.\u003c/br\u003e\nSince they configure 2 on-device \u0026 2 JVM screenshot libraries, you need to pass the library name via\ncommand line for its correct execution. Here some examples with `:lazycolumnscreen` but same applies\nto `:recyclerviewscreen` and `:dialogs`\n\n1. **Record**:\n    1. Paparazzi: `./gradlew :lazycolumnscreen:crosslibrary:recordPaparazziDebug -PscreenshotLibrary=paparazzi`\n    2. Roborazzi: `./gradlew :lazycolumnscreen:crosslibrary:recordRoborazziDebug -PscreenshotLibrary=roborazzi`\n    3. Shot:      `./gradlew :lazycolumnscreen:crosslibrary:executeScreenshotTests -Precord -PscreenshotLibrary=shot`\n    4. Dropshots: `./gradlew :lazycolumnscreen:crosslibrary:connectedAndroidTest -Pdropshots.record -PscreenshotLibrary=dropshots`\n    5. Testify:   `./gradlew :lazycolumnscreen:crosslibrary:screenshotRecord -PscreenshotLibrary=android-testify`\n2. **Verify**:\n    1. Paparazzi: `./gradlew :lazycolumnscreen:crosslibrary:verifyPaparazziDebug -PscreenshotLibrary=paparazzi`\n    2. Roborazzi: `./gradlew :lazycolumnscreen:crosslibrary:verifyRoborazziDebug -PscreenshotLibrary=roborazzi`\n    3. Shot:      `./gradlew :lazycolumnscreen:crosslibrary:executeScreenshotTests -PscreenshotLibrary=shot`\n    4. Dropshots: `./gradlew :lazycolumnscreen:crosslibrary:connectedAndroidTest -PscreenshotLibrary=dropshots`\n    5. Testify:   `./gradlew :lazycolumnscreen:crosslibrary:screenshotTest -PscreenshotLibrary=android-testify`\n\n\u003e **Note**\u003c/br\u003e\n\u003e You can also record and verify via Gradle Managed Devices with Android-Testify as specified in the\n\u003e previous section:\n\u003e 1. Record: `./gradlew :lazycolumnscreen:crosslibrary:pixel3api30DebugAndroidTest -PuseTestStorage -PrecordModeGmd`\n\u003e 2. Verify: `./gradlew :lazycolumnscreen:crosslibrary:pixel3api30DebugAndroidTest -PuseTestStorage`\n\u003e\n\u003e Before verifying, you need to copy the generated screenshots under the corresponding module's\n\u003e build/outputs/managed_device_android_test_additional_output/...) to the correct location, as\n\u003e specified here: https://ndtp.github.io/android-testify/docs/recipes/gmd\n\u003e For that you can run the following gradle task:\n\u003e `./gradlew :lazycolumnscreen:crosslibrary:copyScreenshots -Pdevices=pixel3api30`:\n\nTo enable cross-library screenshot testing, it\nuses [Android UI Testing Utils 2.1.0](https://github.com/sergio-sastre/AndroidUiTestingUtils)\n\n## Parameterized Screenshot Tests\n\nSome tests in this repo are written as parameterized.\nIn doing so, we can test a given view with all possible different configurations, namely:\n\n1. UI mode (Light and Dark)\n2. Font size\n3. Locale\n4. Orientation\n5. Custom themes\n6. Display size\n7. View dimensions (width and/or height)\n8. Specific view states\n9. Others...\n\nWith Parameterized snapshot test we can write the test once and run it for all the desired\nconfigurations! You can read more about how we can profit from it here:\n\n- [Design a pixel perfect Android app 🎨](https://sergiosastre.hashnode.dev/design-a-pixel-perfect-android-app-with-screenshot-testing)\n\nThis can be achieved by using\neither `org.junit.runners.Parameterized`, `com.google.testing.junit.testparameterinjector.TestParameterInjector`\nor in case of Roborazzi via `org.robolectric.ParameterizedRobolectricTestRunner`, as you'll find in\nmost examples in this repo.\n\nDropshots \u0026 Shot do not offer the possibility to set such configurations; Android-Testify only\npartially; Paparazzi and Roborazzi (via Robolectric) do though. Nevertheless, we can set the desired\nconfigurations for those libraries\nwith [Android UI Testing Utils](https://github.com/sergio-sastre/AndroidUiTestingUtils), as seen in\nthe examples.\n\n\u003e **Remark**\n\u003e The Parameterized runner injects the parameter passed in the Test class constructor to every\n\u003e single test. If that is not wished, consider using the TestParameterInjector.\n\u003e On the other hand, TestParameterInjector only works with instrumented test on API 24+. Keep that\n\u003e in\n\u003e mind.\n\n## Filtered parameterized screenshot tests\n\nRunning snapshot tests for all configurations on every PR can be very time-consuming and lead to\nincrementally slower builds. One approach to solve that issue is to run only a part of those tests\non every PR (e.g. the most common config, our \"smoke/happy path\" tests) and all snapshot tests -\nor \"all\nnon-smoke tests\" - once a day (e.g. during the night or whenever it disturbs your team the least).\nIn doing so, we get informed of the most important visual regression bugs on every PR (i.e. blocking\nbugs), and still get notified of the non-blocking bugs once a day.\n\nWe can accomplish this by filtering our tests accordingly.\n\n### Instrumented tests\n\nThis applies for tests running on emulators/physical devices i.e. those using Dropshots, Shot or\nAndroid-Testify\n\nIn order to filter tests, we need to provide the corresponding test instrumentation arguments. A\nstraightforward means to do that is to use custom annotations. For instance:\n\n1. `-Pandroid.testInstrumentationRunnerArguments.annotation=com.your.package.YourAnnotation`\n2. `-Pandroid.testInstrumentationRunnerArguments.notAnnotation=com.your.package.YourAnnotation`\n\nThat works for Shot, Dropshots and Android-Testify with Gradle Managed Devices.\n\nFor Android-Testify without Gradle Managed Devices, you need to configure it, otherwise it\nuses `@ScreenshotInstrumentation`:\n\n```groovy\ndef filterAnnotation = project.hasProperty(\"filterAnnotation\") ? project.filterAnnotation : null\n\ntestify {\n    screenshotAnnotation = filterAnnotation\n}\n```\n\nand then run the record/verify command with\n`-PfilterAnnotation=com.your.package.YourAnnotation`\n\n\u003e **Warning**\u003c/br\u003e\n\u003e These arguments are supported by `org.junit.runners.Parameterized`\n\u003e and `com.google.testing.junit.testparameterinjector.TestParameterInjector`, but not by all\n\u003e runners, e.g. `JUnitParams` fails if used. I strongly recommend to\n\u003e use `com.google.testing.junit.testparameterinjector.TestParameterInjector` for filtered\n\u003e parameterized tests,\n\u003e because you can control which parameters are passed to each test method. That is not the case\n\u003e with `org.junit.runners.Parameterized`, because the parameters are injected into every single test\n\u003e method.\n\u003e Thus, you need to create different Test classes to inject different parameters.\n\nThat's why you'll find some tests in this repo with the following annotations, which are found in\nthe module `testannotations`:\n\n1. `@UnhappyPath`\n2. `@HappyPath`\n3. `@ActivityTest`\n4. `@FragmentTest`\n5. `@ViewHolderTest`\n6. `@ComposableTest`\n   ...\n\nIn order to run filtered tests, execute the following gradle tasks, for the given annotation, for\nexample,`@UnhappyPath` with shot:\n\n1. **Record\n   **: `./gradlew :module_name:shot:executeScreenshotTests -Pandroid.testInstrumentationRunnerArguments.annotation=com.example.road.to.effective.snapshot.testing.testannotations.UnhappyPath -Precord`\n2. **Verify\n   **: `./gradlew :module_name:shot:executeScreenshotTests -Pandroid.testInstrumentationRunnerArguments.annotation=com.example.road.to.effective.snapshot.testing.testannotations.UnhappyPath`\n\nThe same goes for `@HappyPath`\n\n### Gradle tests\n\nThe previous approach only works for instrumented tests. For non-instrumented tests, like those\nrunning with Paparazzi or Roborazzi, the simplest approach is to use gradle test filters. We can\npass them as parameters when executing the corresponding paparazzi test tasks.\nFor this to work, it is important to define a clear test naming convention, for example:\n\n1. UnhappyPath tests -\u003e inside `class myTestClassNameUnhappyPathTest`\n2. HappyPath tests -\u003e inside `class myTestClassNameHappyPathTest`\n\nThis enables to filter the test separately by executing:\n\n1. Only Happy path tests:\n    1. Paparazzi: `./gradlew :module_name:paparazzi:recordPaparazziDebug --tests '*UnhappyPath*'`\n    2. Roborazzi: `./gradlew :module_name:roborazzi:recordRoborazziDebug --tests '*UnhappyPath*'`\n2. Only Unhappy path tests:\n    1. Paparazzi: `./gradlew :module_name:paparazzi:recordPaparazziDebug --tests '*HappyPath*'`\n    2. Roborazzi: `./gradlew :module_name:roborazzi:recordRoborazziDebug --tests '*HappyPath*'`\n\n\u003e **Note**\u003c/br\u003e\n\u003e This approach does not work for instrumented tests though.\n\n## What is coming next:\n\n1. Comparison between libraries regarding e.g. speed, reliability, configurability, etc.\n2. More Snapshot testing samples (e.g. ScrollViews, Material you + dynamic colors...)\n3. Screenshot tests with other libraries: Facebook, ndtp/Testify, without library...\n4. Running snapshot tests on multiple devices/JVM in parallel\n6. Tips to remove flakiness\n7. Tips to increase test execution speed and more...\n\n## Code attribution\n\nSpecial thanks to [Alex Zhukovich](https://github.com/AlexZhukovich) for\nhis [CoffeeDrinksWithJetpackCompose](https://github.com/AlexZhukovich/CoffeeDrinksWithJetpackCompose)\nprojects, from which\nI've borrowed the Jetpack Compose examples!\n\n## Attribution of icons in the app\n\nIcons made by \u003ca href=\"https://www.freepik.com\" title=\"Freepik\"\u003eFreepik\u003c/a\u003e\nfrom \u003ca href=\"https://www.flaticon.com/\" title=\"Flaticon\"\u003ewww.flaticon.com\u003c/a\u003e\n\u003c/br\u003e\nIcons made by \u003ca href=\"https://www.flaticon.com/authors/surang\" title=\"surang\"\u003esurang\u003c/a\u003e\nfrom \u003ca href=\"https://www.flaticon.com/\" title=\"Flaticon\"\u003ewww.flaticon.com\u003c/a\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergio-sastre%2Fandroid-screenshot-testing-playground","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsergio-sastre%2Fandroid-screenshot-testing-playground","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsergio-sastre%2Fandroid-screenshot-testing-playground/lists"}