Slide 1

Slide 1 text

EFFECTIVE SNAPSHOT TESTING ON ANDROID BY SERGIO SASTRE AN INTRODUCTION TO

Slide 2

Slide 2 text

WHAT Snapshot testing is

Slide 3

Slide 3 text

Special type of UI tests take a screenshot of it, compare it to its reference we inflate a view, where and

Slide 4

Slide 4 text

Snapshot tests Ui tests thousand handful Source: "Building Mobile Apps at scale - 39 Engineering Challenges"

Slide 5

Slide 5 text

Snapshot tests Ui tests thousand handful ≈ 1 600 ≈ 500 Source: "Building Mobile Apps at scale - 39 Engineering Challenges"

Slide 6

Slide 6 text

Snapshot tests Ui tests thousand handful ≈ 1 600 ≈ 500 ≈ 2 300 ≈ 20 Source: "Building Mobile Apps at scale - 39 Engineering Challenges"

Slide 7

Slide 7 text

Snapshot tests Ui tests thousand handful ≈ 1 600 ≈ 500 ≈ 2 300 ≈ 20 ≈ 30 000 ? Source: "Building Mobile Apps at scale - 39 Engineering Challenges"

Slide 8

Slide 8 text

Ui tests Snapshot tests Icons made by “Freepik" from "Flaticon" writing speed

Slide 9

Slide 9 text

Ui tests Snapshot tests Icons made by “Freepik" from "Flaticon" writing speed

Slide 10

Slide 10 text

Ui tests Snapshot tests 5- 10 sec Icons made by “Freepik" from "Flaticon" writing speed

Slide 11

Slide 11 text

Ui tests Snapshot tests 5- 10 sec Icons made by “Freepik" from "Flaticon" writing speed

Slide 12

Slide 12 text

Ui tests Snapshot tests 5- 10 sec 1 sec < Icons made by “Freepik" from "Flaticon" writing speed

Slide 13

Slide 13 text

VS Snapshot 
 tests User interface tests

Slide 14

Slide 14 text

Snapshot 
 tests User interface tests VS

Slide 15

Slide 15 text

Snapshot 
 tests User interface tests +

Slide 16

Slide 16 text

Snapshot 
 tests User interface tests + (interaction) WHAT

Slide 17

Slide 17 text

Snapshot 
 tests User interface tests + (interaction) WHAT HOW (visuals)

Slide 18

Slide 18 text

MOTIVATION Snapshot test to

Slide 19

Slide 19 text

Unexpected changes from library updates Spacing, styling, themes… Layout correctness Long text View overlapping Languages (RTL) System font size (HUGE) 1 2 3 visual bugs

Slide 20

Slide 20 text

Unexpected changes from library updates Spacing, styling, themes… Layout correctness Long text View overlapping Languages (RTL) System font size (HUGE) 1 2 3 visual bugs

Slide 21

Slide 21 text

Unexpected changes from library updates Spacing, styling, themes… Layout correctness Long text View overlapping Languages (RTL) System font size (HUGE) 1 2 3 visual bugs

Slide 22

Slide 22 text

Unexpected changes from library updates Spacing, styling, themes… Layout correctness Long text View overlapping Languages (RTL) System font size (HUGE) 1 2 3 visual bugs

Slide 23

Slide 23 text

Unexpected changes from library updates Spacing, styling, themes… Layout correctness Long text View overlapping Languages (RTL) System font size (HUGE) 1 2 3 visual bugs

Slide 24

Slide 24 text

Unexpected changes from library updates Spacing, styling, themes… Layout correctness Long text View overlapping Languages (RTL) System font size (HUGE) 1 2 3 visual bugs which will expand till the end of the line and …

Slide 25

Slide 25 text

Unexpected changes from library updates Spacing, styling, themes… Layout correctness Long text View overlapping Languages (RTL) System font size (HUGE) 1 2 3 visual bugs

Slide 26

Slide 26 text

Unexpected changes from library updates Spacing, styling, themes… Layout correctness Long text View overlapping Languages (RTL) System font size (HUGE) 1 2 3 visual bugs

Slide 27

Slide 27 text

Unexpected changes from library updates Spacing, styling, themes… Layout correctness Long text View overlapping Languages (RTL) System font size (HUGE) 1 2 3 visual bugs

Slide 28

Slide 28 text

Unexpected changes from library updates Spacing, styling, themes… Layout correctness Long text View overlapping Languages (RTL) System font size (HUGE) 1 2 3 visual bugs

Slide 29

Slide 29 text

Example

Slide 30

Slide 30 text

Layout validation

Slide 31

Slide 31 text

Layout validation

Slide 32

Slide 32 text

Settings Display Font size System font size > >

Slide 33

Slide 33 text

Example

Slide 34

Slide 34 text

Example

Slide 35

Slide 35 text

Font Size Largest 6,16% Small 8,06% Large 14,22% Default 71,56% TT Source: Android app with 80 000 monthly active users

Slide 36

Slide 36 text

Font Size Small 8,06% Default 71,56% TT 20, 38 % Large + Largest Source: Android app with 80 000 monthly active users

Slide 37

Slide 37 text

Do not trust Layout validation BUT Manual validation is hard

Slide 38

Slide 38 text

1 Snapshot tests Use 2 Automate hard configs Catch regression bugs

Slide 39

Slide 39 text

WORKS HOW Snapshot testing

Slide 40

Slide 40 text

record AND verify

Slide 41

Slide 41 text

write test CI

Slide 42

Slide 42 text

record CI

Slide 43

Slide 43 text

= CI

Slide 44

Slide 44 text

CI new PR +

Slide 45

Slide 45 text

approve CI

Slide 46

Slide 46 text

merge CI

Slide 47

Slide 47 text

CI new PR verify

Slide 48

Slide 48 text

CI new PR

Slide 49

Slide 49 text

CI new PR

Slide 50

Slide 50 text

CI new PR

Slide 51

Slide 51 text

CI new PR verify

Slide 52

Slide 52 text

CI new PR

Slide 53

Slide 53 text

CI new PR

Slide 54

Slide 54 text

CI new PR

Slide 55

Slide 55 text

CI Intentional change forgot to record regression bug YES NO

Slide 56

Slide 56 text

CI Emulator config matters D1 DN . . .

Slide 57

Slide 57 text

CI Emulator config matters = D1 DN = . . . = OR

Slide 58

Slide 58 text

CI Emulator config matters D1 = . . . DN = = OR

Slide 59

Slide 59 text

CI Emulator config matters D1 DN . . . = =

Slide 60

Slide 60 text

CI Emulator config matters = D1 = = . . . DN OR

Slide 61

Slide 61 text

CI Emulator config matters = D1 = = . . . DN OR

Slide 62

Slide 62 text

1 2 More complex than standard testing Same emulator config everywhere process Snapshot testing

Slide 63

Slide 63 text

How to Snapshot test EFFECTIVELY

Slide 64

Slide 64 text

Example

Slide 65

Slide 65 text

Example

Slide 66

Slide 66 text

Dealing with asynchronicity Choosing test-worthy screen states More views, more prone to flakiness 1 2 3 snapshot testing full screen

Slide 67

Slide 67 text

Dealing with asynchronicity Choosing test-worthy screen states More views, more prone to flakiness 1 2 3 snapshot testing full screen

Slide 68

Slide 68 text

Dealing with asynchronicity Choosing test-worthy screen states More views, more prone to flakiness 1 2 3 snapshot testing full screen

Slide 69

Slide 69 text

Dealing with asynchronicity Choosing test-worthy screen states More views, more prone to flakiness 1 2 3 snapshot testing full screen

Slide 70

Slide 70 text

Dealing with asynchronicity Choosing test-worthy screen states More views, more prone to flakiness 1 2 3 snapshot testing full screen

Slide 71

Slide 71 text

start small & simple Header Training View Text view to memorise 1 split into smaller testable views

Slide 72

Slide 72 text

Header Training View Text view to memorise start small & simple 1 what to test

Slide 73

Slide 73 text

Header Training View Text view to memorise start small & simple 1 what to test

Slide 74

Slide 74 text

A B start small & simple 1 Identifying view states

Slide 75

Slide 75 text

A B start small & simple 1 Identifying view states

Slide 76

Slide 76 text

aim to break the layout 2 Identifying unhappy paths

Slide 77

Slide 77 text

aim to break the layout 2 Identifying unhappy paths thousand of words to train

Slide 78

Slide 78 text

aim to break the layout 2 Identifying unhappy paths thousand of words to train words to train for all 7 languages

Slide 79

Slide 79 text

aim to break the layout 2 Identifying unhappy paths thousand of words to train words to train for all 7 languages huge system font size

Slide 80

Slide 80 text

aim to break the layout 2 Identifying unhappy paths thousand of words to train words to train for all 7 languages huge system font size narrow screen

Slide 81

Slide 81 text

aim to break the layout 2 Identifying unhappy paths thousand of words to train words to train for all 7 languages huge system font size narrow screen

Slide 82

Slide 82 text

aim to break the layout 2 Identifying unhappy paths thousand of words to train words to train for all 7 languages huge system font size narrow screen trainingInfo =
 TrainingItem ( trainingByLang = wordsToTrainPerLang(999_999) , activeLangs = Language.values().toSet( ) ), TrainingTestItem( fontSize = FontScale.HUGE, viewWidth = ViewWidth.NARROW fun withWordsToTrainUnhappyPath() = )
 ) Mock Data

Slide 83

Slide 83 text

aim to break the layout 2 Identifying unhappy paths thousand of words to train words to train for all 7 languages huge system font size narrow screen trainingInfo =
 TrainingItem ( trainingByLang = wordsToTrainPerLang(999_999) , activeLangs = Language.values().toSet( ) ), TrainingTestItem( fontSize = FontScale.HUGE, viewWidth = ViewWidth.NARROW fun withWordsToTrainUnhappyPath() = )
 )

Slide 84

Slide 84 text

aim to break the layout 2 Identifying unhappy paths thousand of words to train words to train for all 7 languages huge system font size training 
 item narrow screen trainingInfo =
 TrainingItem ( trainingByLang = wordsToTrainPerLang(999_999) , activeLangs = Language.values().toSet( ) ), TrainingTestItem( fontSize = FontScale.HUGE, viewWidth = ViewWidth.NARROW ) fun withWordsToTrainUnhappyPath() = )
 )

Slide 85

Slide 85 text

aim to break the layout 2 Identifying unhappy paths thousand of words to train words to train for all 7 languages huge system font size narrow screen trainingInfo =
 TrainingItem ( trainingByLang = wordsToTrainPerLang(999_999) , activeLangs = Language.values().toSet( ) ), TrainingTestItem( fontSize = FontScale.HUGE, viewWidth = ViewWidth.NARROW )
 ) external fun withWordsToTrainUnhappyPath() =

Slide 86

Slide 86 text

aim to break the layout 2 writing the snapshot test with pedrovgs/Shot private fun ScreenshotTest.snapViewHolder(testItem: TrainingTestItem, snapshotName: String){ val activity = FontsizeActivityScenario.launchWith(testItem.fontScale ) .waitForActivity(), val view = waitForView {
 val layout = activity.inflate(R.layout.training_row)
 TrainingViewHolder(layout).apply { bind ( trainingItem = testItem.trainingItem,
 languageClickedListener = null
 ) } } compareScreenshot {
 holder = view,
 widthInPx = testItem.viewWidth.widthInPx , name = snapshotName
 } } Test itself

Slide 87

Slide 87 text

aim to break the layout 2 writing the snapshot test with pedrovgs/Shot private fun ScreenshotTest.snapViewHolder(testItem: TrainingTestItem, snapshotName: String){ val activity = FontsizeActivityScenario.launchWith(testItem.fontScale ) .waitForActivity(), val view = waitForView {
 val layout = activity.inflate(R.layout.training_row)
 TrainingViewHolder(layout).apply { bind ( trainingItem = testItem.trainingItem,
 languageClickedListener = null
 ) } } compareScreenshot {
 holder = view,
 widthInPx = testItem.viewWidth.widthInPx , name = snapshotName
 } }

Slide 88

Slide 88 text

aim to break the layout 2 writing the snapshot test with pedrovgs/Shot private fun ScreenshotTest.snapViewHolder(testItem: TrainingTestItem, snapshotName: String){ val activity = FontsizeActivityScenario.launchWith(testItem.fontScale ) .waitForActivity(), val view = waitForView {
 val layout = activity.inflate(R.layout.training_row)
 TrainingViewHolder(layout).apply { bind ( trainingItem = testItem.trainingItem,
 languageClickedListener = null
 ) } } compareScreenshot {
 holder = view,
 widthInPx = testItem.viewWidth.widthInPx , name = snapshotName
 } } Wait for 
 Ui thread 
 idle + launch 
 ActivityScenario

Slide 89

Slide 89 text

aim to break the layout 2 writing the snapshot test with pedrovgs/Shot private fun ScreenshotTest.snapViewHolder(testItem: TrainingTestItem, snapshotName: String){ val activity = FontsizeActivityScenario.launchWith(testItem.fontScale ) .waitForActivity(), val view = waitForView {
 val layout = activity.inflate(R.layout.training_row)
 TrainingViewHolder(layout).apply { bind ( trainingItem = testItem.trainingItem,
 languageClickedListener = null
 ) } } compareScreenshot {
 holder = view,
 widthInPx = testItem.viewWidth.widthInPx , name = snapshotName
 } } Inflate view 
 & bind data Wait for 
 Ui thread 
 idle +

Slide 90

Slide 90 text

aim to break the layout 2 writing the snapshot test with pedrovgs/Shot private fun ScreenshotTest.snapViewHolder(testItem: TrainingTestItem, snapshotName: String){ val activity = FontsizeActivityScenario.launchWith(testItem.fontScale ) .waitForActivity(), val view = waitForView {
 val layout = activity.inflate(R.layout.training_row)
 TrainingViewHolder(layout).apply { bind ( trainingItem = testItem.trainingItem,
 languageClickedListener = null
 ) } } compareScreenshot {
 holder = view,
 widthInPx = testItem.viewWidth.widthInPx , name = snapshotName
 } } record screenshot

Slide 91

Slide 91 text

aim to break the layout 2 writing the snapshot test with pedrovgs/Shot private fun ScreenshotTest.snapViewHolder(testItem: TrainingTestItem, snapshotName: String){ val activity = FontsizeActivityScenario.launchWith(testItem.fontScale ) .waitForActivity(), val view = waitForView {
 val layout = activity.inflate(R.layout.training_row)
 TrainingViewHolder(layout).apply { bind ( trainingItem = testItem.trainingItem,
 languageClickedListener = null
 ) } } compareScreenshot {
 holder = view,
 widthInPx = testItem.viewWidth.widthInPx , name = snapshotName
 } }

Slide 92

Slide 92 text

aim to break the layout 2 writing the snapshot test with pedrovgs/Shot @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(private val testItem: TrainingTestItem): ScreenshotTest{ companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath()
 .. . )
 } @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } Parameterized 
 tests

Slide 93

Slide 93 text

aim to break the layout 2 writing the snapshot test with pedrovgs/Shot @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(private val testItem: TrainingTestItem): ScreenshotTest{ companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath()
 .. . )
 } @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) }

Slide 94

Slide 94 text

aim to break the layout 2 writing the snapshot test with pedrovgs/Shot @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(private val testItem: TrainingTestItem): ScreenshotTest{ companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath()
 .. . )
 } @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } set 
 Runner

Slide 95

Slide 95 text

aim to break the layout 2 writing the snapshot test with pedrovgs/Shot @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(private val testItem: TrainingTestItem): ScreenshotTest{ companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath()
 .. . )
 } @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } generate 
 test data

Slide 96

Slide 96 text

aim to break the layout 2 writing the snapshot test with pedrovgs/Shot @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(private val testItem: TrainingTestItem): ScreenshotTest{ companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath()
 .. . )
 } @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } test with 
 parameter

Slide 97

Slide 97 text

aim to break the layout 2 writing the snapshot test with pedrovgs/Shot @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(private val testItem: TrainingTestItem): ScreenshotTest{ companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath()
 .. . )
 } @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) }

Slide 98

Slide 98 text

aim to break the layout 2 thousand of words to train words to train for all 7 languages huge system font size narrow screen recording unhappy path

Slide 99

Slide 99 text

aim to break the layout 2 @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(private val testItem: TrainingTestItem): ScreenshotTest{ companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath()
 .. . )
 } @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } recording happy and unhappy paths

Slide 100

Slide 100 text

aim to break the layout 2 @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(private val testItem: TrainingTestItem): ScreenshotTest{ companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath(), withWordsToTrainHappyPath()
 withoutWordsToTrainUnhappyPath(), withoutWordsToTrainHappyPath( ) )
 } @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } recording happy and unhappy paths

Slide 101

Slide 101 text

aim to break the layout 2 recording happy and unhappy paths without WordsToTrain HappyPath() with WordsToTrain HappyPath() with WordsToTrain UnhappyPath() without WordsToTrain UnhappyPath()

Slide 102

Slide 102 text

group tests by relevance 3

Slide 103

Slide 103 text

2’ 100 happy + unhappy group tests by relevance 3 100%

Slide 104

Slide 104 text

2’ 4’ 200 100 happy + unhappy group tests by relevance 3 100%

Slide 105

Slide 105 text

2’ 4’ 8’ 400 200 100 happy + unhappy group tests by relevance 3 100%

Slide 106

Slide 106 text

2’ 4’ 8’ 16’ 800 400 200 100 happy + unhappy group tests by relevance 3 100%

Slide 107

Slide 107 text

2’ 4’ 8’ 16’ … 800 400 200 100 happy + unhappy 100% group tests by relevance 3

Slide 108

Slide 108 text

2’ 4’ 8’ 16’ 800 400 200 100 group tests by relevance 3 unhappy happy 90 - 100% 5 - 20% …

Slide 109

Slide 109 text

2’ … 1’ 3’ 40 80 160 320 6’ unhappy happy 90 - 100% 5 - 20% group tests by relevance 3

Slide 110

Slide 110 text

daily builds night build

Slide 111

Slide 111 text

night build run happy path tests ONLY catch most important bugs run on every PR daily builds

Slide 112

Slide 112 text

night build run happy path tests ONLY catch most important bugs run on every PR run unhappy path tests ONLY catch less important bugs run once a day daily builds

Slide 113

Slide 113 text

night build run happy path tests ONLY catch most important bugs run on every PR run unhappy path tests ONLY catch less important bugs run once a day daily builds fast development cycles

Slide 114

Slide 114 text

3 unhappy vs. happy path @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(
 private val testItem: TrainingTestItem
 ): ScreenshotTest{ @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } group tests by relevance companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath(), withWordsToTrainHappyPath()
 withoutWordsToTrainUnhappyPath(), withoutWordsToTrainHappyPath( ) )
 } Filtered parameterized 
 tests

Slide 115

Slide 115 text

3 unhappy vs. happy path @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(
 private val testItem: TrainingTestItem
 ): ScreenshotTest{ @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } group tests by relevance companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath(), withWordsToTrainHappyPath()
 withoutWordsToTrainUnhappyPath(), withoutWordsToTrainHappyPath( ) )
 }

Slide 116

Slide 116 text

3 @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(
 private val testItem: TrainingTestItem
 ): ScreenshotTest{ @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } group tests by relevance companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath(), withWordsToTrainHappyPath()
 withoutWordsToTrainUnhappyPath(), withoutWordsToTrainHappyPath( ) )
 } unhappy vs. happy path

Slide 117

Slide 117 text

3 @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(
 private val testItem: TrainingTestItem
 ): ScreenshotTest{ @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } group tests by relevance companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath(),
 withoutWordsToTrainUnhappyPath( ) )
 } companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainHappyPath()
 withoutWordsToTrainHappyPath( ) )
 } @RunWith(Parameterized::class)
 class TrainingItemHappyPath(
 private val testItem: TrainingTestItem
 ): ScreenshotTest{ @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } unhappy vs. happy path

Slide 118

Slide 118 text

3 @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(
 private val testItem: TrainingTestItem
 ): ScreenshotTest{ @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } group tests by relevance companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath(),
 withoutWordsToTrainUnhappyPath( ) )
 } companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainHappyPath()
 withoutWordsToTrainHappyPath( ) )
 } @RunWith(Parameterized::class)
 class TrainingItemHappyPath(
 private val testItem: TrainingTestItem
 ): ScreenshotTest{ @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } unhappy vs. happy path

Slide 119

Slide 119 text

3 @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(
 private val testItem: TrainingTestItem
 ): ScreenshotTest{ @UnhappyPat h @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } group tests by relevance companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath(),
 withoutWordsToTrainUnhappyPath( ) )
 } companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainHappyPath()
 withoutWordsToTrainHappyPath( ) )
 } @RunWith(Parameterized::class)
 class TrainingItemHappyPath(
 private val testItem: TrainingTestItem
 ): ScreenshotTest{ @HappyPat h @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } unhappy vs. happy path

Slide 120

Slide 120 text

3 @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(
 private val testItem: TrainingTestItem
 ): ScreenshotTest{ @UnhappyPat h @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } group tests by relevance companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath(),
 withoutWordsToTrainUnhappyPath( ) )
 } companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainHappyPath()
 withoutWordsToTrainHappyPath( ) )
 } @RunWith(Parameterized::class)
 class TrainingItemHappyPath(
 private val testItem: TrainingTestItem
 ): ScreenshotTest{ @HappyPat h @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } = path.to.annotation.UnhappyPath -Pandroid.testIntrumentationRunnerArguments.annotation ./gradlew -Precord unhappy vs. happy path

Slide 121

Slide 121 text

3 @RunWith(Parameterized::class)
 class TrainingItemUnhappyPath(
 private val testItem: TrainingTestItem
 ): ScreenshotTest{ @UnhappyPat h @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } group tests by relevance companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainUnhappyPath(),
 withoutWordsToTrainUnhappyPath( ) )
 } companion object {
 @JvmStatic
 @Parameterized.Parameter s fun testData(): Array = arrayOf ( withWordsToTrainHappyPath()
 withoutWordsToTrainHappyPath( ) )
 } @RunWith(Parameterized::class)
 class TrainingItemHappyPath(
 private val testItem: TrainingTestItem
 ): ScreenshotTest{ @HappyPat h @Test
 fun snapTrainingItem(){ snapViewHolder(testItem)
 ) } = path.to.annotation.HappyPath -Pandroid.testIntrumentationRunnerArguments.annotation ./gradlew -Precord unhappy vs. happy path

Slide 122

Slide 122 text

1 Snapshot testing effectively 2 Start small & simple Aim to break the layout 3 Group tests by relevance

Slide 123

Slide 123 text

attention for your THANKS

Slide 124

Slide 124 text

code blogs @SergioSastre @sergio-sastre @sergio-sastre @Gio_Sastre Sergio Sastre Flórez Lead & Senior Android developer appdev.de