Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Our app...

Slide 3

Slide 3 text

Types of tes*ng we can do on Flu4er

Slide 4

Slide 4 text

Types of tes*ng we can do on Flu4er • Unit tests for pure Dart only classes

Slide 5

Slide 5 text

Types of tes*ng we can do on Flu4er • Unit tests for pure Dart only classes • Widget tests for isolated widgets

Slide 6

Slide 6 text

Types of tes*ng we can do on Flu4er • Unit tests for pure Dart only classes • Widget tests for isolated widgets • Integra.on tests for the applica6on as a whole

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

There's no tests!!!

Slide 12

Slide 12 text

Let's start wri+ng some unit tests...

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

Let's recap for a second

Slide 19

Slide 19 text

Let's recap for a second • Tests start with main

Slide 20

Slide 20 text

Let's recap for a second • Tests start with main • test keyword for each individual unit test

Slide 21

Slide 21 text

Let's recap for a second • Tests start with main • test keyword for each individual unit test • flutter_test as part of the SDK

Slide 22

Slide 22 text

Let's recap for a second • Tests start with main • test keyword for each individual unit test • flutter_test as part of the SDK • Use expect to run an asser;on

Slide 23

Slide 23 text

Let's recap for a second • Tests start with main • test keyword for each individual unit test • flutter_test as part of the SDK • Use expect to run an asser;on • flutter test on CLI to run tests

Slide 24

Slide 24 text

How do we test throwing excep1ons? !

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

Can we refactor our tests a li0le bit?

Slide 27

Slide 27 text

Yes!

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

The test framework offers...

Slide 30

Slide 30 text

The test framework offers... • setUpAll runs ONLY ONCE BEFORE any test is executed

Slide 31

Slide 31 text

The test framework offers... • setUpAll runs ONLY ONCE BEFORE any test is executed • setUp runs ONCE BEFORE every test

Slide 32

Slide 32 text

The test framework offers... • setUpAll runs ONLY ONCE BEFORE any test is executed • setUp runs ONCE BEFORE every test • tearDown runs ONCE AFTER every test

Slide 33

Slide 33 text

The test framework offers... • setUpAll runs ONLY ONCE BEFORE any test is executed • setUp runs ONCE BEFORE every test • tearDown runs ONCE AFTER every test • tearDownAll runs ONLY ONCE AFTER all test have been executed

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

Let's con*nue tes*ng the public API of the GuessGame class

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

Did you no)ce the amazing thing we just did?

Slide 39

Slide 39 text

Let's look again!

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

We just tested asynchronous code!

Slide 42

Slide 42 text

async and await are your new test best friends!

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

Can we refactor our tests a li0le bit?

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

We can organize our test file a bit be2er by using group()

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

We have completed the unit tests for the GuessGame class

Slide 49

Slide 49 text

Let's recap...

Slide 50

Slide 50 text

Let's recap... • Create a unit tests using the test method

Slide 51

Slide 51 text

Let's recap... • Create a unit tests using the test method • Use expect to make asser3ons

Slide 52

Slide 52 text

Let's recap... • Create a unit tests using the test method • Use expect to make asser3ons • Test lifecyle: setUpAll, setUp, tearDown and tearDownAl

Slide 53

Slide 53 text

Let's recap... • Create a unit tests using the test method • Use expect to make asser3ons • Test lifecyle: setUpAll, setUp, tearDown and tearDownAl • Async tests just by using async and await

Slide 54

Slide 54 text

Let's recap... • Create a unit tests using the test method • Use expect to make asser3ons • Test lifecyle: setUpAll, setUp, tearDown and tearDownAl • Async tests just by using async and await • Organize your test file using group

Slide 55

Slide 55 text

Let's add widget tests for our Flu2er applica5on

Slide 56

Slide 56 text

What are our widgets? What's their mission?

Slide 57

Slide 57 text

AndroidSummitApp2020App

Slide 58

Slide 58 text

AndroidSummitApp2020App • Takes a GuessGame as a dependency in the constructor, but it does nothing with it

Slide 59

Slide 59 text

AndroidSummitApp2020App • Takes a GuessGame as a dependency in the constructor, but it does nothing with it • No need to mock it

Slide 60

Slide 60 text

AndroidSummitApp2020App • Takes a GuessGame as a dependency in the constructor, but it does nothing with it • No need to mock it • Creates a MaterialApp

Slide 61

Slide 61 text

AndroidSummitApp2020App • Takes a GuessGame as a dependency in the constructor, but it does nothing with it • No need to mock it • Creates a MaterialApp • We can find this widget

Slide 62

Slide 62 text

AndroidSummitApp2020App • Takes a GuessGame as a dependency in the constructor, but it does nothing with it • No need to mock it • Creates a MaterialApp • We can find this widget • Renders the Questionnaire widget immediately

Slide 63

Slide 63 text

AndroidSummitApp2020App • Takes a GuessGame as a dependency in the constructor, but it does nothing with it • No need to mock it • Creates a MaterialApp • We can find this widget • Renders the Questionnaire widget immediately • We can find this widget

Slide 64

Slide 64 text

No content

Slide 65

Slide 65 text

Ques%onnaire

Slide 66

Slide 66 text

Ques%onnaire • Takes a GuessGame as a dependency in the constructor that controls the state

Slide 67

Slide 67 text

Ques%onnaire • Takes a GuessGame as a dependency in the constructor that controls the state • We can mock it to control behavior

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

Where do we start?

Slide 70

Slide 70 text

No content

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

In widget tests...

Slide 73

Slide 73 text

In widget tests... • We use testWidgets instead of test

Slide 74

Slide 74 text

In widget tests... • We use testWidgets instead of test • It provides a WidgetTester to interact with the user interface (UI)

Slide 75

Slide 75 text

In widget tests... • We use testWidgets instead of test • It provides a WidgetTester to interact with the user interface (UI) • We use Finders to locate widgets on the test UI

Slide 76

Slide 76 text

In widget tests... • We use testWidgets instead of test • It provides a WidgetTester to interact with the user interface (UI) • We use Finders to locate widgets on the test UI • There are special matchers: findsOneWidget, findsNWidgets, findsNothing...

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

That's pre+y much all the tests we can write for AndroidSummit2020App

Slide 79

Slide 79 text

Let's test the Questionnaire

Slide 80

Slide 80 text

No content

Slide 81

Slide 81 text

Mockito

Slide 82

Slide 82 text

Mockito • Since GuessGame is a dependency, we are going to mock its behavior using mockito

Slide 83

Slide 83 text

Mockito • Since GuessGame is a dependency, we are going to mock its behavior using mockito • The syntax is as follows:

Slide 84

Slide 84 text

Mockito • Since GuessGame is a dependency, we are going to mock its behavior using mockito • The syntax is as follows: • We use when(...).thenReturn(...) to control synchronous behavior

Slide 85

Slide 85 text

Mockito • Since GuessGame is a dependency, we are going to mock its behavior using mockito • The syntax is as follows: • We use when(...).thenReturn(...) to control synchronous behavior • We use when(...).thenAnswer(...) to control asynchronous behavior

Slide 86

Slide 86 text

No content

Slide 87

Slide 87 text

Why do we mock?

Slide 88

Slide 88 text

Why do we mock? • We care about the public API contract, not about the implementa6on details

Slide 89

Slide 89 text

Why do we mock? • We care about the public API contract, not about the implementa6on details • It'd be tedious if GuessGame had 100 ques6ons

Slide 90

Slide 90 text

Why do we mock? • We care about the public API contract, not about the implementa6on details • It'd be tedious if GuessGame had 100 ques6ons • We can simulate successes and errors without knowing the internals of our dependencies

Slide 91

Slide 91 text

Let's finish the test!

Slide 92

Slide 92 text

Let's remember our UI

Slide 93

Slide 93 text

No content

Slide 94

Slide 94 text

Our tests failed

Slide 95

Slide 95 text

Questionnaire renders the question and the final score when the game is completed: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ The following assertion was thrown building Text("Test question 1", debugLabel: (englishLike display1 2014).merge(blackMountainView headline4), inherit: false, color: Color(0x8a000000), family: Roboto, size: 34.0, weight: 400, baseline: alphabetic, decoration: TextDecoration.none): No Directionality widget found. RichText widgets require a Directionality widget ancestor. The specific widget that could not find a Directionality ancestor was: RichText The ownership chain for the affected widget is: "RichText ← Text ← Column ← _Question ← Questionnaire ← [root]" Typically, the Directionality widget is introduced by the MaterialApp or WidgetsApp widget at the top of your application widget tree. It determines the ambient reading direction and is used, for example, to determine how to lay out text, how to interpret "start" and "end" values, and to resolve EdgeInsetsDirectional, AlignmentDirectional, and other *Directional objects.

Slide 96

Slide 96 text

What are we missing?

Slide 97

Slide 97 text

An App widget that serves as Scaffold

Slide 98

Slide 98 text

Let's fix it!

Slide 99

Slide 99 text

No content

Slide 100

Slide 100 text

What the *&%$! It cannot find the "Test question 2" text?

Slide 101

Slide 101 text

What is going on?

Slide 102

Slide 102 text

What is going on? • Between ques+on and ques+on there's a state change

Slide 103

Slide 103 text

What is going on? • Between ques+on and ques+on there's a state change • We need to ask the test system to paint a new frame a;er the state change

Slide 104

Slide 104 text

What is going on? • Between ques+on and ques+on there's a state change • We need to ask the test system to paint a new frame a;er the state change • We use pump and pumpAndSettle for these situa+ons

Slide 105

Slide 105 text

Let's fix it!

Slide 106

Slide 106 text

No content

Slide 107

Slide 107 text

Wohoooo!

Slide 108

Slide 108 text

All unit and widget tests are passing!

Slide 109

Slide 109 text

...but

Slide 110

Slide 110 text

How do we know if we are missing something?

Slide 111

Slide 111 text

flutter test --coverage

Slide 112

Slide 112 text

No content

Slide 113

Slide 113 text

What is that?

Slide 114

Slide 114 text

No content

Slide 115

Slide 115 text

No content

Slide 116

Slide 116 text

Not bad...

Slide 117

Slide 117 text

So far we have...

Slide 118

Slide 118 text

So far we have... • ✅ Unit tests

Slide 119

Slide 119 text

So far we have... • ✅ Unit tests • ✅ Widget tests

Slide 120

Slide 120 text

So far we have... • ✅ Unit tests • ✅ Widget tests • ❌ Integra/on tests

Slide 121

Slide 121 text

What do we need to run integra/on tests?

Slide 122

Slide 122 text

What do we need to run integra/on tests? • A new folder for integra0on test: test_driver

Slide 123

Slide 123 text

What do we need to run integra/on tests? • A new folder for integra0on test: test_driver • A new dart file called app.dart that will contain an "instrumented" version of our app

Slide 124

Slide 124 text

What do we need to run integra/on tests? • A new folder for integra0on test: test_driver • A new dart file called app.dart that will contain an "instrumented" version of our app • A new dart file called app_test.dart that will contain the scripted tests that will exercise our applica0on using FlutterDriver

Slide 125

Slide 125 text

What do we need to run integra/on tests? • A new folder for integra0on test: test_driver • A new dart file called app.dart that will contain an "instrumented" version of our app • A new dart file called app_test.dart that will contain the scripted tests that will exercise our applica0on using FlutterDriver • We cannot use any internal details of our applica0on (blind tests)

Slide 126

Slide 126 text

No content

Slide 127

Slide 127 text

No content

Slide 128

Slide 128 text

No content

Slide 129

Slide 129 text

No content

Slide 130

Slide 130 text

To run integra,on tests... flutter driver --target=test_driver/app.dart --driver=test_driver/app_test.dart

Slide 131

Slide 131 text

Demo &me!

Slide 132

Slide 132 text

That was fast!

Slide 133

Slide 133 text

So far we have...

Slide 134

Slide 134 text

So far we have... • ✅ Unit tests

Slide 135

Slide 135 text

So far we have... • ✅ Unit tests • ✅ Widget tests

Slide 136

Slide 136 text

So far we have... • ✅ Unit tests • ✅ Widget tests • ✅ Integra/on tests

Slide 137

Slide 137 text

Where to go from here!?

Slide 138

Slide 138 text

No content

Slide 139

Slide 139 text

No content

Slide 140

Slide 140 text

Automa'on for the win!

Slide 141

Slide 141 text

Here's a surprise for you!

Slide 142

Slide 142 text

It is &me to say "see you later!"

Slide 143

Slide 143 text

It is &me to say "see you later!" • ! Very Good Ventures

Slide 144

Slide 144 text

It is &me to say "see you later!" • ! Very Good Ventures • " GDE for Flu3er

Slide 145

Slide 145 text

It is &me to say "see you later!" • ! Very Good Ventures • " GDE for Flu3er • # @jcocaramos

Slide 146

Slide 146 text

It is &me to say "see you later!" • ! Very Good Ventures • " GDE for Flu3er • # @jcocaramos • $ h3ps:/ /github.com/jorgecoca/androidsummit2020

Slide 147

Slide 147 text

Thank you!