Testing Fragments with Dagger-Hilt - Testing on Android - Part 12

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys welcome back to a new video so in the last video you will learned how we can use dagger hilt for our test cases to inject dependencies into these classes and here we just injected our shopping item database here into our shopping door test class and that was working perfectly fine but i also said that this works fine as long as we don't want to test fragments because usually when we test fragments we use a so-called fragment scenario that means that we will launch the fragment we want to test in an empty activity and that is super easy but with dagger hilt this doesn't work out of the box because when we use arm dagger hilt with fragments and we want to inject dependencies into those fragments then we need to annotate these fragments on the one hand with android entry point that is not a problem but we also need to annotate the activity these fragments are hosted in with android entry point and since this fragment scenario launches an empty activity that comes with a library this this annotation is missing there so this empty activity is not annotated with at android entry point and because of that this will always crash if you use that with hilt so dagger hilt is currently still in alpha so depending on when you watch this video maybe there is a solution for that an easier solution then i will present you but if not then this is the only way we can do that right now and what we will do here is we will basically do that manually so we will launch a custom activity that is annotated with at android entry point and then we will attach our fragment to that custom activity and that is also not my solution that is a solution that you can find in google's architectural samples in their documentation is the link to that but i will slightly modify that function from them that so that it fits for our needs here so first of all we need to add a dependency for these fragment based tests in our build.gradle module app file here so i will just paste that dependency in here as usual you can get that from my github repository link is in this video's description we click on sync now and you can also see that this says debug implementation and we had this android test implementation we had test implementation and now we have debug implementation so that means we need to create such a debug source set in which we will create our actual custom test tilt activity so that activity that is annotated with at android entry point and on which we then will attach our fragment and for that i will actually switch to this project view here in our project hierarchy and in our src folder we will create a new directory here and call that debug and we also need to have the same structure here so we would just use debug.java.com so our package name but sadly this doesn't work for directories because those are not packages so we need to create these folders on their own so first of all the debug directory here and then right click new directory now comes the java folder and okay i think now we can create a package here instead of this java folder so now we can use our package name that we have on the other folders as well so com.android devs in my case dot shopping lists testing youtube and you can see now we created that hierarchy here and inside of this folder here we will create a new kotlin follow class called test hilt activity or let's actually use healed test activity select class here and that will just be an empty activity that inherits from app combat activity just as usual and we annotate this with android entry point as i said so this will be the activity we will then attach our fragments to for our test cases and because that is now annotated with at android entry point this will work but exactly the same way if we would create a new activity in our main package also in this package we need to add this activity to our manifest file but we won't add this to our real manifest file because this is only an activity that we use for testing and we don't want to include this in our manifest by default so we will actually copy our actual manifest and paste this into this debug folder so that debug folder basically has its own manifests so we actually just go to this android manifest here in our main source set copy that paste it into our debug folder press ok and we can actually remove most of that so for our activity we can remove that stuff and of course not main activity instead that will be hilt test activity here then we can remove all of these that is fine but we also want to set export it to false which just means that we can only access this activity from this package here and not from the outside basically okay and then we can actually switch back to our android view here and now we should be able to see this debug package here in addition to our android test and test directories now we want to go inside of our android test directory and in our root package here we will create a new kotlin folder class that will just be a plain kotlin file here so not a class and we will call that hilt extension so that will just be an extension function well not really an extension function but just a function that is kind of a utility function for dagger hilt so we will just put that in here and then we can call that from everywhere afterwards and this is also not an easy function but i still decided to go through it and not copy and paste it because this is related to testing and i think it's interesting for some of you to understand how this actually works behind the scenes so i will explain everything we do here in this function first of all i want to annotate this with experimental curtains api and then we can create that function so that will be an inline function if you don't know what inline is that is basically just a way to make a lambda function in kotlin more efficient because the compiler won't make that an actual function instead it will just take the code of the function and put it on the place where we call that function so that function doesn't have its own address for example our app would use to call that function instead it will just take the code of the function put it on the place where we call that and that will be a generic function because we want to be able to use that for all kinds of fragments and we use refined t which inherits from fragment so that generic typo meter here must inherit from fragment and refined just means that we can access the class information of that generic type parameter in this function so that is basically known at compile time then this function will be called launch fragment in hilt container and it will take a bunch of parameters here first of all the arguments for that fragment so fragment which is of type bundle nullable and we set that to null by default and then we have a parameter for the theme resource id which is of type integer and we set it by default to r dot style dot i think it's empty a fragment scenario empty fragment activity theme and now a little modification here in comparison to this function from the architectural samples is that we can attach a fragment factory to that so that just allows us to use constructor injection into our fragments because for that we need to define such a factory that is very similar to creating view models that we also need that viewmodel factory to use parameters for review model and with that parameter here we can just make sure that we pass such a factory so we can also use this function for fragments that have parameters and that is very useful for testing by the way so we use fragment factory here which is of type fragment factory and we also set this to null by default and then the last parameter will be along the function which we will just use to get a reference to the fragment that we launched in this hilt container and this lambda function will be a cross inline function that is just a keyword that makes a little bit more efficient if we have lambda functions inside of inline functions actually we we need that here so that will be called action here which will be on which will basically give us a reference of the type t here so our generic type doesn't take any parameters and we'll return a unit so nothing at all and we set that to an empty block of code by default and then in here we first want to create the intent that starts our actual activity in which we want to attach our fragment then and since this activity serves as the main activity here because that is the only activity in that running test case we need to make a special intent here so val main activity intent is equal to intent dot make main activity and you can see that takes a component name here which we can simply create and that component name takes the context and the class name of our actual main activity here so the context is actually just application provider dot get application context since we are inside of the android source set here we can use that and for the class here we will use our hilt test activity double colon class dot java and we also want to attach an extra to that intent so dot put extra the name or the key of that extra will be fragment scenario dot empty fragment activity dot theme extras bundle key and the value will just be our theme res id so we can just define the theme for that fragment and now we can use a so-called activity scenario which is very similar to the fragment scenario and then simply attach this activity and intent here for that activity so we just launch our hill test activity here and then instead of this scenario we will create our actual fragment and attach it so let's make a little space here and create that activity scenario so activity scenario dot launch and that will actually be a generic function so we need to specify our activity that we want to launch here which is hilt test activity and in the parameters here we simply pass the intent we want to start the activity with which is our main activity intent and afterwards we call a dot on activity so that will just give us a reference to this just started activity so you can see it is a hill test activity here and we can give this a name activity and in here we will then attach our fragment to this activity so first of all we want to attach our fragment factory that we pass as a parameter so we use um fragmentfactory.oled so we first want to make sure that this is not equal to null in that case we want to set our activity dot support fragmentmanager.fragmentfactory to it and afterwards we will use our activity dot support fragmentmanager.fragmentfactory again to instantiate our fragment here so for the class loader we will use preconditions from the android x core util package here and use check not null here and in here we will pass our t so our generic type our fragment double colon class.java.classloader and then as a second parameter here we need to pass the class name which is just t double colon dot class.java.name all right then this actually created our fragment and to get reference to that fragment we simply need to save that in a variable here so val fragment is equal to that and then we can use that fragment and set its arguments to the parameter we passed so to fragment arcs and after that we just perform a usual fragment transaction to launch that fragment instead of that activity so we use the activities support fragment manager and call dot begin transaction we want to add our fragment which has the container view android dot r dot id dot content and as a second parameter we need to pass the fragment itself and we don't want to attach a tag to that fragment then we want to call a dot commit now and then simply use our lambda function here our action function and call that with our fragments so that we have a reference to that fragment instead of our lambda function so i will actually convert our fragment so our fragment here to our generic type t and call a dot action on that and that is it for our function i know this is a little bit complicated here let's go through this one more time so we have that function launch fragment in hill container which will just take our fragment that we want to test and attach it to our custom hilt activity that is annotated with at android entry point we define that main activity intent here to just make this activity that we launched here a main activity because each application needs a main activity and in a test environment we don't have that by default so we specify that here and then we use that activity scenario to launch that activity with our intent and in the on activity function we get a reference to that activity we instantiate our fragment factory here so if we have one we will attach that if not it will just say null and then we create our fragment here and instantiate that basically we define the arguments of that fragment and then just launch that fragment with a fragment transaction and after that we call this action lambda function and inside of this lambda function we will have a reference to this fragment here so if that is a shopping fragment for example then we will have a reference to that shopping fragment and to actually test that i just want to go into our shopping dow test class here this is actually not the place where we need that but just to test that i will add a test case here add test function test launch container in on not launch container launch fragment in build container of course and in here we will just call that function launch fragment and hilt container and we want to launch let's say our shopping fragment and we just leave this block empty but as you can see we get a reference to the shopping fragment here so it will actually be much clearer what we need this for in particular when we write our actual fragment cases in the next videos that we will start with ui testing with integration tests with navigation tests and all that stuff with um mokito also so we will create marks you don't need to know what that is right now but just as a little teaser let's just test if this is working so if we run this test case and it does not crash that means it is working so let's just do that run the test case and i'm sure gradle will take ages again but i will see you soon okay let's see hopefully this test case passes here and yes it does so everything is working as expected i hope you liked this video and it was helpful for you also that i just um did not copy that function from github because i think it's it is actually important to understand how this works so you just get a feeling how all that fragment scenario stuff works how activity scenario works because we will actually not use activity scenarios in our tests because we don't have much in our activity to test but just that you know that this also exists and if you one day need to test an activity then use this activity scenario which will just launch your activity for fragment for for a test case not for a fragment and yeah just that you saw how this works if you like this video hit the like button if you love this video comment below and if you're not a subscriber of my channel then quickly do that click on subscribe and you will get android content every second day see you in the next video have an awesome day bye bye
Info
Channel: Philipp Lackner
Views: 20,791
Rating: undefined out of 5
Keywords: testing, android, test, test case, junit, mockito, mock, fake, stub, tdd, test driven development, intellij, android studio, unit test, unit testing, integration test, integrated test, end to end test, ui test, kotlin, mvvm, live data, coroutines, dagger, retrofit, room, database, roboelectric, android test, jvm, flaky test, development, programming, git
Id: k4zG93ogWFY
Channel Id: undefined
Length: 19min 4sec (1144 seconds)
Published: Sat Sep 05 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.