Share
Embed
Transcript
73 slides
-
Snapshot Test UI Test ~ 1000 ~ 1600 ~ 2300
~30,000 handful ~ 500 ~ 20 ? Ref: “Building Mobile App At scale: 39 Engineering Challenge” -
When Card Render, 1. Text should show 2. Icon that
looks like location pin with plus icon should show 3. Icon should be on left and Text on right 4. Content have padding of 64dp 5. It should have a background color of #XXXXXX and content tint color of #XXXXXX -
When Card Render, Text should show fun shouldShowText() { composeTestRule.setContent
{ ClickableCard() } composeTestRule .onNodeWithText("Click Me!") .assertIsDisplayed() } -
When Card Render, 1. Text should show 2. Icon that
looks like location pin with plus icon should show 3. Icon should be on left and Text on right 4. Content have padding of 64dp 5. It should have a background color of #XXXXXX and content tint color of #XXXXXX -
When Card Render, 1. Text should show 2. Icon that
looks like location pin with plus icon should show 3. Icon should be on left and Text on right 4. Content have padding of 64dp 5. It should have a background color of #XXXXXX and content tint color of #XXXXXX -
When Card Render, 1. Text should show 2. Icon that
looks like location pin with plus icon should show 3. Icon should be on left and Text on right 4. Content have padding of 64dp 5. It should have a background color of #XXXXXX and content tint color of #XXXXXX -
When Card Render, 1. Text should show 2. Icon that
looks like location pin with plus icon should show 3. Icon should be on left and Text on right 4. Content have padding of 64dp 5. It should have a background color of #XXXXXX and content tint color of #XXXXXX -
Photo by Erica Magugliani on Unsplash UI Test • Cannot
test visual aspect of a component • Slow to execute • Require complex setup -
Snapshot Test UI Test Interaction bugs (Scroll, clicks, etc) Visual
bugs (Styling, spacing, theme etc) -
Visual Bugs 1. Changes from libraries 2. Styling, Padding, Margins,
Colors, Theme etc 3. Layout edge cases a. Long texts b. Constraints c. Internationalization (RTL Language, fonts) d. Accessibility (Bigger Font Size, Color Contrast) -
Visual Bugs 1. Changes from libraries 2. Styling, Padding, Margins,
Colors, Theme etc 3. Layout edge cases a. Long texts b. Constraints c. Internationalization (RTL Language, fonts) d. Accessibility (Bigger Font Size, Color Contrast) -
Visual Bugs 1. Changes from libraries 2. Styling, Padding, Margins,
Colors, Theme etc 3. Layout edge cases a. Long texts b. Constraints c. Internationalization (RTL Language, fonts) d. Accessibility (Bigger Font Size, Color Contrast) -
Visual Bugs 1. Changes from libraries 2. Styling, Padding, Margins,
Colors, Theme etc 3. Layout edge cases a. Long texts b. Constraints c. Internationalization (RTL Language, fonts) d. Accessibility (Bigger Font Size, Color Contrast) -
Visual Bugs 1. Changes from libraries 2. Styling, Padding, Margins,
Colors, Theme etc 3. Layout edge cases a. Long texts b. Constraints c. Internationalization (RTL Language, fonts) d. Accessibility (Bigger Font Size, Color Contrast) -
Visual Bugs 1. Changes from libraries 2. Styling, Padding, Margins,
Colors, Theme etc 3. Layout edge cases a. Long texts b. Constraints c. Internationalization (RTL Language, fonts) d. Accessibility (Bigger Font Size, Color Contrast) -
Tools are great but manual validation is tedious and hard
and they don’t catch regressions -
Snapshot Testing Validate app’s appearance by using a screenshot or
a snapshot as a reference to compare against. -
Take a snapshot of the component and store it as
a reference for verification Record Verify Compare the last snapshot against the current changes to see if it matches -
Record Process Dev make changes and record a new snapshot
LGTM! PR submitted -
Record Process Dev make changes and record a new snapshot
LGTM! PR submitted Store snapshot for verification for future changes -
What go with? 1. Screen level snapshot testing 2. Component
level snapshot testing? -
Screen level snapshot testing 1. A lot of dependencies 2.
Too many states -
Screen level snapshot testing 1. A lot of dependencies 2.
Too many states 3. More components = More flakiness -
Screen level snapshot testing 1. A lot of dependencies 2.
Too many states 3. More components = More flakiness 4. Hard to trace which changes breaks -
App Bar are likely not to change, not priority to
test Using Library component, not priority to test -
• Main feature of the app • Has different states
Important text -
App Bar are likely not to change, not priority to
test • Main feature of the app • Has different states Important text Using Library component, not priority to test -
1. Most used features 2. Components that are being iterated
quickly 3. Important text bodies and messages -
Tooling - pedrovgs/Shot - shopify/android-testify* - dropbox/dropshots - takahirom/roborazzi -
QuickBirdEng/kotlin-snapshot-testing - facebook/screenshot-tests-for-android -
Roborazzi A snapshot testing framework built upon square’s paparazzi to
make it work with Robolectric -
@Composable fun ProjectCard() { val project = loadData() Column {
ProjectIcon(project) Row { Text(project.name) ... } } } -
@Composable fun ProjectCard() { val project = loadData() Column {
ProjectIcon(project) Row { Text(project.name) ... } } } -
Use a stateless composable @Composable fun ProjectCard() { val project
= loadData() ProjectCardContent(project) } @Composable fun ProjectCardContent( val project: Project ) { // Render here } Snapshot test UI Test -
@RunWith(AndroidJUnit4::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class ProjectCardScreenshotTest { @get:Rule val composeTestRule = createComposeRule()
@Test fun screenshotTest() { composeTestRule.setContent { ProjectCard(...) } composeTestRule.onRoot().captureRoboImage() } } -
@RunWith(AndroidJUnit4::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class ProjectCardScreenshotTest { @get:Rule val composeTestRule = createComposeRule()
@Test fun screenshotTest() { composeTestRule.setContent { ProjectCard(...) } composeTestRule.onRoot().captureRoboImage() } } -
@RunWith(AndroidJUnit4::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class ProjectCardScreenshotTest { @get:Rule val composeTestRule = createComposeRule()
@Test fun screenshotTest() { composeTestRule.setContent { ProjectCard(...) } composeTestRule.onRoot().captureRoboImage() } } -
@RunWith(AndroidJUnit4::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class ProjectCardScreenshotTest { @get:Rule val composeTestRule = createComposeRule()
@Test fun screenshotTest() { composeTestRule.setContent { ProjectCard(...) } composeTestRule.onRoot().captureRoboImage() } } -
@RunWith(AndroidJUnit4::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class ProjectCardScreenshotTest { @get:Rule val composeTestRule = createComposeRule()
@Test fun screenshotTest() { composeTestRule.setContent { ProjectCard(...) } composeTestRule.onRoot().captureRoboImage() } } -
@RunWith(AndroidJUnit4::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class ProjectCardScreenshotTest { @get:Rule val composeTestRule = createComposeRule()
@Test fun screenshotTest() { composeTestRule.setContent { ProjectCard(...) } composeTestRule.onRoot().captureRoboImage() } } -
@RunWith(ParameterizedRobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class ProjectCardScreenshotTest( private val config: DeviceConfig ) {
companion object { @JvmStatic @Parameters fun testParamsProvider() = listOf( DeviceConfig("Small_Phone_Font_Small", RobolectricDeviceQualifiers.SmallPhone, 0.5f), DeviceConfig("Small_Phone_Font_Normal", RobolectricDeviceQualifiers.SmallPhone, 1.0f), DeviceConfig("Medium_Tablet_Font_Large", RobolectricDeviceQualifiers.MediumTablet, 2.0f), ) } @Test fun screenshotTest() { RuntimeEnvironment.setFontScale(config.fontScale) RuntimeEnvironment.setQualifiers(config.qualifier) ... } } Use Parameterized Runner for testing multiple device configuration -
@RunWith(ParameterizedRobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class ProjectCardScreenshotTest( private val config: DeviceConfig ) {
companion object { @JvmStatic @Parameters fun testParamsProvider() = listOf( DeviceConfig("Small_Phone_Font_Small", RobolectricDeviceQualifiers.SmallPhone, 0.5f), DeviceConfig("Small_Phone_Font_Normal", RobolectricDeviceQualifiers.SmallPhone, 1.0f), DeviceConfig("Medium_Tablet_Font_Large", RobolectricDeviceQualifiers.MediumTablet, 2.0f), ) } @Test fun screenshotTest() { RuntimeEnvironment.setFontScale(config.fontScale) RuntimeEnvironment.setQualifiers(config.qualifier) ... } } Use Parameterized Runner for testing multiple device configuration -
@RunWith(ParameterizedRobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class ProjectCardScreenshotTest( private val config: DeviceConfig ) {
companion object { @JvmStatic @Parameters fun testParamsProvider() = listOf( DeviceConfig("Small_Phone_Font_Small", RobolectricDeviceQualifiers.SmallPhone, 0.5f), DeviceConfig("Small_Phone_Font_Normal", RobolectricDeviceQualifiers.SmallPhone, 1.0f), DeviceConfig("Medium_Tablet_Font_Large", RobolectricDeviceQualifiers.MediumTablet, 2.0f), ) } @Test fun screenshotTest() { RuntimeEnvironment.setFontScale(config.fontScale) RuntimeEnvironment.setQualifiers(config.qualifier) ... } } Use Parameterized Runner for testing multiple device configuration -
@RunWith(ParameterizedRobolectricTestRunner::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) class ProjectCardScreenshotTest( private val config: DeviceConfig ) {
companion object { @JvmStatic @Parameters fun testParamsProvider() = listOf( DeviceConfig("Small_Phone_Font_Small", RobolectricDeviceQualifiers.SmallPhone, 0.5f), DeviceConfig("Small_Phone_Font_Normal", RobolectricDeviceQualifiers.SmallPhone, 1.0f), DeviceConfig("Medium_Tablet_Font_Large", RobolectricDeviceQualifiers.MediumTablet, 2.0f), ) } @Test fun screenshotTest() { RuntimeEnvironment.setFontScale(config.fontScale) RuntimeEnvironment.setQualifiers(config.qualifier) ... } } Use Parameterized Runner for testing multiple device configuration -
Small Phone with Font Scale 0.5 Small Phone with Font
Scale 1.0 Medium Tablet with Font Scale 2.0 -
Road to Snapshot Testing 1. Analyze which are to test,
start with small & critical components 2. Scale up to different configurations 3. Integrate into your development workflow 4. Happy Snapshot Testing! -
Good read - Introduction to snapshot testing blog series -
Android screenshot testing playground - AndroidUITesting Utils - NowinAndroid bit.ly/droid-snap-read