JUnit 5 Tutorial - Crash Course

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
j unit j unit t d d marco codes today we're gonna have a look at j unit five nice and easy unit tests integration tests tests of tests junit has you covered let's check it out as always i prepared a tiny sample project that we're going to use throughout this video you'll find the sources on github the link in the references it's a maven project it doesn't matter if you're using maven or gradle basically the same concepts apply i'm going to talk about the differences if there are any but for now what we're going to do is we're going to start off with the boring stuff which means setting up dependencies open up the pom xml file right and what you would need to do now is go onto the junit 5 homepage read through the documentation and find the maven dependency you need to add here actually i find the home page confusing the documentation confusing and i'm going to give you the shortcut the one dependency you need to add to your project either go to one of the popular maven search engines or just in your ide add a new dependency right and you'll need to find the j unit jupiter dependency quick caveat there's multiple junit jupiter dependencies so api engine power arms whatever you want to get the aggregator you don't have to do all of that if you're using a framework like spring boot which pulls in junit 5 automatically for you but for our case an empty project junit 5 aggregator set the scope to test in intellij add the dependency and don't forget to reload the maven configuration here in intellij wait a second until intellij pulls down dependency you can see it's here 5a2 perfect that's it with the boring stuff let's have a look at our project we have just one class inside the project it's a user record the user has a name he or she has an age the user can be blocked and the user has a birth date that's all there is and we're going to write some unit tests for that user so in good old maven speak you go to source test java inside the same package we're going to create a new class and call it user test class now one tiny point if you want to run those tests not just from inside your id but later on with maven from the command line those test classes they need to end with either test or tests otherwise maven won't pick them up so just call them user test so user blah for example won't work we're going to create that we have our test class and now let's just quickly create a new user user marco right age still 37 i'm not blocked and my birth date is local date now minus years 37 years something like that and we want to write some tests for that user what you do usually with your unit you start writing a test method so public void and then let's say user should be at least 18. something like that there's a couple of things to watch out for now now first of all every test method in your unit needs a test annotation so just make sure right add tests and get the rj unit jupiter annotation here if you don't have jupiter in the package name it means you have for whatever reason still junit four on the class path somewhere kick it out it's old it's legacy just use january jupiter equals 2.5 we're gonna add that second point is there's somewhat of a style guide with junit 5 that you write as few characters as possible inside of your test classes you get rid of all the waste which means get rid of the access modifiers public void users should be at least just remove the public same with the test class right i'm not saying it's a law i'm just saying that this is somewhat of the style some projects use and as we're talking about style already the question always pops up how should i call my how should i name my test methods should i camel case them should i start with tests that user should should be should i maybe have underscores by the way i myself users should be at least 18. i like my underscores again there's no specific law what is important is that you agree with your team members across all of your projects on the same homogenous style also you'll find a nice link to a stack overflow discussion on where multiple people told about their specific ways about the experiences with how to name test methods pros and cons whatever so go and have a read now our test isn't doing anything but let's check out if the setup is working correctly so i'm just going to run the test from inside intellij and as you can see right here is my test method user should be at least 18. the test runner is green because there was nothing that was tested inside so everything is fine here let me try something else let me try open up a terminal window right and we're gonna execute maven test here on the command line let's see what happens here so as you can see everything gets compiled and then there's the stage where the maven surefire plugin runs or tries to run tests but interestingly enough it says tests run zero no failures no errors and that is a tiny caveat a tiny problem you have with maven because depending on the maven version you're using maven uses old plugins to all plug-in versions to run tests and those old plug-in versions they don't yet understand junit 5. which is why you have to go to your pom-xml file you don't have to do that in greatly in the recent gradle versions you need to go to the build section and what i would recommend you do is first of all make sure that the maven surefire plugin here is a version i think it has to be higher than 2.20 or something i'm just going to go with the latest one at the time of this recording right maybe shaw fire executes the unit tests and new versions understand junit 5. maven compiler plugin i'm just going to put that here because you need a relatively recent compiler plugin version if you want to work with java 17 upwards so i just make sure i have these both two blocks inside my pom xml file i'm going to reload the changes and now when i rerun or when i open up my terminal maven test hopefully i should see that yay there was one test now you can see maven detected my user test class it ran one test one test method everything was fine everything was green and that is the biggest caveat to watch out for you don't have to do that if you're using spring boot for example because they pull in they configure these plugins for you automatically enough with the boring stuff let's write some tests so back to the user test class users should be at least 18. a big concept of june is you have assertions meaning what you want to do is assertions right and then you want to assert you have a so true a sort of array equals a third equals you have a couple of assert classes essentially a third false blah blah blah so what you want to do is for example let's see these should at least be 18 so search true that user dot age greater than or equals 18. that's the assertion obviously in most projects what you do is you add a static import for the methods for all these methods under assertions so the test is going to read like that we're going to run the test and now junit make sure that the user's age is obviously 18 if we put it to the user's age had to be 40 or whatever we would get our first failure message right so that obviously failed now the thing is there's also a cert equals for example so you could say assert equals user.name equals marco and so and so forth the thing is i'm not a big fan of junit's a surgery assert equals methods there's also another library you might have heard of it's called hamcrest it's only slightly better and in general what i would advise you to do is use one very popular assertions library in combination with junit5 that library is called srj which means what you need to do is pom xml file you want to add a new dependency i'm going to make it quick always you'll find the reference documentation down below with the link to the project the homepage documentation whatever but just for now i'm just going to paste in the dependency you need it's the srj car dependency in a relatively recent version which is 3.22 at the time of this recording now what happens we're going to go back to our test class we're going to delete these junit assertions we're going to make sure that the static inputs here are gone and then the entry point for srj is always the assert that method actually when you write assertions dot here let me just quickly rip there should be another import right tricky make sure you're not importing or junit jupiter api make sure you're importing org as a j core api right then you get an assert that method not answer that you are gonna put in the object inside a sir that and then the nice thing about searches you're gonna have you're now gonna have fluent assertions which means you just write dot and you're gonna get an autocomplete the autocomplete references methods that have to do with the object actually not let me not just put in the object user.h i'm gonna put that in here right and the methods i'm now getting only makes sense in the context of an integer so i'm going to get these methods here saying the age must be greater than or equal to greater than is close to is equal to is less than is even is odd is negative whatever i have all these methods here pretty damn cool so i would actually write something like is greater than or equal to 18 something like that again make sure static import rips like so and then i could have basically let's actually write two tests rips um usually at least b18 user should be marco that name doesn't make sense and that's a bigger topic actually how to properly not just from the style guide but actually how to split the tests what to test integration test unit tests i'm gonna make a separate video about that in the future but for now what we're gonna do is we're gonna say let's see user.name let's try the autocomplete and i have new methods here for example is my string base64 encoded no it's not it's my string does my string contain specific sequences only digits could does it contain a pattern a regex pattern blah blah blah all of these methods what i'm going to do is something like starts with ma does my name start with marco right it probably should we're gonna run that and as you can see two test methods they work perfectly fine now i'm gonna show you one more thing which is and now i'm just gonna put it all into the same test method because i don't want to duplicate methods here back and forth in real life i might have separate methods here but what we're going to do is assert that and then we're going to say user dot blocked right make sure that here you write is false or is true at the end right otherwise nothing is being tested so actually it should be is false and oh no actually let's try with this too so we're going to run that and we should get an error because marco is not blocked as you can see you'll get an error message saying that expecting value to be true but was false expected true false and it's a bit hard to read and that is why a search j has a different another concept it's called as and let me show you how that works so what you're going to do is you can say that user and placeholder should be blocked right you're going to put in my name inside here and now let's see what that line actually does we're going to rerun our test and now you get not just the expecting value to be true but was false true false you actually also get in the output a nice little error message saying user marker should be blocked and it kind of helps you especially with these boolean comparisons it helps you quickly understand what's happening if the test method has a completely different name for example it's part of a bigger test right you can play with the with the adding the descriptions just make sure you're not adding a description after that is true because then it won't have an effect at all so what we're gonna do is we're gonna comment this one out here and talk about one last tiny thing because we have been talking about how to name test methods here and whatever if you want to be passive aggressive you don't like what your team members did what you can always do is instead of you know changing these names you can always add the add display name annotation here user should be at least 18 whatever let's see what now happens we're going to rerun the test and actually you can see the only thing that changed now is the test method has the same name and we just overwrote it here in the output and intellij's test runner also mayman's testrunner with a new display name use that feature sparingly whenever you have a ver very complex name that for whatever reason you might transfer to something more human readable other than that just go with a homogenous style across your project all right so you can play with the assert that methods try it out with your own objects with your own types and whatnot i'm going to show you two more things now with search a because usually nowadays what you also do is you want to assert json objects maybe and you want to assert xml we're going to start off with json and a search a has a nice integration with another library which lets you do json assertions what does that mean first of all setup you need new dependencies as always you'll find them in the references but for now i'm just going to make it quick and easy i'm going to paste in a couple of dependencies here the dependency you want to be using is called json unit srj it brings in the json unit library which lets you work with json in tests and a bridge between json unit and assert j also you'll need a library jackson in my case that converts java objects from n2.json and jackson is a popular one also being used in springboot so you have to add these three dependencies here unless you for example are already using springboot what can you do now now when i go back to my test class and again i'm going to put everything into the same method so to not have to switch back and forth between different message methods and streams and whatnot but you now have a new method the method actually the class first so there's a class called json assertions it has a method assert.json the as always where you can do add a static import so you just can write write it like that what you can do is for example put your user object your java user object inside that method assert that jason again fluent methods you can have a look at them yourself what i'm going to show you is the simplest one is equal to imagine we want to compare our object against the json string for example i'm just going to you know put a string in here and as you can see the json string is named marco age 18 which isn't quite right blocked false is true but born probably also isn't quite right so we're just going to run that and we're going to see what happens what the library what assert that json will actually do now and what it's going to do is as you can see is it converted my user object my java user object to adjacent string and said hey this is the actual string h37 right so that's gonna change that here uh blocked false seems to be fine birthday is a different birth date thank god it's on my real birth date right false and bond that's okay let's rerun our test whoops no still not working because yes born birth date so what we're gonna do is birthday gonna put that here let's re-run it and now everything should work as expected that is pretty cool you don't have to worry about the ordering of the json properties it will also work in a different order but just for quick tests json tests json unit great library check it out in combination with a search a even better next step we had a look at jason now let's have a look at xml right as always you know the drill we need some libraries libraries means let me put another library down here as always documentation links whatever in the references the library is called xml unit srj it will pull in xml unit helps you test against xml and the bridge to assert j there's one tiny caveat the problem is the xml unit's library brings in transitively pulls in a bytebody which is another tiny java library doesn't really matter the problem is the the version it pulls in is too old to work for example with java 17. so what you'll have to do again is only if you're not using spring boot for example you have to make sure to put this block in here reference the section the block is called dependency management all it does essentially by the way let me just delete this here is it sets the dependency versions of transitively pulled in byte bodies to a specific new version that is able to handle java 17. it's a lot of infrastructure code but if you don't do that you won't be able to run your tests which is rather stupid so but that is everything we need and we can finally go back to our user test class you might have guessed correctly let's add a new line xml assert right that's your new friend i said that so compared with the json unit library you now are using a cert that again which clashes with the assert that method from a sir j the usual one so you'll have to put xml asset the full name here xml sort of thought that then you could put your object in inside here you know the drill that your object would get converted to xml whatever i'm just going to paste in a very simple xml document in here a has a child b with an attribute abc what we want to do is we want to do some xml stuff more specifically we want to use xpath to find out if the path a b and an attribute actually exists so it's just a check not even for the value but just that the document looks like a with the child b with an attribute abc and we can call exists right here so we're just gonna run that and we can play with that hopefully that worked as you can see everything is green everything is perfect if you don't believe me and if you're new to xpath and whatever let me just show you a failing test here you can see right this document is not valid because there is no c child basically of a and that's how you would use a search a together with xml assert okay we talked enough about assertions up next junit life cycle what a big word now let me show you what that actually means so you have in general you have test methods in junit you also sometimes have the need because you want to set up a database for whatever or in general it's called setting up the tests fixture like the user object here you might want to run something before a test runs to set something up and then after test run to you know clean up stuff and it might be that you want to run stuff you know once for every test method or once for all test methods very simple before each again drop the public void setup right what you can do is we're just going to say that now my user is being initialized inside that method here and actually i'm just going to say setup was called just to find out how often setup was called now when we run this after each again avoid cleanup for example just make sure you agree with all your teammates what those methods are called homogenously again across your whole project we're just going to set the user null even though it doesn't make any sense here and even recreating the user here all the time doesn't make any sense it's always the same record essentially so we have setup was called cleanup was called and we're just gonna run the whole test class let's see what happens what you can see is right we have two test methods so test one test number two and twice we get setup was called clean cleanup was called setup was called cleanup was called as we expected if you just want to run it once before all right makes sense after all tiny caveat the method now needs to be static also the after all method needs to be static otherwise it won't get executed it's quite an we know a tricky thing actually now you need to make everything static the user static setup cleanup static when we run our tests you can see everything was only called once and that makes sense in general for stuff like for example if you want to set up a bootable docker container across you know one test class or all the test classes you might just want to do it once per test class not for every single test method right i'm just going to put it back to before each here just a bit of you know exercise for you after each without static like so let's continue with the cool features well parameterized tests what does that mean so imagine you want to for whatever reason you have a list of friends right and you want to make sure that all of my friends all of marco's friends here they are also at least 18. so you would write a test method like void um all friends should at least be 18 something like that and then you could have a list of friends inside here you could write a for loop over the list of friends and do your assertions here the problem is it's really ugly especially the for loops because you're losing track if some if the fifth element failed inside a test it's really tricky to understand where does something fail because of what a much nicer way is actually to tell the unit about the list of friends and let your unit basically duplicate the test method for every friend and run it for every single friend and that case your output your test output won't get jumbled up the way you do this is what you have to do is you have one annotation parameterized tests the next one is to tell junit about the list right about the values you want to put into the test method let's start off simple let's imagine you don't have a list of friends you just have a list of ages integer ages like so and inside value source you can actually put in strings booleans charts doubles whatever arrays so what i would do now is i would say i have a friend who's 20 a friend who's 50 a friend who's 80. that's just how we specify the list inside the math method assert that age is um no not ages into age is greater than or equal to 18 something like that let's run it let's see what happens in the output you can see that now actually there's one big test method all friends should at least be 18 and you get several outputs for 20 for 50 for 80. pretty damn cool well the thing is actually with such a simple value source it's not that cool but i'm going to show you more sources csv source what you could do is inside that annotation here put in the csv file yeah well it's not the greatest idea in my you can do for very simple csv files by all means but there's a different annotation csv file source as you might have guessed here you have you can specify csv files thank god i have a csv file under source test resources right so i'm just going to reference that friends.csv let's have a look at the csv file uh it has name age first line lisa 20 hans 30 hana 40. going back i want to do one more thing i want to skip the first line from the csv file and that's enough for junit to read in every line and create a new test method for every line in the csv file but first what we want to do is string the first column is the name of the friend the second column is the age right name age we have that and we're just gonna say that age is greater than or equal to 18. let's run our test and see what happens right you can see all friends should at least be 18 there was one test for lisa 20 one for hans 30 one for hannah 40. pretty damn cool let's wrap it up because the other one useful sources the built-in source is enum source if you have an enum with 20 different values and you want to test that enum against all the values essentially then just use an enum source and put your enum class inside here right other than that play with parameterized tests write your own sources super powerful super cool let's take that concept one step further taking it one step further means creating new tests through code sounds crazy it's not that crazy you want to use another annotation at test factory isn't that cool every method that is annotated with a test directory needs to return a collection or a stream of so-called dynamic tests these are tests you create through code that we now have to create through code um i'm just gonna sell dynamic tests created through code what a crazy name for that method so at the end of it what you want to do is obviously you want to return your new tests what could the possible use case be well i have two xml files under source test resources and these test files essentially uh users user id one marko uses two id5 lisa doesn't matter what i want to do is for every file inside here if i add new ones i want to create a completely new test that's what i want to do so what you now need to do is is to read in those xml files convert them to strings i wrote the tiny util library it's called resources which you can check out in my project again link below which reads in those files gives you the file name and gives you the contents as uh strings essentially right you can simply call it i'm just gonna make it quick what you can do is you can say resources to strings which will check out you know the folder here and the pattern we're going to put in is i just want to get everything and this is a regex pattern now so what you want to do is users and then whatever some characters can be there and it should end with xml this gives me back a tiny helper class xml which is a stupid class that just wraps the file name and the file's contents right and what i then want to do is i want to do i don't want to return the an empty list actually what i want to do is i want to trans transform these xml files to something else what do i need to return well at the end of it we want dynamic tests so we're just going to go dynamictest.dynamictest dynamic test needs two things it needs a display name right what should the test be called in that lower left corner here in intellij and then what should the test actually do the display name is going to be the xml's name that i can just you know get from my tiny wrapper object and then the test also needs to do something which might look a bit dirty because it's essentially a nested lambda here before we fill in what our test should do let's do two things first of all we need to return the list down here we can delete this down here we can actually make it look a bit nicer without the right without the return statement here that's a lot of kind of crazy codes but for now let's see what should our dynamic test do i'm going to take a quick shortcut what i'm going to do is i just pasted in an xml assertions right what i want to assert is that xml dot content the content of x of our xml has an x path you know that from before users user name right a lot of code check it out especially with the sources on github make sure that the code compiles and now let's finally fire up this test class and see what happens as you can see everything ran you have you know from before the value sources csv file source and normal tests and now you have dynamic tests created through code there's two test methods that run one for user one xml one for user two xml if we added a third xml file for now let's just quickly do that i'm just gonna copy and paste that here right like so you should see that our user test class when we rerun it automatically gets a new test method and it does you can see it's here that's actually super cool so for files network calls whatever database entries maybe depending on if you're not abusing it it is actually quite cool to have the ability to create dynamic tests all right let's continue with a tiny with a simpler topic especially later on if you want to run your tests for example on the ci cd server like team city jenkins what have you you want to most often split up between unit tests integration tests whatever what is quite cool with j5 you just basically essentially you tag your test you just say this is a unit test for example this is an integration test you can tag whole classes you can tag methods whatever have you and at the end of it when you're running maven then later on on your ci cd system you can say maven tests only the test classes that have the tag integration there only that have a tag unit there and i'm going to show you the commands as always in the references section on how to do exactly that but it's actually a good practice to at some point start tagging your tests especially your classes the bigger picture not just write tests for only one year and after a year you know having to find out oh what's an integration test what's the unit test what is that test yeah so get into the habit of also correctly tagging uh your tests and you can also nest a test can also have multiple tags you can nest them so this is just a tiny reminder before we finish up with the last exciting topic that's right last topic for today now what happens if you want to integrate your unit with not just assertion libraries but other libraries that have something to do with testing for example mojito which can mock objects or pack whatever just different libraries or spring boot for example also specific testing libraries then what you do in junit 5 first of all imagine we want to integrate with with more keto this is not going to be a video on mokito itself there's a new video coming out soon about mokito but for now what you want to do is go to your pom xml file add a dependency as always you want to add you'll find the dependency in the references the mojito g-unit jupiter dependency it pulls in mojito it also pulls in the bridge between mokito and junit what you now can do is the following you can go back to your test class imagine for whatever stupid reason you wouldn't want to set up your user here you want to mock your user and in mokito speak you do that with an annotation called add mock the problem is when you now run the test that your unit doesn't understand the mock annotation it won't do anything with the use it won't initialize the user so what you actually get here in these two test methods you get a null pointer exception as expected because the user doesn't exist what you want to do is you want to tell junit hey have a look at the unit at the mokito annotations you do that with the extend with annotation makito extension.class make sure the input is right and then this is enough to tell junit hey please have a look enable the mokito integration if you used to springboot for example you would see the spring extension i think it's called pack extension does for all libraries third-party libraries you'll find extensions nowadays and that's all you need to do so when we now run this we still get an error message but now the error message is because mokito wasn't able to mock our user record because it's a final class which is absolutely fine for our use case because it tells us moki to actually try to mock the user for the test so it got enabled and it actually is working that is just the final concept i wanted to show you not going too deep into mokito into any other third party libraries but when once you see it extends with always enables integrations with third-party libraries that brings us to the end of it that was the introduction into the whole junit 5 universe as always you'll find further reading links in the references section but down below other than that i hope that was the quickest easiest nicest possible way of learning all the stuff the most used stuff on your day-to-day work whenever it comes to junit 5. thanks for watching and sayonara
Info
Channel: Marco Codes
Views: 19,310
Rating: undefined out of 5
Keywords:
Id: 6uSnF6IuWIw
Channel Id: undefined
Length: 35min 51sec (2151 seconds)
Published: Wed May 11 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.