Tech Showcases,
Developer Resources &
Partners
droidcon News
Fake it till you make it - effective use of test doubles
By
Jarosław Michalik
droidcon Americas 2020
Test doubles are powerful tool in developer trying to write unit tests. Mocks allow us to keep system under test isolated so we can check given scenarios and perform assertions on singular piece of logic with ease.
In this talk I will dive into fakes, mocks and other test doubles and I will present use cases for each and every one of them. Also, I will share my thoughts about mocking frameworks and give some tips & tricks of how to use them with ease.
Transcript
English
00:10
hi everyone very excited to be here i always enjoy droid con conferences both online and offline but this year is rather online conferences only
00:15
and we're here to talk about units tests and effective use of test doubles but first of all let me briefly introduce myself most of the most of the time i work on android applications at tango digital agency in krakow in poland besides that i do some freelancing work and i write a blog dedicated to testing with kotlin and kotlin related test tools my sport activities are focused on volleyball both vegetable and indoor volleyball
00:20
and i also play some music you can hear my work on spotify if you want to links are provided here aggregated on my social page
00:25
okay we are here to talk about fundamentals of unit test the concept of test doubles and i will share my thoughts about unit tests especially using test doubles for better and more efficient mocking here you can see and the test pyramid this is some kind of graphical representation of how tests suit for software projects should be designed on the top we have end-to-end tests in android case that would be tests created with espresso appeal or stuff like that the second stage are integration tests in this case in android we are testing integration between high-level components we can use some kind of libraries such as roboelectric or write integration test without android framework directly and on the bottom of test pyramid we have units tests tests that are performed in insulation are relatively cheap and should be running fast usually in software projects there are there should be much more units tests than integration or end-to-end tests so on the bottom we have more insulation on the top we have more integration between components we are testing
00:30
and units tests should be entirely cheaper
00:35
than other kinds of tests and who needs tests and ways of keeping isolation is the main focus of today's presentation
00:40
so let's take a look at those puzzles those puzzle represents some kind of software system where each puzzle is some system component as you can see they are somehow connected while we are focusing on the class we want to test we should take care of its direct dependencies marked in yellow and for the sake of units tests we don't care much about other components
00:45
so now we can break it down a little we have our class which we will be calling system under test
00:50
and we have uh other components that are direct dependencies of a system under test and they should be usually passed by constructor we won't pass the whole dependency but we will try to somehow provide a test double object which will fit right into particular puzzle in a convenient way i have android background and i believe most of listeners here are android developers so i will try to present tensed doubles in android context in android way in mobile applications we usually have some kind of splash screen which is displayed right after app is opened by the user in this place we can decide if user should be redirected to the main screen of the app or show him logic screen maybe includes some permission check of or stuff like that and splash screen dependencies are hidden behind interfaces we have router which will be responsible for opening event screens we have interface for view such as activity or fragment where we would display loading or trigger error message
00:55
and we have some kind of use case which will tell us if user is actually locked into our app or not and in the end we have also some kind of tracking id which could be some string representing advertising campaign from google store or other analytical property which should be a simple value in here so what should we test of course we should test stuff like displaying loading indicator routing to proper places in app and displaying error if something goes wrong so if we take a look at the puzzle was once again our system under test would be splash screen presenter
01:00
and other components would be fitting right into him just like the puzzles
01:05
and we will try to provide test doubles to have splash screen presenter tested in isolation
01:10
ok first dependency of splash screen presenter splash router real implementation would start activity change something on fragment manager or even trigger some action on navigation component in test double we would like to know if a given function was invoked so we could perform assertion on that we will need some kinds of verificator something that will allow us to record splash shooter invocations so we could perform assertion other dependency view android implementation would rely uh on a layout in xml it would be changing visibility of some loading indicator or trigger snapbar events in fragment or activity but in unis tests we cannot smoke the whole activity or fragment in a convenient way so in here we would provide test double some kind of object that will restore information about state of the screen or record information that function given function was invoked the next dependency use case resolving if user is locked in or logged out this class in real implementation would check if user exists in local database or we'll check some kind of token validity then we'll reduce this information to nm class representing user state if it's logged in or logged out in our fake implementation we will just return a given enum values or draw exception to simulate real behavior and finally we have tracking id while in a real application that string would be passed from external applications such as playstore here we can directly provide fake value for units test
01:15
so now would be good moment to distinguish some kinds of test doubles we have stop test double which will return predefined value we have fake which will act as lightweight implementation we have mock special object that would be storing information about its interactions and finally dummy value some property we pass to unit test just to satisfy compiler but we don't take it under consideration but it's uh good to remember that we often refer to alt as the doubles as box so now let's see how we can create mocks for each dependency in our splash presenter first of all we have this resolve user state use case which is functional interface in kathleen in the newest version of language we don't have to implement object of type resolve user state use case if this is a fun interface with only one function we can just pass a value here and we will pass this value
01:20
other value or throw exception from that method next thing we have our dummy value we will provide this value even if we don't use it just to have build passing and then we have mock we want to somehow record interactions with splash router we can use here mocking framework such as mokito to verify if given method was invoked so given user status checks throws some excepts exception when we are starting this presenter then we assert that error was displayed and here you can see this dummy value we don't we don't use it but we have to pass it
01:25
okay so we have mock verify defined here with mokito and we have other test case using mokito verify once again we are stubbing resolve user state use case we return just value logged in we start at the presenter and then we verify that open main screen was invoked on the router mokito makes it very easy to verify function function invocations
01:30
and of course there is opposite case when user is locked out we expect to open a login screen on router and in a very convenient way we can achieve that test behavior with mokito we can also provide mock for view in case of exception thrown by resolve user state use case we want to display error on view and thanks to mokito we can perform that kind of verification too
01:35
this step throws error and then with mokito we verify that display error was invoked on view and this view is created with mokito moc function okay but we are not bound to mocking frameworks we can just provide our lightweight implementation ourselves good example would be here the view of the splash screen somewhere in the test directory we create implementation of that view interface and it will keep the state of loading indicator and record information that error was displayed so in that case when in our test we throw exception and display error is invoked on view we simply check if our fake object state was changed and instead of using mojito for verification we perform simple assertion with a certain library of our choice
01:40
once again stop draws error and then we perform assertion on fake object okay so what are effective differences between mock using framework and custom build fake mock can be created for us by frameworks such as mokito or mokkay but the framework is controlling this object behavior and its life cycle for more complex tracks with mocks with library for mocking like mokit or mackay you can use matchers to for example verify how many times given function was invoked and if was invoked with proper arguments fake in the other hand has to be implemented by developer in non-complex cases such as splash screen view it's just implementing a few methods in interface and adding some boolean properties you are in full control here you can decide how this fake should behave and effectively you can reduce complex verification to simple assertions as shown in example before but of course when you are writing uh fake objects you are introducing some uh logic to test the directory and maybe you should test that too i don't know it's up to developer
01:45
one of the most popular mocking frameworks in jfoam and in android general is mokito
01:50
mojito allows creating mocks in a simple way even in one line i will present you also usage of mokito with mokito kathleen extensions
01:55
let's consider another screen with a model viewpresenter architecture we have some view which will which will display a list of items display one item or render error we have also data provider some kind of data source such as database described by interface with two methods get all elements or get one element by id and we will use mokito to create stop for data provider and create mock for view
02:00
so let's just jump straight into first test case we want to verify that if data provider returns non-empty list of elements that all items are displayed on view so first we create mock for view then create mock for data provider
02:05
and this notation in here in stopped mokito mokito method uh means that every get all method invocation will always return this given a list of elements and then we are starting presenter and using and we are using mokito function verify to check that display items on view was invoked with that particular list of elements that was pretty straightforward example so let's do something else another test case could be verification of displaying only one element so here we will stop another method of data provider get one
02:10
and we write that on get one with any parameter it should return given element defines before and here is example of mature this method would be stopped for all possible invocations excluding null values in kotlin so on every possible argument passed to get one this mock will return that given element defined before and then we can proceed with regular verification on view with mokito we verified that display details function was invoked with given element that was returned by our mocked data provider and of course we can introduce a more complex matcher if we need to let's suppose that we want to return the same element for function invocations where id is between 0 and 5. and we can use mokito matcher arc dat for that case in lambda we write boolean expression some predicate and while performing stubbing mokito will check that boolean expression and return given element for elements matching this expression and for function invocations with argument greater than 5 it will throw exception
02:15
and matchers can be used for verifications too in this example we can use similar matcher in assertion and make the test pass for each argument that matches that given expression in a mokito there are a lot of matchers but i usually use arg that and eq
02:20
that's that's not all that's mokito can do for us we can perform assertions on verifications with it we can now we are testing in here a path where the data provider returns null instead of valid element and then we want to verify that error was displayed on view and additionally that display details was never invoked with any arguments of course all of those things could be achieved without using mokito for every test case we can provide our own lightweight implementation and create custom fakes that may be convenient when we are not sure how mokito matcher work or which matter we should use in here or we would really need to stretch the smokes very hard to mimic behavior we want to achieve and in general there is no silver ballads for creating test doubles every test case is special in its own way
02:25
mokito is java framework and core routines are a kotlin concept by built into the language and java correcting integration and foreign unfortunately is not straightforward so when we will try to mark suspend function with mokito test code simply won't compile and we can of course try to use some generic mechanism of mokito use some do answer construct but in that way we'll lose readability of mokito kathleen dsl but there is uh another framework uh for mocking we can use and it's mock k kotlin first library with similar dsl style stabbing mechanism and with corrupting support in a convenient way there is methods we can use uh every get data returns one two three and for suspend function we can write co every from coroutine every
02:30
so this stepping mechanism uh should be easier for a suspend function while using mock k but of course there is other solution for mocking suspend functions or providing fakes we can just create lightweight implementation instead of relying on the framework so every every suspended invocation of get data will return something predefined passed to our fake via constructor and it's up to developer if he likes to use more complex functions from mocking framework or maybe write maybe create fakes by on his own
02:35
static properties static properties in java and in jfoamr libraries are very common some developers love to use static constructs while others are criticizing it
02:40
that's not for me to tell if static access is good or bad it's a tool it exists and i will show you a few approaches to handle static access in test code but first of all let's check again uh our puzzles with system under test and its dependencies
02:45
while other dependencies are injected through the constructor static objects may not be direct dependency of system under test in a way they are part of it and they are kinda outside so it's very special case so first approach to handle static accessing test would be to remove static access and provide wrapper function for that code let's suppose that we are using yoda time in our system under test and we would like to change behavior of date time dot now function
02:50
so if you need to change behavior of that property in test you could just use extra inter interface or fs8 to abstract it away inject it to your system under test and life goes on other case that developers that are using x java may be familiar with would be abstracting away every scheduler we need to use in our system under test such as main scheduler for android or io and background solvers if we are using some trading in our code but we would like to execute our test in a synchronous way okay other approach to mock static methods would be to set static properties globally sometimes some static methods are invoked under the hood just just like it happens in live data and some kind of approach to solve that problem of live data using android main thread under the hood could be delegating main threat executor in our test and setting its value before we execute our test suit and setting this delegate to null after we execute all the tests and remember do things like this only if you have no control over the static access in a library you use or you have to use
02:55
and of course for other cases there is library for that mock k actually allows us to mock static things for one of my sourdough integration tests for data binding i used mock k to mock android looper just to not have a crushing code so summary how to approach testing static things if you can remove static k words uh and then rewrite particular piece of code that's great but if that's not possible and you have to use those static things in your unit test try to at least create provider or wrapper some kind of little abstraction if it fails check if you cons if you could set something globally just like this uh threat executor for live data i believe you can set globally date time for units test and things like that and if other things fails try to use smoking framework to to do the hard work for you you can use mock k or power mock there are plenty tools to achieve that behavior
03:00
and of course if you are brave enough you can try to use reflection to modify existing properties and then use static objects in your test directly and if you are using some library that is that is well documented
03:05
and developers actually care about its existence there may be library with test extensions to to handle that stuff okay to sum things up we talked about fundamental concepts of units testing test doubles and in the end i will give few takeaways know your test structure with given when then flow you will find creating test doubles much easier in the given section prepare dependencies for your system under test in a when section execute logic you want to test and in the end in assert section in then section verify that your system acts just like you want compare your results to what was generated by the system and what you expect those properties to be
03:10
know when to use proper test double sometimes you need to pass only stop the value or write simple fake you don't need a mockito for everything using mokito or mokkai just return predefined value could be overkill for your system when you have very big test suit creation of this objects may be very expensive in terms of
03:15
cpu or memory usage and sometimes it could be easier and more efficient to write fake implementation by yourself and last but not least keep a good separation of concerns or at least try to use constructor injection it's not impossible to provide fakes for static variables and objects but every test case is special in its very own way readability comes first and if your code is solid and follow in general dependency in inversion principle then you are good to go with writing great test suits that will serve you your team and your project as well
03:20
so thank you for your attention that's all from me today i hope that you enjoyed my presentation for more units testing related content check my blog kotlintesting.com and if you want to find me online go to the link you saw on the screen there are my social profiles there are my social profiles so now i believe we have some time for qnt
03:25
okay i see first question can you explain what corrupting scope is in lyman's term unfortunately i cannot answer that question i'm not encouraging expert i usually use eric java in my projects and co routines
03:30
are only coming to me just to use in the future projects
03:35
another question are there any performance impacts of mock k well i didn't measure it by myself but i remember reading some article on medium so i believe you could find answer to your question there
03:40
and the last question i see on the screen what are the other other use cases of mock k
03:45
most of the presentation was dedicated to mokito kathleen a mochito with mokitokatlin extensions
03:50
and in general you can use mock k just like you were using mokito and mokito kotlin before every function for basic for basic mocking is in here in addition uh mock k supports mocking final classes uh i believe that in regular mojito to enable mocking final classes and i will just tell that classes in kotlin are final by default unless you specify that class is open or abstract of course and with mokito you had to enable special plugin to to to be able to mock final classes in okay it's enabled by default okay another question is mock k better option than mokito does these test tools have an impact when we talking about java or kotlin language so the greatest thing about mckay is that it's gotten first we can uh just like i showed you before mock corrupting is in very convenient way in a one-liner we have these dsl constructs to create mocks and stops syntax is slightly different and of course the biggest difference between uh mokito and smok k it's this dsl for creating stops and mocks remember that mokito is a java tool but we have mokitokatlin extensions
03:55
and in a legacy project where someone was using
04:00
regular makita and we are writing cutlin code i would just add smoky tokatlin extensions but when i would start new projects i will go with mock cave from the very beginning
04:05
okay i think that's all you wanted to ask me now so thank you once again for attention i hope that you enjoyed this presentation that you learned something new
04:10
and of course you can find me online my social profiles should be should be linked somewhere thanks again for hosting me droidcon and see you soon
droidcon News
Tech Showcases, Developer Resources & Partners
EmployerBrandingHeader
jobs.droidcon.com
![]() Latest Android Jobs
Kotlin Weekly
![]() Your weekly dose of Kotlin
ProAndroidDev
![]() Android Tech Blogs, Case Studies and Step-by-Step Coding
Zalando
![]() Meet one of Berlin's top employers
Academy for App Success
![]() Google Play resources tailored for the global droidcon community |
Droidcon is a registered trademark of Mobile Seasons GmbH Copyright © 2020. All rights reserved.