The real face of functional testing
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 46 | |
Author | ||
License | CC Attribution 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor. | |
Identifiers | 10.5446/47198 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
| |
Keywords |
droidcon Berlin 20152 / 46
5
7
10
13
16
18
20
21
23
26
31
34
35
36
41
42
45
00:00
Statistical hypothesis testingLattice (order)Statistical hypothesis testingProduct (business)Real numberBitProjective planeCartesian coordinate systemStatistical hypothesis testingLecture/ConferenceMeeting/Interview
01:11
Lattice (order)Slide ruleExecution unitWechselseitige InformationTouchscreenStatistical hypothesis testingCartesian coordinate systemBeta functionBitLibrary (computing)Computer animationSource code
01:38
EmailView (database)PasswordMessage passingError messageLoginAbstractionGoogolLibrary (computing)CodeView (database)Goodness of fitPasswordEmailError messageRight angleLoginType theoryField (computer science)Group actionComputer configurationPredicate (grammar)Interactive televisionTouchscreenMultiplication sign
03:45
OvalAbstractionError messageLoginPasswordEmailVoltmeterType theoryAbstractionCodeStatistical hypothesis testingEmailMobile appPasswordCalculationWritingError messageView (database)MereologyComputer animation
05:26
InternetworkingClient (computing)InternetworkingBitService (economics)Multiplication signMobile appLecture/Conference
06:07
VotingService (economics)CodePresentation of a groupCASE <Informatik>Right angleUniversal product codeError messageInterface (computing)InjektivitätService (economics)Social classStatistical hypothesis testingJava appletInverse functionElectronic mailing listObject (grammar)PredictabilityProduct (business)Algebraic closureModule (mathematics)
07:42
Interface (computing)Local ringService (economics)Multiplication signCASE <Informatik>Statistical hypothesis testingService (economics)Client (computing)Uniform resource locatorCodePoint (geometry)Algebraic closureGoogolMobile appJava appletLecture/Conference
09:13
FacebookLoginInterface (computing)OvalError messageContext awarenessMathematicsGame controllerGoogolFacebookError messageInternet service providerService (economics)InternetworkingConnected spaceLoginModule (mathematics)Statistical hypothesis testingCondition numberImplementationClient (computing)CASE <Informatik>Line (geometry)CodeComputer animation
10:58
Statistical hypothesis testingTouchscreenMaizeSlide rulePay televisionView (database)Inheritance (object-oriented programming)Cartesian coordinate systemType theoryMobile appLecture/ConferenceComputer animation
11:36
OvalPay televisionView (database)Inheritance (object-oriented programming)Functional (mathematics)Statistical hypothesis testingType theoryInformationSource codeComputer animation
12:08
Pay televisionOvalInheritance (object-oriented programming)View (database)CodeEvent horizonBoolean functionGroup actionBootingString (computer science)InformationError messageData typeMessage passingCodeBitSystem callView (database)Java appletInformationString (computer science)Electronic visual displayComputer animation
12:56
Functional (mathematics)Event horizonGroup actionData typeError messageMessage passingSign (mathematics)InformationInformationMobile appComputer animationSource code
13:32
Group actionString (computer science)OvalPay televisionEvent horizonBootingInheritance (object-oriented programming)Android (robot)Statistical hypothesis testingCompilerStatistical hypothesis testingCASE <Informatik>Constructor (object-oriented programming)Cartesian coordinate systemSocial classComputer animationLecture/Conference
14:51
OvalGroup actionCompilerAndroid (robot)InformationView (database)Demo (music)Type theoryHidden Markov modelLecture/ConferenceMeeting/Interview
16:11
LaceAndroid (robot)Fluid staticsCompilerView (database)Exception handlingoutputGroup actionStatisticsSinguläres IntegralStatistical hypothesis testingSubject indexingGroup actionType theoryView (database)Statistical hypothesis testingProduct (business)Presentation of a groupComputer animation
18:43
Exception handlingGroup actionAndroid (robot)Fluid staticsView (database)outputFlip-flop (electronics)Coma BerenicesHeat transferConvex hullMountain passBit error rateCartesian coordinate systemStatistical hypothesis testingSource codeComputer animationLecture/Conference
19:13
Android (robot)Heat transferEmulatorMountain passExecution unitTurtle graphicsDivision (mathematics)System callFluid staticsComputer-generated imageryStatistical hypothesis testingView (database)Dynamic random-access memoryJava appletTerm (mathematics)Coma BerenicesException handlingGroup actionRevision controlRight angleComputer animationSource code
19:48
Execution unitOvalPhysical systemView (database)Android (robot)Statistical hypothesis testingCode2 (number)Lecture/Conference
20:28
View (database)Statistical hypothesis testingException handlingExpected valueAndroid (robot)Fluid staticsGroup actionPhysical systemAnnulus (mathematics)OvalStatistical hypothesis testingComputer animationSource code
20:59
Library (computing)CompilerSinguläres IntegralModule (mathematics)CodeGastropod shellStatistical hypothesis testingFluid staticsInversion (music)InternetworkingStatistical hypothesis testingModule (mathematics)Universal product codeRevision controlSocial classBitCodeConnected spaceProduct (business)Computer animation
22:52
Block (periodic table)Statistical hypothesis testingGastropod shellMaizeInclusion mapOvalCodeoutputInternet service providerInformation securityJava appletInformationLibrary (computing)Online helpComputer animation
23:54
Local ringInclusion mapConnected spaceStatistical hypothesis testingVideo game consolePhysical systemRAIDModule (mathematics)Library (computing)outputModul <Datentyp>View (database)OvalHidden Markov modelUniform resource locatorComputer animationLecture/Conference
25:23
Gamma functionAndroid (robot)Functional (mathematics)Statistical hypothesis testingLocal ringGastropod shellLibrary (computing)Error messageMoment (mathematics)Computer animationLecture/Conference
26:17
Statistical hypothesis testingGastropod shellException handlingInheritance (object-oriented programming)Physical systemView (database)CodeFunction (mathematics)CountingOvalPiLocal ringGamma functionBootingRevision controlLogic gateProduct (business)Order (biology)Module (mathematics)Computer animation
26:49
Statistical hypothesis testingInheritance (object-oriented programming)OvalView (database)Gastropod shellCodeProduct (business)Connected spaceInternetworkingError messageRevision controlUniversal product codeElectronic visual displayFunctional (mathematics)CASE <Informatik>Module (mathematics)Unit testingStatistical hypothesis testingSingle-precision floating-point formatComputer animationLecture/Conference
27:53
InformationOvalPhysical systemView (database)Gastropod shellException handlingStatistical hypothesis testingRAIDMathematicsLibrary (computing)Module (mathematics)Error messageStatistical hypothesis testingRun time (program lifecycle phase)Computer animation
28:29
Drum memoryStatistical hypothesis testingLocal ringGastropod shellPhysical systemView (database)CodeAndroid (robot)String (computer science)Limit (category theory)Statistical hypothesis testingExpert systemError messageComputer animation
29:30
Inclusion mapExpected valueAndroid (robot)Statistical hypothesis testingLocal ringChainMIDIException handlingGastropod shellInheritance (object-oriented programming)Cartesian coordinate systemTerm (mathematics)Presentation of a groupStatistical hypothesis testingComputer animationLecture/Conference
30:14
View (database)Social classGroup actionOvalComputer iconLoop (music)Exception handlingBitStatistical hypothesis testingMultiplication signImplementationNumberKeyboard shortcutCodeFlagComputer animation
31:31
Electronic mailing listMedical imagingView (database)Library (computing)CASE <Informatik>Statistical hypothesis testing
32:37
Statistical hypothesis testingAbstractionLibrary (computing)Multiplication signBitJava appletDot productSocial classLecture/Conference
33:30
Bit error rateCASE <Informatik>Library (computing)CodeSocial classInjektivitätLecture/Conference
34:08
InjektivitätDisk read-and-write headMultiplication signCartesian coordinate systemTwitterProduct (business)Software developerLecture/Conference
35:06
Lecture/Conference
Transcript: English(auto-generated)
00:05
Hello, thank you for your introduction, let me run something, okay, so we can start. My name is Maciej Gurski, I'm here to talk about you, to get you a bit more excited
00:22
about functional testing and to show you it's not that hard to do that, even if you may think it's impossible for doing that for real applications, so that's my Twitter handle if you need it for anything. But before we start, I want you
00:46
to meet my product owner, Mateusz, a guy I'm working with for a few months, and because I'm working with him, I can be here actually, he allows me to write a lot of tests,
01:04
he's really into quality of our product, so let me show you what's there, yeah, you should probably see, that's from our application, that should be
01:20
released by now, but it's still in beta on Google Play, unfortunately, but yeah, it looks good, there are a lot of tests there, and yeah, it's all because of him, so remember him for a bit. So, Espresso, I'm here to talk about this new cool library
01:47
from Google, and yeah, it's still new, but what it allows us is to, instead of what we were doing manually, clicking in the UI to check everything is working, we can automate that, there were some other libraries before that, but this is
02:05
really, really easy to use, for example, if you have a simple login screen, we can make it click a few things, write some text, and in the end check if the error is shown there in red, some of you may see it, some maybe not, so how does it look
02:27
in the code? In the code, it's very simple, almost every time you do that, you start with onView, which is you create a view interaction with some predicate for this
02:44
view, for example, onView with some ID, or onView with some text, or any other thing will do, and then you have two options, the first thing is you can perform an action, for example, type text in an email or password field, or click
03:06
a button, and then after all the actions are done, what would a user normally do, you verify or check what happened, so in this example, we check
03:20
that our login error message view contains some text, simple, right? Looks good, but I don't really like to look at code like this, maybe it's simple in this example, but it gets complicated when you do multiple things and go to
03:44
another screen, so I like to abstract things, and let's think about, let's think for a minute, for a second, what's the first part, what does the user wants to do? Yes, they exactly, they want to log in, log in with some
04:03
email and password, that happens to be too short, so it's clear from this text that they want to log in, you don't have to analyse the Espresso test code, and the second part, yes, we just check the error message, so this
04:24
is the code I like, and what's inside? You would never guess, well, there is not the code that was there before, there is another layer of abstraction that makes you, makes it easier for you to read the code, so for example,
04:46
there is typed text in the ID, and the text you want to type. Another thing inside, there is actually the code that was there before, so we need to write it somewhere, but it's really good to abstract it,
05:05
so that's the code you would write, so by now, you would probably know how to write tests for calculator app, like 2 plus 2, type equals, and you check that on a view there is 4 shown, yes, but our apps do a little
05:28
bit more, usually, who is writing an app that doesn't use internet rest client? Nobody? That's weird, I do that all the time, no, so if you're
05:46
using internet, you're probably using retrofit for rest client, maybe where it's Java, if you are cool, so it doesn't matter much, but you have this service that you subscribe to, and after the work is done
06:05
in the background, you just show something or show the error, right? And to make tests easy, you cannot instantiate this service in your UI code, in your activity or fragment or presenter, you have to
06:23
inject it, you have to use dependency inversion, so in my case, I'm using Dagger to inject services in UI code in activities, and well, for production code, it's the code that you would normally have
06:44
there, like rest adapter and creating the object from the interface that was defined somewhere, but in tests, you can create a module that overwrites what happens in production, and simply return a
07:05
value that you can assert on, that you know that will be there for you, so there is predictability of that, and it just works, so in
07:22
case you are using Eric's Java, you return observable list of some stuff, for example doctors in our case, and you may not recognize this, because this is groovy code, it doesn't matter much, it's like anonymous class in Java, so a closure in this case,
07:51
what about other things, our apps use GPS from time to time, well is it any different, where there is this location client from
08:02
Google, and in my case, I try to abstract the code I don't like, like the APIs from Google are not that fun to use, so we create a simple location service, and we expect it to return location
08:22
at some point in time, so it's also asynchronous, it basically works like our rest client, so in the tests, we simply overwrite
08:41
what location service returns for us, in my case it is some location in Poland, and we can assert on that in tests, in this case it doesn't even matter what you return, you could just return observable of no, because the value is used for another
09:02
service to call the API, and it basically doesn't look at the latitude and longitude there, so it doesn't matter much. What about other things, like Google pass login or Facebook login,
09:20
you cannot have control over that, right, well there is a button you put into layout, and you cannot possibly use dagger to inject into layout, or is there anything change in dagger too? No, I don't think so, so what can we do with this
09:41
Facebook login, how can we test it? Well, we can inject a provider for this button, and not add it to the layout, but do it in code, so we have this provider, and the implementation
10:03
will be simple, it will create a new login button, and add it to the container, add some callbacks, and it will work. In tests, we have another implementation that adds a normal button, that on click, just calls our callback instantly, so
10:25
we won't have any lag, we won't have any connection to internet, to Facebook services, nothing like that, so basically it's the same case like the first one, with rest client,
10:42
and in case we want to check in our tests error conditions, like there is no internet, and we click Facebook button, what happens? Well, we switch one line in another module in tests, it looks awesome, so I could talk to you about this
11:02
for like maybe two more minutes, about all the things you can do, but I think we could switch to code, so I have this simple application, I should show it to you, yes,
11:24
this is an application that you can type your, sorry, GitHub user name, I will make it larger, not rotate, but larger, okay, so if I type my user name, and GitHub
11:54
user name, and click enter, I see some information about me, like who am I, where I work, where I live, it's using
12:04
GitHub API, so nothing fancy, let's look at the code for a bit, it's using dagger, it's using retrofit, or it's Java, so there is this on create view, where the layout is created, simple, we need the views, also simple, and
12:24
on enter we do something, like call folder user, which is basically using Java and retrofit, and after that, if you look here, we subscribe with display user method, which shows some information about the user, so the full info
12:45
about user, or if user didn't provide any information about themselves, there is a predefined string to show empty user, so if I do something else, like 8 0's, there is a
13:03
guy from Japan, yeah, some people like to use different strange handles, and I will make it smaller, someone called themselves, like, in C, if you can read C code, some
13:22
includes, defines, doesn't matter, and there is a user who didn't provide any information, so the app works. So we want to write some tests for it. I usually do it like that, that create a happy path of the application,
13:43
and then only then start writing functional tests, it would make real sense, not much sense to create a test first, and then go to create layout and stuff like that, so let's go
14:01
to some tests, we will create new, let's be groovy class, main activity test, of course our tests need to inherit from this weird class, activity instrumentation test case 2,
14:26
and they need to have some constructor without this, okay, so we have a class, so the first test we would write is
14:42
for the happy path, probably, so if we press enter after typing some text, we expect to see some text about the user, probably, so let me add a method, shows info about user,
15:09
and what we do first, we do on view, we want to type some text, sorry for that, usually happens during demo, why
16:05
doesn't, let me think, yeah, works now, so let me switch
16:45
to presentation mode, I started to import that, so we want to do on view with some text, sorry, with ID, we have
17:02
text ID, yes, should work, less text is better, and it's groovy so we can skip some parenthesis, doesn't matter, and we want to perform an action, type text, this will
17:30
be me, okay, and then on other, oh, no, and we want to
17:40
press action button, yes, or actually no, because this is this ID, and then we want to confirm that the other view, which is text view, check, and it contains the same thing
18:16
we have seen in the production, so it will be something like
18:20
this, and there is one thing missing, and if you run this
18:43
test now, what do you expect to happen? Let me switch the tests and run it, let's see the application, so any clue what will happen?
19:11
Exactly, if it runs, and it failed, oh, there is another
19:30
reason why it failed, because, let me make it closer, you need to call get activity, because it starts activity, and it makes sense, right, like gather starting activity, well,
19:46
they changed that in recent versions of Espresso, but this is what we have in our code base, and yes, it still failed because it didn't contain the text, so we can fix that, we
20:04
can make it sleep for 10 seconds, and this will work, right, it's running, and yeah, no, it failed, yes, exactly,
20:31
thank you, now I hope it will be okay, fingers crossed, so
20:49
we are done, or we need to wait for the test to finish, yes, cool, so, but this is not the way we should write
21:01
tests, they should be faster, a bit at least, so I have this injector in our production code, where we can set this modules, unfortunately, one thing for tests need to be in production code, and we can create a module like success
21:27
module, or test module, and let's create this class, so
21:46
it's a module, so we annotate that with module, and it overwrites some stuff, and for groovy, you need to do this, doesn't matter much, and what we wanted to do is
22:02
for our GitHub API to be provided, but not the slow using internet connection version, but we want to return something that returns observable with the text,
22:38
usually don't write this long text for test code, let's
22:44
statically import it, and now everything will be a bit faster, or no, why it didn't work, oh, annotation, of
23:29
course, you know Dagger, who doesn't know Dagger, there were talks about it, so if you missed them, I can't
23:43
help you, sorry, it still fails, or I didn't run it,
24:08
what's wrong now, oh, because, yeah, the API, I
24:25
completely forgot it, let's see at the API, which is here, it actually returns a user, so groovy, it didn't compile for some reason, even with compile static, so we
24:42
need to return a user here, and there is this name, component, location, name, component, location, yes,
25:33
almost, we are really, really close there, the text is
25:47
still blank, let me call the setter directly, a moment of
26:14
silence, now, of course, again, the wrong order,
26:23
because when the get activity is called, it starts activity and injects production version, and then just changing the module won't inject it again, so yes, it worked now, let's see it again, yay, thank you, yes,
26:54
so that's not all, we want to make an error version of it, so for example, in our production code, we have when
27:05
we, when there is internet connection error, we display error, or for any other reason, like there is no user, and it's all defined in some ugly switch case, switch case, for all the cases,
27:23
for things like that, you should probably use unit tests to cover all the cases, but a single test for functional tests should also be considered, so let me quickly add an error test module, and
27:48
another test, let's just copy it, shows error, and error test module,
28:07
and our module will return runtime error, so
28:21
in our tests, let's first run it, and see it fails, yes, there is this
28:41
test which expected some error, so we can fix that, and here I will simply copy it from resources,
29:05
which error was that, probably this, our experts know nothing about this error, and run it again, okay, so everything's correct now, that was
29:47
faster, so let's go back to application, to our presentation, and yes, that's very simple, but in Espresso, not everything is
30:02
like plain and simple, and if you start testing, there are some things you need to remember or learn the hard way, like we did, so for example, if you write more than two tests, and they will start another activity,
30:21
Espresso will not close all the activities, and the next test will fail, so you need to add, press back several times from your teardown after the test is finished, so what's the implementation, my favorite number is six, so we we do that six times, and we catch some exceptions,
30:42
and it works, yes, another thing, if your device is showing keyboard, well, Espresso is not fast enough to close the keyboard, and then continue, it continues before the keyboard is fully closed, so you will have some tests randomly failing from time to time,
31:04
or often, so we use Travis CI to run our tests, and on Travis, when the build is made on Travis, we have this flag, and we check it in our test code
31:20
to sleep for a bit after closing keyboard, and it works, but it's slower, that's sad, but it works, and Espresso, well, it's new, maybe not that new, but there's still a lot of things missing from it,
31:43
so you will end up adding your matchers, and a lot of them, for simple stuff like confirming if your view is showing an image with ID, like predefined resource, or other stuff,
32:01
especially if you're working with recycler views, well, there is a contrip library for Espresso, it contains some stuff, but in our case, it was not sufficient to write complex tests, so you will end up with code like that,
32:20
so, but basically, if you abstract it, you probably will have you find yourself easier to read, so with our Dr. House, we check if his calendar is displayed, so abstract, always abstract, and to finish this,
32:42
let's see what we've learned today, that, well, Espresso is expressive a bit, in Groovy, it's more fun, I found it the first time I started using Espresso in Groovy, without all the dots, it's very readable compared to Java,
33:04
but most of you probably using Java, it's still really, really nice compared to Robotium, and again, remember to abstract your code, so in the end, you could replace Espresso
33:21
with any other new library that will come later, just in a few classes, there will be dependencies to Espresso, I like to do that, I like to replace libraries when they don't work anymore, but Espresso is working really nice, except for these few cases,
33:41
and always remember dependency injection, dagger in our case, so it will help you, even in cases when you wouldn't think about, it could, always try to remove the code that's not your,
34:00
and wrap it in small classes that you don't have to test, they are simple, yes, and not all the things are easy, you will find yourself banging with your head in the wall from time to time,
34:21
and now, if you really like this talk, if you learned something, or if you confirmed your knowledge, what you already do, it's time to thank my product owner for it, so please tweet to that guy, you can, for example, take a picture of someone around you or something,
34:44
or ask a stupid question when we release the application, because we are waiting for that as developers, but there's some business there, so it's now time for you,
35:04
and I will take a picture of you, of course, so please smile.