Hilt makes testing easier on Android

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video i'm going to take you through all of the testing related nuances that have to do with hilt so dependency injection with hilt in the previous video i talked about constructor injection fragment constructor injection with hilt and how this can be done and why we want to do this to make testing easier or i guess the main reason that we do it is to make testing easier you know constructor injection in general is pretty much all about testing we want to do it so that we know what dependencies we're passing to the object to instantiate it so as i said in this video what i'm going to do is i'm going to go through the hilt documentation and just kind of we're going to go through it like step by step and i'm going to talk about uh you know how hilt makes testing easier on android if you have done testing previously uh you know namely testing with dagger because most people use dagger some people use coin or what is the other one codeine or other dependency injection frameworks which is fine but if you're used to using dagger you know that you basically got to build a isolated test environment for dagger when you're building instrumentation tests so with hilt this is way easier because not only does hill generate the components for you which you already know if you've come this far in this this short video course but in for testing it generates all the test components also so there's a lot of like big conveniences when it comes to testing but there's a little bit of setup involved but you know if i was to give it a number there's probably five percent of the setup that you would have had to do if you used regular dagger to build your kind of test environment so i'm gonna be taking you through everything let's start at the documentation and we'll go through it kind of step by step the way that the the the hilt team and the android team has uh laid it out for you so if you've been following along with the course whether it's on youtube or on my website by the way if you're watching it on youtube you can watch it for free on my website and it keeps track of your progress if you have been following along the previous video that we watched was this one right here fragment constructor injection with hilt so in this video i'm going to be kind of carrying on from where we left off in that video because when we're when we're writing tests we want to be able to pass constructor arguments to fragments anyway so i figured this is a good place to just carry on from okay so here we are in the documentation dagger.dev slash hilt slash testing and like i said we're gonna go through this document and i'm gonna explain things and give examples kind of as we go through and just show you like this is the general way to set this up and to get started with this so the first thing up here is this note that says currently hilt only supports android instrumentation tests and roboelectric tests although you can still write regular tests so regular like jvm tests regular unit tests but you can't use hilt for it and this is this is similar to you know regular dagger if you're writing unit tests you can't use regular dagger or you know i don't personally know that you can use i've i've tried and i thought oh this doesn't you could probably get it to work but it was not easy from what i could see so this is not different from from what regular dagger already offers all right so now let's uh let's let's go down here let's just go to the test setup and just kind of go through the steps of what we need to do to set this up so first we need to get the test dependencies as shown up here so i've got those already in my source code so i'm just going to copy paste them over so i'm just going to so you could actually just click on the link down in the description of this video and copy these dependencies i just copied them now i'm going to go to the build.gradle file and i'm going to paste them in so again there's a link down in the description of this video and you'll just want to like i'll bring it over here actually it'll take this is the build.gradle file just scroll to the bottom and copy these test dependencies right here and then just go to your your project and just paste them in right here like i have so we have the hilt android testing dependency notice i'm using android test implementation because these are test dependencies we have the fragment testing dependency because we need this to use uh the the fragment scenario if you're familiar with testing on android kind of the modern way to do testing on android we need we use this fragment scenario thing and we use this activity scenario thing so this is for the activity scenario this is for the fragment scenario i'm using debug implementation here because i'm i believe there's a bug or something if i use android test implementation here so i'm using debug implementation i can't remember the exact reason but there was definitely a reason for it so just make sure to use the exact same thing that i'm using so i'm gonna press sync and get those all set up okay now let's go back to the hilt documentation so it says to use hilt in a test we annotate the test with hilt android test add the hilt android rule test rule and use hilt test application so instead of just going through the document i'm just going to go to android studio now and take you through that and talk about the nuances between those those steps so let's go to android studio and let's do that okay so i'll close the build.gradle file i'm going to go into the android test directory up here expand this and there's no tests in here because i've already deleted it i'm going to create a new test class so new column file create a new test class i'm going to call this main test and this will be a class so the documentation said three steps there i'm just going to go through everything and without referring back to the documentation so first it says you need to annotate a test with hilt android test that will mark this test and say hey hilt this is this is something i'm going to be injecting things into it also said to use the test rule hilt android rule so i'm going to use at get rule so this is the notation to insert a rule into a test again i'm assuming that you know things about testing so i'm not going to be talking about like test rules and general testing related things if you're watching this i'm assuming you've written instrumentation tests before so i'm just covering the hilt related stuff so you can call this hill tool you can call this whatever you want then you want to write hilt android rule and reference this to the test so this this stuff right here is kind of the setup that you need to prepare this class to use hilt and this this colon should actually be an equal sign there we go now before i can inject anything i need to call hilt rule dot inject so a good place to do this is to use the before block so i'm just going to write before because this will run before every test and i can say hilt rule dot inject now keep in mind you also could like if you had a test function whoops if you had a test function i did fun you know some tests and if i wanted to inject something and then use that thing i could have also done hilt rule.inject here and then used used the thing whatever the thing is but i you know i think putting it in the before block is much more efficient then you don't got to remember like your test order or whatever it's just a better idea i think to put it in before so if you are going to put it anywhere i think putting it in before is is what is what to do now there's one thing you need to know about the this uh this rule one more thing and i'm actually gonna go back to the documentation to reference this so if you scroll down to the very bottom of this testing document it says hilt rule order it says if your test uses multiple test rules make sure that the hilt android rule runs before any other test rules that require access to the hilt component so if you if you have dependencies that require any kill dependencies or any rules that require health dependencies you need to make sure that this the hilt android rule is the first one that runs and you can do this very easily through this little order kind of option that you can pass to the test rule you can also use something you can also use a rule chain to specify this so let's go back and i want to tell you another kind of caveat so i could specify the order here say order equals zero and then imagine i had some other rule so i'll just do add get rule and i'll say var some rule of course i didn't create this this class i'm just imagining that i have some rule here and somehow it depends on something that hilt has so i could do order equals one now you're probably trying to follow along and noticing that your order option is not available to you the reason it's not available is because it's only available in uh junit 4.13 so you need to right now you probably have 4.12 inside of your test implementation and android test implementation so you need to change this to 4.13 this is a new version of junit 4 i guess yeah a new version of junit 4 that has been released and it's got this new feature so make sure to add that and then that will give you the option to pass an order to the rules so they get executed in that order and then everything will be fine so i'm going to delete this and we can get rid of the order because that is not needed it's the only rule that we have and we are ready to go into the next step so if you go to the documentation and we're going to go up to the very top again and look at number three so it says use hilt test application for your android application class so just like with the production code it generates the the you know test component and all that kind of stuff it will also generate a hilt test application along with the test components so we need to we need to tell hilt how to use that test application and just like you would do with dagger we need a custom test runner for that so just to kind of show you that we need that test application i could try running this right now so if i just right click and go to run try running this test it will give me an error and it will say hey did you remember to set up your uh application oh it's actually giving me a different error so this this is actually a weird error that i ran into that i wanted to also share with you so if you get this error com google code find bugs jsr blah blah blah blah blah blah i don't even actually know what this means it's i think it's like some kind of a bug but it's a relatively easy fix so all you need to do is go into your build.gradle file and you need to add something here so right config dot all open this up and then write resolution strategy oh it should be a capital s strat strategy dot force equals co dot com.google.com bugs and then colon jsr 305 3.3.0 oh this should not be an equal sign there we go so sync that now we can go to our test so go into main test and delete this inject because we obviously don't want to inject something twice now run this test and we should see the correct error which should say something like hey we we've we need an application so there's the air it says hilt test uh cannot use hilt android app application can't be found my or the application class is basically wrong so it says to use a custom one or we need to specify the one that has been generated for us so like i said hilt will create this test application for us we just need to tell it how to use it and we're going to build a custom test runner for that if you've built instrumentation tests using dagger this is something you have to do and so you have to do with hilt also so right click on the testing package directory here and create a new kotlin file i'll just call it you know my test runner and this will look like this so it'll be class you know my test runner extend android junit runner we need to initialize that open this up i want to insert the new application method the new application function and for the second parameter here the class name parameter i want to get the hilt test application class so dot java and then name that tells the test runner when it runs a test which application to use so it's basically saying don't use the regular application which is this uh whatever wherever that is the my application class don't use that it's saying use this one that hilt has generated for us so what we want to do now is we want to go to our build.gradle file and we want to tell it to use that test runner and we can specify that right here so i'm just going to grab the package that i created it in comcoding with mitch dagger hilt playground and i can just paste that package name in here and do my test runner and now hilt knows which application class to use for the tests so now if i go back to the main test and i run this we will no longer get any errors and it should run even though we're not testing anything the test will run and it should not give us any errors and there we go the test passes even though it's not actually testing anything so just to summarize kind of what we did here the first three steps to use hilt in a test this will never change you always have to annotate the test with hilt android test you always have to add the hilt android rule and make sure it runs first then we need to to use the hilt test application by by telling it or where we need to tell android studio where to find that hill test application and how to use it in your test those are the three things that we just did and also i showed you there was some weird error with this um what was it this jsr thing so if i was to just comment this out if i was to sync it and then try to rerun my test so if i just press the play button here you'll see that i get that weird jsr error i really have no idea what this is all about i googled it i tried this solution it worked i didn't really look any more into it you can if you want i just wanted to make sure that i i mentioned it in the video now we're going to talk about test dependencies so if you've written tests before you know that often when you're writing the tests you build test fakes or sometimes test mocks you kind of have two kind of options there if you build a test fake you can use a mocking library like mock.io or mokito or whatever but if you want to build test fakes which i'm a big fan of test fakes you you'll need to if those if those dependencies in production are provided through modules or through hilt you need to build test modules to provide the test fakes so that's what i'm going to show you how to do here we're going to we're going to assume that there's some dependency that you're using in production but you can't use that dependency in testing you need to build a fake for it so i'm going to show you how to uninstall the module uh the the kind of production module i guess you would say and how to install the testing module with the test fake all right so let's assume we have some dependency so i'll go into our our di package here and go into app module and let's just assume that for whatever reason we can't use this string in our test because you know for whatever reason we can't we have to build a test fake for it so let's go into main test and i'll show you how you would do that so there's a couple ways well i guess the first the first thing that i want to say is if you want a module to be specific to only like one test class you can build it inside of the test class if you want it to be able to be used in any other test classes you would build it outside of the test class i'll come back to that that point in just a second here after we build this so if i wanted to build a like a test version of that i could say at module so i'm just creating a module i'm going to say install in and specify the component so it's going to be the application component because if we look at the app module here that's where this is installed so i'm essentially just creating a test version of this so i want to install it in the same component i'll say object i'll say you know app module i can call it the same thing or i could call it i could call it test app module if i wanted to be really clear it doesn't really matter i'm going to call it test module because that is the module that i'm replacing and i can just do like like you know whatever this i want to do the same thing so i can copy this singleton that provides the provide string paste this in say okay and i can say this it's some you know testing string so it's changed slightly this is a test version of this so now the question is how do i tell hilt to use this one as opposed to using this one because at this point they're identical other than the string which is different so what you can do is actually go up to the up to the the class up here and i can i can uninstall that module from the component so i can just annotate this and say uninstall modules and i can specify the app module whoops specify the app module and of course you want to get the one that is not within the testing class so app module should be that package right there and do class i guess it would have made more sense if i you know called this if i called this test app module because then you would be able to tell the difference um i guess that makes more sense so let's do that so now if i was to inject this string so i'll just go up to the top here and i'll write you know add inject latent variable some string and that's a string now i want to do a test and just make sure that i am seeing the correct thing so i can just do assert that i can do some string equals contains contains i spell it right contains string it should be test or testing string because it says testing string so getting that import getting that import i think either one of those is fine so notice that it says testing in this string if we go to the app module this one does not contain testing so that's what i'm making sure of i'm testing to make sure that some string contains the the substring testing so now if i was to run this this should pass no problem and there you can see the test passes so just to confirm that what i'm saying is true and it's you know what what i'm saying is happening is actually happening instead of uninstalling this module so i'm not i'm going to comment this out so that it's not getting installed and i'm going to comment out this module and now i'm going to run this test again and you should see that this test will now fail because there's no testing in this string and that's the one that's going to be getting injected so there's the test running and there is the test failing it says we expected a string that contains testing but we got it's some string so that's uh that's one really convenient way that you can provide like testing fakes or testing alternatives you know really really simply using hilt and to come back to my first point when i was saying you know you can put this inside of the class or you can put it outside of the class if i want to make this available to all other testing classes i could just put it outside or if i wanted to make this you know only available inside of this this test for whatever reason you just move it inside it's you know really convenient that way you have a nice isolated test now we're going to talk about activity scenario and fragment scenario so if you've been you know introduced or taken a look at any of the kind of modern testing methods the modern instrumentation testing methods that the android team has been recommending you know that there's this thing called an activity scenario and a fragment scenario basically activity scenario will create a simulated kind of test version of the activity that you mentioned and fragment scenario does the same thing with fragments the fragment will actually spin up a fake activity and then put that fragment into that fake activity so that you can test these things in isolation so the reason that i want to bring this up with hilt is there's some nuances with it and and it's definitely worth mentioning because the fragment scenario actually doesn't work out of the box we need to do some extra setup to get it to work with hilt so let's do the let's do the activity scenario first because it's easy and it just works so function tests uh you know function i'll just do main activity test and we'll just whoops we'll just create an activity scenario and i'll just show you that it works fine so scenario equals launch activity and then i can specify you know main activity or whatever activity you're testing it doesn't matter this is telling me i need to add the experimental core routines api function because i'm using something experimental in there i don't remember but this will work just fine so it works exactly the same as what it would have done before we have an activity even if i was injecting stuff into here you know it doesn't matter it's gonna work exactly the same as it did before previously when you would do uh launch activity so nothing's different here i'll just run this and just show you that it does work just to kind of confirm things so coming down here and there we have both of our tests passing so now now we come to fragment scenario this one does not work out of the box and i will i'll explain to you why so i'll first just do function test i'll do you know main fragment test because that's the only fragment that we have here other value scenario equals launch fragment in container i'll mention main fragment and you'll see that this will not play nice so if we look in main fragments obviously well for one thing we have we're doing constructor injection here and if you are doing uh constructor injection we need to to specify a fragment factory so i'll do you know factory i'll inject the factory up here so at inject and i'll do latent variable fragments factory fragment factory get our main fragment factory wherever that is so we're injecting the factory this should be totally fine because remember we have our our factory in here somewhere there's the main fragment factoring it's being injected this should be no problem i can inject it provide it this theoretically should work fine and if you were testing previously with dagger like without using hilt this would work just fine so let me just run this test and you'll see that it does not work so we have our main activity test passing or some test passing but main fragment test does not work and the error message here it says hilt fragments must be attached to an android entry point activity so the reason this doesn't work is when you when you do this launch fragment and container function what it does as i said is it launches a simulated activity or launches like a fake activity it's a real activity but then it puts that fragment whatever fragment you specify into that activity so hilt one of the requirements of hilt is that for if any fragment is if any fragment is injected with android entry point and you are injecting anything into it the activity that hosts it also must have android entry point so if we're using kind of like a quote-unquote fake activity and then just putting a fragment into it obviously that isn't going to work so we need to we need to solve this problem so if you go to the android documentation where hilt is explained and you go to the hilt testing guide here if you go to the very bottom of the page launch fragment in container so this little kind of excerpt here is the solution to the problem so it says it's not possible to use launch fragment in container from the android x fragment testing library with hilt because it relies on an activity that is not annotated with android entry point and then it says laun use this launch fragment in hilt container code from the architecture samples so jose i don't remember how to pronounce his last name jose our our man here jose has come up with a solution to this problem so if you go into i think it was in an android test i don't know it doesn't matter i'll just show you in my code but anyway he came up with this solution it's somewhere in there i found it i'll actually just bring it over on the screen here because this is what we're going to be doing so his solution is this this kotlin extension function right here so he created this function launch fragment in container what it does is it is it um uses this activity called hilt test activity which we are also going to create and it's and it's annotated with that that annotation that we need and then he puts the fragment into there so uh let's let's do that and then we'll i'll come back to this and say why he almost had the perfect solution and he forgot kind of one thing or maybe he left it out intentionally i don't know but anyway let's let's do that so i'm going to build a extension class here so right click on the package directory hilt extension and i'll just you know paste this in now this is going to give us some warnings because this hilt test activity does not exist so we need to create this hill test activity so i'm going to right click on source here create a new directory called debug so this is only going to be used in debug there's no need for this test activity in production now i'm going to create a java directory and then inside the java directory i'm going to copy the package structure so com dot codingwithmitch. hilts playground yeah that should be good and now inside here i'm going to create this hilt test activity so right click new file hill test activity now i just need to extend this by app compat activity and annotate this with at our android entry point and of course this needs to be initialized so this also needs to get added to the manifest actually so we need to create a manifest here so right clicking on debug also create a new android resource file call this androidmanifest.xml i don't know if there's a manifest option no i think just xml oh maybe get rid of the xml there android manifest i'm not sure why it's not letting me create that so i'm just going to copy the manifest from here paste it into debug sure there we go yes add so everything's the same we don't need the internet permission though and we just we just can get rid of like kind of all of these things so we can just close off the application class here and just have the one activity which is going to be the test activity or the hilt test activity hill test activity i don't need an intent filter i don't need anything we also need the exported equals false i don't remember why but i have it in my notes i'm so i'm going to add it i do not remember why i need that but anyway there's our kind of testing manifest we have our hilt test activity it's got the android entry point and now if you look at the hilt extension that's what that's going to use so that kind of satisfies the requirement that we need to use like a simulated activity or a fake activity but it must be annotated with android entry point so now let's uh let's use this so let's use launch fragment in hilt container and then i'm going to show you why it kind of falls short a little bit so launch launch fragment in hilt container and specify the main fragment so that was kind of weird specify main fragments and i don't need the factory because it doesn't accept the factory parameter so we're launching the scenario we're launching the fragment theoretically everything should work here but it doesn't so let's run this and see okay so there's a test running and we do get a failure again but we get a different error so notice the error is unable to instantiate the fragments because it can't find the constructor so this error is actually because of the factory there's no fragment factory provided here because before we had you know launch fragment in container and we were specifying a we were specifying a fragment factory parameter well here we we can't specify a fragment factory parameter so that's where his extension function kind of fell a little bit short so i built an extension function which is almost identical to his the only difference here is you're passing a fragment factory as input and then you are using that factory to instantiate the fragment so that it actually knows how to build the fragment if it has constructor arguments so i'm just copying that i'm going to go to hilt extensions paste that down below and by the way this is all in the source code so you don't don't bother typing this out again the only difference here is that this uses a fragment factory and i'm calling activity support fragment manager and setting that factory when that test activity is being built so now we can go back to our hilt test or sorry our our main test here i can say launch fragment in container get rid of this and i can specify a factory so save factory equals fragment factory you can open this up or not whatever you want now if we run this test it will be able to find the constructor because we specified that factory so there we go we get main activity test main fragment test and some test and everything is working as we expect one other thing that i would mention too is if we open up main fragments notice here there's a log that says hey look and it's printing out some string notice that if we go to the logcat that we see the test string so because in the test we we uninstalled the app module and we installed this test app module and we were using the testing string instead of the normal testing string or instead of the normal string and it just noticed that it is working correctly so we're using the testing string here because that's the one that the test uses so that's pretty much it that's kind of mostly all you need to know about hilt and and how it makes testing easier there there truly is a lot of time savings here like if you've ever built instrumentation tests with dagger and built that like simulated dagger environment for the instrumentation test you know that that's a lot of work you got to build a lot of things hilt made this way simpler there's a lot of things that are generated a lot of things that work kind of out of the box i was really surprised that they didn't have a solution for that fragment scenario thing because that's like the main thing that you test is the fragments in isolation that was really surprising but jose had us all covered there he built that extension function which works great other than you couldn't pass the factory but two lines of code was an easy fix for that too so overall i would say hilt definitely makes testing easier on android i really enjoyed it so far the only thing i would say is i didn't really do any like really uh intricate tests like i didn't do any like really heavy tests like big tests so i'm sure there's probably some like edge cases that maybe hilt doesn't work well with you know i'm just speculating i'm just assuming this because it's a new piece of software edge cases typically always cause problems i'm just telling you that i didn't like thoroughly test this but from the from like the simple cases that i see there's definitely a lot of time saving and it seems like it's everything is really good so we'll see what happens but i'm sure it's going to be mostly good there's always edge cases there's always things to watch out for so that's just software development now i think this is probably going to be my last hilt video it's probably gonna be the last one in the series i know we didn't cover any like really advanced concepts like i didn't show you how to build like a custom component uh a custom test application um there's this thing called entry points which you can use but i don't know i mean to be honest i'm just kind of bored of hilt and i want to move on to do something more fun and i think that it wouldn't be like these are kind of things that don't come up very often anyway so if you had to go research them you know yeah i think you could do that no problem so anyway what i'm going to be working on next is i'm i think i'm going to originally i was going to jetpack compose and and just kind of play with it and do make maybe a little mini course on it but i kind of thought about it more and more and i've been talking to some of my members on discord and i i think i got a better idea of what i want to do and i'm much more excited about this idea so compose is still in beta so maybe i think i'm going to wait a while for it to come out of beta and then you know use it when it's quote-unquote stable i guess so instead what i think i'm gonna do is i'm gonna rebuild the powerful android apps course with all of the new stuff that is out now and like kind of like how i would build an app today because it's change it was it's about a year old and things have changed a lot if you know android development that's how it goes so i think i can you know significantly improve it and i want to add some really cool features so clean architecture obviously mvi i would use kotlin flows which i used kotlin before but use flows now yeah use cases with clean architecture so that's going to greatly you know really clean things up now the thing that i want to add to it the thing that's going to make it kind of like that i'm really excited about is i'm going to do something with sockets so we're going to use socket.io and i'll create so that the blog feed will be real time so uh you know if anybody doesn't know what socket.io is it's basically like an api or a tool that you can use to interact with a database and it makes the content real time so it's kind of like you have say you have a any kind of a database like a postgres data database on a server or whatever and that's not real time right like you can't like observe the changes to that database so something like socket.io you can hook up to your database and make it real time basically so we're going to make the the blog feed real time and probably what i'll do is i'll update open api dot xyz and i'll add like a commenting functionality on the blogs so if you click on a blog you can see like the comments just like on instagram or any other any other website if there's like content there's always comments below so we'll create comments and then make those real time also so i'm super excited about this i've wanted to do something with socket.io for like a long time so i'm i'm really excited to get started with this now if this video series on hilt helped you don't forget to go to codingwithmitch.com go down to testimonials and please leave me a testimonial and let me know if it helped you hey even if you got something bad to say i don't mind say something bad i highly doubt it because my courses are the best but i'm just throwing it out there go register an account it's free click write a testimonial and write a testimonial it's it's that easy it only takes a few clicks i would really really appreciate that from you i kind of heavily rely on testimonials everybody on the internet if they're ever looking for you know content they always the first thing they look for is testimonials so i would really appreciate that if you just took you know takes probably two minutes go to the website go to testimonials write a short testimonial and hey then you'll be registered and you'll be able to get notified notified when i work on the powerful android apps course which i know that you're interested in because sockets are really cool and it's a good skill to know as always do not forget to like the video and i will see you in the next one you
Info
Channel: CodingWithMitch
Views: 16,450
Rating: undefined out of 5
Keywords: android instrumentation test, android hilt, hilt android, dagger hilt, dagger hilt android, dagger hilt android tutorial, fragment scenario, activity scenario, android fragment scenario, android activity scenario, hilt testing, dagger testing, android hilt testing
Id: vVJeOACGSOU
Channel Id: undefined
Length: 32min 37sec (1957 seconds)
Published: Mon Jul 20 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.