We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

The real face of functional testing

00:00

Formal Metadata

Title
The real face of functional testing
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
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
Testing mobile apps is not easy. If you wanted to test "Hello World" app with a button and text label, that wouldn't be really hard. But our apps do a lot more. They are calling RESTful services, checking on user's GPS location or using Facebook or Google+ login buttons, which connect to network themselves. How do you test that? I want to show you my approach to functional testing with Espresso and a bit of dependency inversion magic. Do you have a feeling testing network error conditions is hard or cumbersome when done manually? Turns out it's even easier to automatically test it than "the happy path". You will see real code from production app, so remember to drink a shot of espresso before joining this session.
Keywords
18
Statistical hypothesis testingLattice (order)Statistical hypothesis testingProduct (business)Real numberBitProjective planeCartesian coordinate systemStatistical hypothesis testingLecture/ConferenceMeeting/Interview
Lattice (order)Slide ruleExecution unitWechselseitige InformationTouchscreenStatistical hypothesis testingCartesian coordinate systemBeta functionBitLibrary (computing)Computer animationSource code
EmailView (database)PasswordMessage passingError messageLoginAbstractionGoogolLibrary (computing)CodeView (database)Goodness of fitPasswordEmailError messageRight angleLoginType theoryField (computer science)Group actionComputer configurationPredicate (grammar)Interactive televisionTouchscreenMultiplication sign
OvalAbstractionError messageLoginPasswordEmailVoltmeterType theoryAbstractionCodeStatistical hypothesis testingEmailMobile appPasswordCalculationWritingError messageView (database)MereologyComputer animation
InternetworkingClient (computing)InternetworkingBitService (economics)Multiplication signMobile appLecture/Conference
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)
Interface (computing)Local ringService (economics)Multiplication signCASE <Informatik>Statistical hypothesis testingService (economics)Client (computing)Uniform resource locatorCodePoint (geometry)Algebraic closureGoogolMobile appJava appletLecture/Conference
FacebookLoginInterface (computing)OvalError messageContext awarenessMathematicsGame controllerGoogolFacebookError messageInternet service providerService (economics)InternetworkingConnected spaceLoginModule (mathematics)Statistical hypothesis testingCondition numberImplementationClient (computing)CASE <Informatik>Line (geometry)CodeComputer animation
Statistical hypothesis testingTouchscreenMaizeSlide rulePay televisionView (database)Inheritance (object-oriented programming)Cartesian coordinate systemType theoryMobile appLecture/ConferenceComputer animation
OvalPay televisionView (database)Inheritance (object-oriented programming)Functional (mathematics)Statistical hypothesis testingType theoryInformationSource codeComputer animation
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
Functional (mathematics)Event horizonGroup actionData typeError messageMessage passingSign (mathematics)InformationInformationMobile appComputer animationSource code
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
OvalGroup actionCompilerAndroid (robot)InformationView (database)Demo (music)Type theoryHidden Markov modelLecture/ConferenceMeeting/Interview
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
Exception handlingGroup actionAndroid (robot)Fluid staticsView (database)outputFlip-flop (electronics)Coma BerenicesHeat transferConvex hullMountain passBit error rateCartesian coordinate systemStatistical hypothesis testingSource codeComputer animationLecture/Conference
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
Execution unitOvalPhysical systemView (database)Android (robot)Statistical hypothesis testingCode2 (number)Lecture/Conference
View (database)Statistical hypothesis testingException handlingExpected valueAndroid (robot)Fluid staticsGroup actionPhysical systemAnnulus (mathematics)OvalStatistical hypothesis testingComputer animationSource code
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
Block (periodic table)Statistical hypothesis testingGastropod shellMaizeInclusion mapOvalCodeoutputInternet service providerInformation securityJava appletInformationLibrary (computing)Online helpComputer animation
Local ringInclusion mapConnected spaceStatistical hypothesis testingVideo game consolePhysical systemRAIDModule (mathematics)Library (computing)outputModul <Datentyp>View (database)OvalHidden Markov modelUniform resource locatorComputer animationLecture/Conference
Gamma functionAndroid (robot)Functional (mathematics)Statistical hypothesis testingLocal ringGastropod shellLibrary (computing)Error messageMoment (mathematics)Computer animationLecture/Conference
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
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
InformationOvalPhysical systemView (database)Gastropod shellException handlingStatistical hypothesis testingRAIDMathematicsLibrary (computing)Module (mathematics)Error messageStatistical hypothesis testingRun time (program lifecycle phase)Computer animation
Drum memoryStatistical hypothesis testingLocal ringGastropod shellPhysical systemView (database)CodeAndroid (robot)String (computer science)Limit (category theory)Statistical hypothesis testingExpert systemError messageComputer animation
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
View (database)Social classGroup actionOvalComputer iconLoop (music)Exception handlingBitStatistical hypothesis testingMultiplication signImplementationNumberKeyboard shortcutCodeFlagComputer animation
Electronic mailing listMedical imagingView (database)Library (computing)CASE <Informatik>Statistical hypothesis testing
Statistical hypothesis testingAbstractionLibrary (computing)Multiplication signBitJava appletDot productSocial classLecture/Conference
Bit error rateCASE <Informatik>Library (computing)CodeSocial classInjektivitätLecture/Conference
InjektivitätDisk read-and-write headMultiplication signCartesian coordinate systemTwitterProduct (business)Software developerLecture/Conference
Lecture/Conference
Transcript: English(auto-generated)
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
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
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,
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
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
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
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
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
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
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
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
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
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
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,
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,
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
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
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
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
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
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
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
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,
what about other things, our apps use GPS from time to time, well is it any different, where there is this location client from
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
at some point in time, so it's also asynchronous, it basically works like our rest client, so in the tests, we simply overwrite
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
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,
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
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
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
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,
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
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,
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
user name, and click enter, I see some information about me, like who am I, where I work, where I live, it's using
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
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
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
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
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,
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
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,
and they need to have some constructor without this, okay, so we have a class, so the first test we would write is
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,
and what we do first, we do on view, we want to type some text, sorry for that, usually happens during demo, why
doesn't, let me think, yeah, works now, so let me switch
to presentation mode, I started to import that, so we want to do on view with some text, sorry, with ID, we have
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
be me, okay, and then on other, oh, no, and we want to
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
we have seen in the production, so it will be something like
this, and there is one thing missing, and if you run this
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?
Exactly, if it runs, and it failed, oh, there is another
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,
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
can make it sleep for 10 seconds, and this will work, right, it's running, and yeah, no, it failed, yes, exactly,
thank you, now I hope it will be okay, fingers crossed, so
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
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
module, or test module, and let's create this class, so
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
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,
usually don't write this long text for test code, let's
statically import it, and now everything will be a bit faster, or no, why it didn't work, oh, annotation, of
course, you know Dagger, who doesn't know Dagger, there were talks about it, so if you missed them, I can't
help you, sorry, it still fails, or I didn't run it,
what's wrong now, oh, because, yeah, the API, I
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
need to return a user here, and there is this name, component, location, name, component, location, yes,
almost, we are really, really close there, the text is
still blank, let me call the setter directly, a moment of
silence, now, of course, again, the wrong order,
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,
so that's not all, we want to make an error version of it, so for example, in our production code, we have when
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,
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
another test, let's just copy it, shows error, and error test module,
and our module will return runtime error, so
in our tests, let's first run it, and see it fails, yes, there is this
test which expected some error, so we can fix that, and here I will simply copy it from resources,
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
faster, so let's go back to application, to our presentation, and yes, that's very simple, but in Espresso, not everything is
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,
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,
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,
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
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,
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,
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,
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,
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,
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
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,
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,
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,
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,
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,
and I will take a picture of you, of course, so please smile.