Anzeige
Anzeige

Más contenido relacionado

Similar a Seven Peaks Speaks - Compose Screenshot Testing Made Easy(20)

Anzeige

Seven Peaks Speaks - Compose Screenshot Testing Made Easy

  1. Compose Screenshot Testing Made Easy Ju Tipatai Puthanukunkit Junior Principal Engineer (Mobile) @ Muvmi
  2. What is screenshot testing?
  3. 1. Record
  4. 1. Record Save as login.png
  5. 1. Record Save as login.png 2. Verify Compare to verify
  6. ● Easy to validate all possible states of screen Pros
  7. ● Easy to validate all possible states of screen ● Simpler and faster than Compose UI Test, Espresso, Kakao or Appium Pros
  8. ● Easy to validate all possible states of screen ● Simpler and faster than Compose UI Test, Espresso, Kakao or Appium Pros Cons ● Can’t test navigation flow, E2E test
  9. How?
  10. Shot - https://github.com/pedrovgs/Shot - run on physical device or emulator, but slow, flaky.
  11. Shot - https://github.com/pedrovgs/Shot - run on physical device or emulator, but slow, flaky.
  12. Paparazzi - https://github.com/cashapp/paparazzi - No need to run on physical device or emulator, faster way, not flaky.
  13. Paparazzi - https://github.com/cashapp/paparazzi - No need to run on physical device or emulator, faster way, not flaky. - Recommend to keep image files in git LFS (read more in above url)
  14. Paparazzi should run on Android library module only app screenshot-test (paparazzi stay here) ui (composable should stay here)
  15. It’s work for multi feature modules too app screenshot-test (paparazzi stay here) login-ui product-ui
  16. class SampleTest { @get:Rule val paparazzi = Paparazzi(maxPercentDifference = 0.05) @Test fun launchComposable() { paparazzi.snapshot { MyComposable() } } }
  17. Let’s do paparazzi screenshot test
  18. LoginScreen Default Value ValueAndLoading Error
  19. ViewState ViewAction (onClick,onValueChanged ) State Container (state in Stateful Composable or viewModel) Stateless Composable Screen
  20. data class LoginViewState( val loading: Boolean = false, val phoneNumber: String = "", val phoneNumberError: String? = null, ) ViewState
  21. sealed interface LoginViewAction { data class SetPhoneNumber(val value: String) : LoginViewAction object Submit : LoginViewAction } ViewAction
  22. Stateless Composable Screen @Composable fun LoginScreen( state: LoginViewState, dispatch: (LoginViewAction) -> Unit, ) { // Implementation }
  23. Let’s @Preview
  24. Default @Preview @Composable fun Preview_LoginScreen() = AppTheme { LoginScreen( state = LoginViewState(), dispatch = {}, ) }
  25. With Value @Preview @Composable fun Preview_LoginScreen_WithValue() = AppTheme { LoginScreen( state = LoginViewState( phoneNumber = "+66812223333" ), dispatch = {}, ) }
  26. With Value and loading @Preview @Composable fun Preview_LoginScreen_WithValueAndLoading() = AppTheme { LoginScreen( state = LoginViewState( phoneNumber = "+66812223333", loading = true, ), dispatch = {}, ) }
  27. With Error @Preview @Composable fun Preview_LoginScreen_WithError() = AppTheme { LoginScreen( state = LoginViewState( phoneNumber = "", phoneNumberError = "Required Phone Number" ), dispatch = {}, ) }
  28. Put @Preview functions in paparazzi test
  29. class PaparazziTest { @get:Rule val paparazzi = Paparazzi( maxPercentDifference = 0.05, deviceConfig = DeviceConfig.PIXEL_2.copy(softButtons = false), ) @Test fun test_LoginScreen() { paparazzi.snapshot { Preview_LoginScreen() } } @Test fun test_LoginScreen_WithValue() { paparazzi.snapshot { Preview_LoginScreen_WithValue()} } @Test fun test_LoginScreen_WithValueAndLoading() { paparazzi.snapshot { Preview_LoginScreen_WithValueAndLoading()} } @Test fun test_LoginScreen_WithError() { paparazzi.snapshot { Preview_LoginScreen_WithError()} } }
  30. Record ./gradlew recordPaparazziDebug
  31. Verify ./gradlew verifyPaparazziDebug
  32. If Verify Error
  33. Expected Delta Actual delta- com.github.judrummer.screenshottest_PreviewScreenshotTests_test[com.github.judrummer.common_null_Defaul tGroup_Preview_LoginScreen_WithValue_0_null].png
  34. class PaparazziTest { @get:Rule val paparazzi = Paparazzi( maxPercentDifference = 0.05, deviceConfig = DeviceConfig.PIXEL_2.copy(softButtons = false), ) @Test fun test_LoginScreen() { paparazzi.snapshot { Preview_LoginScreen() } } @Test fun test_LoginScreen_WithValue() { paparazzi.snapshot { Preview_LoginScreen_WithValue()} } @Test fun test_LoginScreen_WithValueAndLoading() { paparazzi.snapshot { Preview_LoginScreen_WithValueAndLoading()} } @Test fun test_LoginScreen_WithError() { paparazzi.snapshot { Preview_LoginScreen_WithError()} } } Each screens will need to have this code
  35. If we can generate all @Preview to be test cases automatically?
  36. Showkase - https://github.com/airbnb/Showkase - Use Annotation processing to generate metadata (kapt or ksp)
  37. Showkase - https://github.com/airbnb/Showkase - Use Annotation processing to generate metadata (kapt or ksp) - Detect @Preview to generate metadata.componentList
  38. Showkase - https://github.com/airbnb/Showkase - Use Annotation processing to generate metadata (kapt or ksp) - Detect @Preview to generate metadata.componentList - Detect @ShowkaseTypography to generate metadata.typographyList
  39. Showkase - https://github.com/airbnb/Showkase - Use Annotation processing to generate metadata (kapt or ksp) - Detect @Preview to generate metadata.componentList - Detect @ShowkaseTypography to generate metadata.typographyList - Detect @ShowkaseColor to generate metadata.colorList
  40. Showkase - https://github.com/airbnb/Showkase - Use Annotation processing to generate metadata (kapt or ksp) - Detect @Preview to generate metadata.componentList - Detect @ShowkaseTypography to generate metadata.typographyList - Detect @ShowkaseColor to generate metadata.colorList - Use metadata as UI Components catelog
  41. Showkase - https://github.com/airbnb/Showkase - Use Annotation processing to generate metadata (kapt or ksp) - Detect @Preview to generate metadata.componentList - Detect @ShowkaseTypography to generate metadata.typographyList - Detect @ShowkaseColor to generate metadata.colorList - Use metadata as UI Components catelog - Use metadata as screenshot testing
  42. Showkase - Use metadata as UI Components catelog
  43. TestParameterInjectors - https://github.com/google/TestParameterInjector - JUnit test runner that runs its test methods for different combinations of field/parameter values.
  44. TestParameterInjectors - https://github.com/google/TestParameterInjector - JUnit test runner that runs its test methods for different combinations of field/parameter values. - Transform Showkase metadata to be unit test cases
  45. Apply Showkase and TestParameterInjectors
  46. Add Showkase annotation import com.airbnb.android.showkase.annotation.ShowkaseRoot import com.airbnb.android.showkase.annotation.ShowkaseRootModule @ShowkaseRoot class ScreenshotShowkaseModule : ShowkaseRootModule
  47. Define PreviewProvider object PreviewProvider : TestParameter.TestParameterValuesProvider { override fun provideValues(): List<TestPreview> { val metadata = Showkase.getMetadata() val components = metadata.componentList.map(::ComponentTestPreview) val colors = metadata.colorList.map(::ColorTestPreview) val typography = metadata.typographyList.map(::TypographyTestPreview) return components + colors + typography } }
  48. Apply to Paparazzi @RunWith(TestParameterInjector::class) class PreviewScreenshotTests { @get:Rule val paparazzi = Paparazzi() @Test fun test( @TestParameter(valuesProvider = PreviewProvider::class) componentPreview: TestPreview, ) { paparazzi.snapshot { componentPreview.Content() } } }
  49. Summary - Paparazzi - screenshot test without physical device or emulator.
  50. Summary - Paparazzi - screenshot test without physical device or emulator. - Showkase - generate showkase metadata(components, colors, typographies)
  51. Summary - Paparazzi - screenshot test without physical device or emulator. - Showkase - generate showkase metadata(components, colors, typographies) - TestParameterInjector - convert showkase metadata to be test case
  52. Example repository https://github.com/Judrummer/compose-screenshot-test-sample Useful links https://github.com/cashapp/paparazzi https://github.com/airbnb/Showkase https://github.com/google/TestParameterInjector https://developer.android.com/jetpack/compose/state-hoisting
  53. Thank you :)
Anzeige