JUnit Jupiter Extensions: Writing End to End Tests
Marbaise, Karl Heinz
You can write unit- and integration tests in different ways, though sometimes you need to write higher level tests, such as end-to-end tests, which are often hard to write. This talk will show you examples of how to write end-to-end tests by using JUnit Jupiter Extension mechanism with the support of Testcontainers and, as a foundation, Spring Boot, in a convenient way. By using frameworks like Spring Boot, there is already very good support within the framework to write tests and integration tests but if you have to deploy not on the Cloud (like DC/OS, K8S etc.), you might have issues to create an appropriate test environment. In particular, if you have several participants needed to get your application correctly running. So, based on JUnit Jupiter, Testcontainers, and some Java code, you can write real end-to-end Tests which are very helpful in several ways. It makes it more or less easy to create end-to-end Tests which can be run via your IDE also. There are several aspect which need to be taken care of, like how to synchronise the application and your test code, and so on.
 Hi to all, this is Kaleinsmoves speaking with a lecture about JUnit Jupiter extensions writing and turn tests. If you like to contact me, you can do that via Twitter, via GitHub or via email. Take a look on this slide. So some words about me. I'm working as a German freelancer, working with different technologies like Java, Ducca, Jenkins, Spring Mode and so on, as you see on the slide. So the agenda for this lecture is about the example application. We would like to enter and test how it looks like and so on. Then we will see how JUnit Jupiter platform looks like, how to use it. Writing some unit tests with JUnit Jupiters, what are the details here and what is needed or not. And then finally we start with the running end to end tests, what is needed to do some real end to end testing. And then we start writing an extension with JUnit Jupiters to run those end to end tests. And finally we have a Q&A session. At the beginning we need some example application which we like to do some end to end testing on. I have decided to use a Spring Mode application, very simple one of course, for simple purposes, has two REST API, end points and things like that and writes to a database. In this case I am using a Postgres database and we will take a look into the code later. Just going to the code now. So here in the code we are starting with the Spring Mode application, main application part, which is very, very simple here in this case. We have two controllers here, we have a simple Hello World controller, call it Hello Controller, very easy. Then we have an employee controller, we have two different end points, one which is create and one which is lists. The first one create will create with each call, with each post call, a single entry into the database. Where the lists endpoint will just list all the entities which are existing in the database. So this is the connection to an external service which we need to run our application here. We have of course our employee entity defined, it's very easy, very simple, very basic, nothing fancy here at the moment. So we are starting that and we have now an application. I have to decide a little bit larger project which comprises of three different modules, application E2E and extension. About E2E and extension we will talk about later and now we will start and take a look at how the JUnit Jupiter platform looks like. So now we are taking a brief look into the architecture of JUnit Jupiter platform which comprises of two parts here in general. There is an engine, usually the JUnit Jupiter engine in the middle, written in green, and the platform. Those two parts are communicating with which it's other. So we have the JUnit Jupiter engine which is responsible for finding tests, identifying what a test is and how it looks like and then give back the information to the platform which then uses the engine, the platform runner to execute the tests. This decouples completely the IDEs and build to's from the identification of tests and finding out what a test is. Sometimes it's useful or necessary to filter out some of the found tests but based on particular filtering criteria that can be done as well and then you have a complete decoupling via the platform from the engine and your real tests. If you take a look on the left side you see the vintage engine which is responsible for running the JUnit4 tests and identifying them of course and that means you can combine JUnit4 and JUnit5 tests into a single project which is possible, very convenient for migration from JUnit4 to JUnit5 only tests. So on the right side you see another option that you can implement your own test engine, identify tests and things like that and if you have done that correctly via the interfaces which are existing in JUnit platform then you have automatically integration into your IDE and build tools without supplemental implementation. So that makes it very easy and very comfortable to use that engine. First of the time you don't need to implement your own engine but it is possible here. So going to unit test. Okay into the unit test. So we have here a simple unit test which contains two unit tests like first test and second test which are annotated with the appropriate test annotation. So sometimes it happens that you need to do before each test something set up something or initialize something. That can be done and simply achieved by using it before each annotation with an appropriate method. If you need to do something going down after that you have to use after each with the appropriate method. If you have a necessary set up to be done before all the tests only once then you can use before all which needed to be a static method based on the part that the instantiation of the test class in this case the standard test class is done for each test separately. So you have to do that into before all or after all if you're going down with something like that. So that would be the first. So now let's assume and think about what is needed to do run really end to end test. We have to start all the parts which are needed for our application for example a database or Redis or something like that. Then we have to of course start the application itself. Then we need to do the testing whatever we do accessing the database accessing rest end points or whatever. And then of course finally we need to shut down the application itself. And last we have to shut down the other parts like the database or Redis or whatever we are using here. Okay now let us translate these steps into real annotations which are used by JNitubitor. You see the part before each we need to start up all the components start up the application. Then doing the test in the real test method which is annotated via a test of course. Then after each we have to shut down the application and shut down all the components we have. If we think about that approach it becomes clear that you are writing a lot of code inside your before each and in your after each method for starting up the application for starting up the dependencies like database like Redis or something like that. And for the after each method as well. So it becomes not very clear where the real test code is and it is a little bit cluttering the test cloud with application components set up shut down code and so on. But on the other hand it is very easy to do so. So we might think of a better approach here. A good solution could be using JNitubit's opportunity to do some so called extensions. JNitubit has a lot of extension points where you can intercept different calls of different methods for example the before each, after each, before test or something like that. Let us take a look on a more clear diagram. So if you take a look here in a simplified order of the calling you have different calls like before all callback or before each callback or before test execution callback and then the parts which are marked orange here. That means the before all callback method is called before the before all of the user code. And furthermore the before each callback is called before the before each call of the user code in your test. So we can intercept these different points here and get an interception into that and for example start with an before each callback and start our application for example and the needed parts here. And in the after each callback we can do the same thing here and say we stopped our application and the needed parts. So that's the way we will go and we will start with that and you will see to go via an extension which is using these things here. So now we are in the code. I'm starting with the example of my end to end test as an example. Here you see the test method which I have defined and which is usually annotated with the add test annotation. But you see also an annotation which you can't be familiar with because I have defined it on my own. This is the e2e test annotation which I have defined here for running these end to end tests here. That looks like this here and usual annotation part where I'm allowed to put that. And a very important thing here is this is the part where to enter the area of junit. This is the definition of a class which we can look at. Sorry, just go back and then you see the class which I have using here is e2e extension. We are starting with defining some callback interfaces here. Let's start with a before each callback. That's simply an interface which contains only a single method which is the end to point before each as we've seen on the previous slide. If we take a look how it is implemented here, we start with the database container as described in our lectures here. Then we start our application in several ways here. I will explain that later in more detail. Then start up and wait until it's started up. The same thing we have to do is to down the application which is done by using the after each callback here. After each, then we stop the database here. This could be much more than just only as database that you can use and stop any of the components you have used here. And then I'm ready with that. So let us start that on the command line first and see how it works. That will take a few seconds here. I'm creating here a multi-module build already set so and running that without why a supplemental profile here. We will take a look later at the configuration how it looks like. Okay, start it and you see 13 seconds or something. So let us take a look what is really happening here. I've created a separate module where I have the real test here and I have a directory created here and you see that there is a method name or directory called first test. And in that directory you will find the application standard out. That means the startup of your complete application, in this case the Spring Boot application with all the information you need. I have standard error which should be usually empty. I have created a GC log which is the case because I have configured that into the startup information on the command line of the application. And then we can take a look how that really works. If you want to know some other stuff here, you can take a look because I'm using Maven here in the failsafe report directory. There is an output text file that represents all the output which has been created during the startup session here. There you see the startup here of Docker test containers here. Let a Docker container start it. And if you're going down there then you see somewhere here is the startup of the Postgres database. And you see this Postgres database. So it's downloading that. Take some time here. And we go downstairs here going down through the file. And then we can see somewhere the check if our container is running. That means if our application is running. We are using the health check of the application and just pulling that against that and waiting until it's really ready. Because most of the time most of these services today have a health check and can be easily used for such things here. If it is up there then we know it's up there. And then you see the system output which I have put into the example output here as described here. So now how that looks like all the stuff are working in the extension. So we have two more things here which are very important. The database container is one of them. That means we have a class here very simple which contains a start method which uses test containers to start up a Postgres image here. We are exposing a port. We are trying that several times to be a little bit more safe. We make a timeout in cases there is going something wrong. Then we are defining some environment variables to define the username, the password and the database we are already using. Then delegating to test containers. We do the same thing for the stop method and some other things here. Then we can use that and start up that in our extension and then simply wait until it started. That is a synchronized call in this case here and we wait until the database container is completely started up. You can start in that way for example other stuff if you like like in Redis or whatever you like here that would be possible as well. So we created some directories where we store the resulting standard output information and things like that. That is created by these constructs here. We are getting some information from the context here and creating based on that directory names. So we can create the storage here in a supplemental directory to make a separation between each test run. Then we have application parts which is important to create different TCP iPorts. Simply using that and using the socket details from Spring Framework and we getting two different ports here and defining two different ports. One for the application, one for the actuator here. The actuator is exactly the point with the health check here. Make them separate and give them back to my extension here. So in this case we are using the ports, creating them and then putting them on the command line to define the server port and the management port. And also getter methods have been defined in the database container to get the information from the URL, how it is produced and how it is created. One word, supplemental word about the mapped port. If you start a container on the command line and use a dash uppercase p automatically a TCP iPort is selected which doesn't definitely not clash with other ports. If you do that manually and do that twice you will get a clash between the ports. So that can clash sometimes and usually you should let Docker do that here in that way. Then we have started up our application. So one thing is needed to do is to start up our real application here. And that's why I created this application executor called. That's getting the information of the directory where we should start inside there and how it is named the application. Technically it's not doing something special it's just starting up and using the Java home with the JVM. Just checking if the existence of the file is there. You see there are the JVM arguments. That's the reason why we get in GC log or something like that. That can be changed of course for your requirements. Adding some supplemental calling arguments like the Java dash jar or something like that. That's the start up arguments for Spring Boot. Printing them out on the logger to have more information in cases of errors. And then starting via a process builder this part here redirecting error and setting a working directory here for that. And after that you can start the application and wait until it's up there. The thing here is the is ready is simply using the rest endpoint meaning there are actuator help and check endpoint to wait until it's available application. That can be done in different ways depending on your application. This one I think works with mode of the creation because many many applications today use our actuator for something like Prometheus or something like that as endpoints. And you can use the health check endpoint for that to wait until the application is started. Going down after each as well here we wait doing shutting down the database and awaiting until the database is down. The application is shut it shut it down by a kill signal and then we wait until it's completely down. The details I will take a look later. So the result is that we get the information here from the output all the time here and I can see that it started up correctly. And if I take a look into the output which is created by the show a face if plug in then you see all this stuff and the health check is done and also the information is printed out that my method has been executed. So one thing here is if I do that that way here I can very easily start up a second test without a big issue here. Just simply doing that and if I do that a second time here now that will take some time of course. So that takes a few seconds here to start up that part here. So you see it take 24 seconds because we have started two tests that means the complete application plus the database container has been started up completely two times just sequentially but that has been done. So you can see the results here one application startup lock here and a second one is now created and you see the same thing here. Now you have the possibility to intercept the information and the startup of the application via the usual setup which is already there via before each so we can intercept that and start in before each doing some special stuff if we like to for each of our application that means for two of them in this case I do that here and just calling the rest endpoint of my application here and just creating supplemental entries for the database here just calling the create endpoint here. I'm just printing out something for real use case you should do something different instead of printing out something. So I can rerun that and then you can see the result of that. I will take a number of seconds these are 30 seconds around 30 seconds which takes the time here yes so you see 25 seconds and then you see things like that and then you can see in the output here that if you take a look in that you see the health check here then the running here or in the lock file that will take a time and then you see that the health check of the second call is done here and you see that the create endpoint is called several times here. Furthermore it's also called the list endpoint here is called and you can see that the information is giving back from our application so you can use that for whatever purpose you like. So furthermore there are some special things here to get that internally correctly running here, there are is a so called internal storage which is used here that I said before. I store that here into a so called context here which is provided by the JUnitDupeter framework. Internally this store is more or less a hash map which is synchronized and you can use that in multi-threaded environment. You should use also a namespace for your own extension usually in the name of the class including the package name and so on and then get that store and into that store you can put information. There is a big difference here you can use that and you should use also a unique ID that is a combination of the engine which is run like JUnitDupeter and things like that and thus this information includes also the method name that means this information here is stored by using that which includes also the name of the method which is called for example first test, second test in our class including the class name of course. And that means you can store the information of the database container and that means in consequence that you can completely run this here in parallel so it is possible to do that if you do that correctly with the store here that you can run all the tests in parallel. That's a very good thing in particular if you run such complexity and time consuming end-to-end tests you can run them in parallel if you like but you have to be a little bit careful what you exactly store and how you exactly extract that. Usually there is no choice, no way to give some information from before each to after each and using a local variable is a bad way because it does not work and it's not thread safe of course and using that storage is the only correct way to do that. And then you can use that after each for example get an instance of the class database container for example to stop that also from the application process which is stored at the before each method as well. So this makes it possible to run them in parallel. One more thing is to support parameters here then you have to add an interface implementation which is called parameter, I'm not parameter resolver sorry and these things are used to check if the parameters of before each of the test method itself contains particular parameters which are supported and then you can check the parameters here for example application port, database port and put these informations from the storage into the methods which are returning that as an object here and that means it is possible to have these things here just writing that into your test and inject into your test very easily. So now let us little bit improve the speed here of that test here. So I have preferred something a little bit here and now I run that and you see that last time you run that that took about 25 seconds and now we have two tests running but they are running in parallel and you can do that much more than of them with much more tests end to end test if you like but that depends of course on your machine how many cores you have how much memory you use and things like that and how many components you are studying up or shutting down. And it can increase the speed a lot and you see I am coming down to the speed with 15 seconds as before but I am running two in parallel that is of course possible here as well. So you have a very easy way to separate that as you see I have implemented the extension into a complete separate module you can do that in the before each after each method yourself the first time but it is not a clear separation of concern and not clear separation of the code. I think it is better to use separate module for that because that makes it easier to extract that and put that into a separate project which can be easily reused somewhere else if you like. It is also further more possible to make different extension I have combined at the moment the extension for the database as well as the application. You can do and make separate extension of that for example one for the database startup and shutting down and a separate one for the application startup and shutting down and then you can make it more flexible to combine them but that depends on your use case here. So let us come back to the slides for example as I said different things here as well as possible. And finally I think if you have further questions the Q&A session will be coming there. I have some links about the documentation which I strongly recommend the user's guide. I have made a lecture last year on FOSSTEM as well about basics of JUnitDupeter, the user's guide, extension execution order and things like that and of course a link to the whole code which can be seen on GitHub here as well. Thank you very much and we will see in the Q&A session.
 Hi to all, this is Kaleinsmoves speaking with a lecture about JUnit Jupiter extensions writing  and turn tests.  If you like to contact me, you can do that via Twitter, via GitHub or via email.  Take a look on this slide.  So some words about me.  I'm working as a German freelancer, working with different technologies like Java, Ducca,  Jenkins, Spring Mode and so on, as you see on the slide.  So the agenda for this lecture is about the example application.  We would like to enter and test how it looks like and so on.  Then we will see how JUnit Jupiter platform looks like, how to use it.  Writing some unit tests with JUnit Jupiters, what are the details here and what is needed  or not.  And then finally we start with the running end to end tests, what is needed to do some  real end to end testing.  And then we start writing an extension with JUnit Jupiters to run those end to end tests.  And finally we have a Q&A session.  At the beginning we need some example application which we like to do some end to end testing  on.  I have decided to use a Spring Mode application, very simple one of course, for simple purposes,  has two REST API, end points and things like that and writes to a database.  In this case I am using a Postgres database and we will take a look into the code later.  Just going to the code now.  So here in the code we are starting with the Spring Mode application, main application  part, which is very, very simple here in this case.  We have two controllers here, we have a simple Hello World controller, call it Hello Controller,  very easy.  Then we have an employee controller, we have two different end points, one which is create  and one which is lists.  The first one create will create with each call, with each post call, a single entry into  the database.  Where the lists endpoint will just list all the entities which are existing in the database.  So this is the connection to an external service which we need to run our application here.  We have of course our employee entity defined, it's very easy, very simple, very basic, nothing  fancy here at the moment.  So we are starting that and we have now an application.  I have to decide a little bit larger project which comprises of three different modules,  application E2E and extension.  About E2E and extension we will talk about later and now we will start and take a look  at how the JUnit Jupiter platform looks like.  So now we are taking a brief look into the architecture of JUnit Jupiter platform which  comprises of two parts here in general.  There is an engine, usually the JUnit Jupiter engine in the middle, written in green, and  the platform.  Those two parts are communicating with which it's other.  So we have the JUnit Jupiter engine which is responsible for finding tests, identifying  what a test is and how it looks like and then give back the information to the platform  which then uses the engine, the platform runner to execute the tests.  This decouples completely the IDEs and build to's from the identification of tests and  finding out what a test is.  Sometimes it's useful or necessary to filter out some of the found tests but based on particular  filtering criteria that can be done as well and then you have a complete decoupling via  the platform from the engine and your real tests.  If you take a look on the left side you see the vintage engine which is responsible for  running the JUnit4 tests and identifying them of course and that means you can combine  JUnit4 and JUnit5 tests into a single project which is possible, very convenient for migration  from JUnit4 to JUnit5 only tests.  So on the right side you see another option that you can implement your own test engine,  identify tests and things like that and if you have done that correctly via the interfaces  which are existing in JUnit platform then you have automatically integration into your  IDE and build tools without supplemental implementation.  So that makes it very easy and very comfortable to use that engine.  First of the time you don't need to implement your own engine but it is possible here.  So going to unit test.  Okay into the unit test.  So we have here a simple unit test which contains two unit tests like first test and second  test which are annotated with the appropriate test annotation.  So sometimes it happens that you need to do before each test something set up something  or initialize something.  That can be done and simply achieved by using it before each annotation with an appropriate  method.  If you need to do something going down after that you have to use after each with the appropriate  method.  If you have a necessary set up to be done before all the tests only once then you can  use before all which needed to be a static method based on the part that the instantiation  of the test class in this case the standard test class is done for each test separately.  So you have to do that into before all or after all if you're going down with something  like that.  So that would be the first.  So now let's assume and think about what is needed to do run really end to end test.  We have to start all the parts which are needed for our application for example a database  or Redis or something like that.  Then we have to of course start the application itself.  Then we need to do the testing whatever we do accessing the database accessing rest end  points or whatever.  And then of course finally we need to shut down the application itself.  And last we have to shut down the other parts like the database or Redis or whatever we  are using here.  Okay now let us translate these steps into real annotations which are used by JNitubitor.  You see the part before each we need to start up all the components start up the application.  Then doing the test in the real test method which is annotated via a test of course.  Then after each we have to shut down the application and shut down all the components we have.  If we think about that approach it becomes clear that you are writing a lot of code inside  your before each and in your after each method for starting up the application for starting  up the dependencies like database like Redis or something like that.  And for the after each method as well.  So it becomes not very clear where the real test code is and it is a little bit cluttering  the test cloud with application components set up shut down code and so on.  But on the other hand it is very easy to do so.  So we might think of a better approach here.  A good solution could be using JNitubit's opportunity to do some so called extensions.  JNitubit has a lot of extension points where you can intercept different calls of different  methods for example the before each, after each, before test or something like that.  Let us take a look on a more clear diagram.  So if you take a look here in a simplified order of the calling you have different calls  like before all callback or before each callback or before test execution callback and then  the parts which are marked orange here.  That means the before all callback method is called before the before all of the user  code.  And furthermore the before each callback is called before the before each call of the  user code in your test.  So we can intercept these different points here and get an interception into that and  for example start with an before each callback and start our application for example and  the needed parts here.  And in the after each callback we can do the same thing here and say we stopped our application  and the needed parts.  So that's the way we will go and we will start with that and you will see to go via an extension  which is using these things here.  So now we are in the code.  I'm starting with the example of my end to end test as an example.  Here you see the test method which I have defined and which is usually annotated with  the add test annotation.  But you see also an annotation which you can't be familiar with because I have defined it  on my own.  This is the e2e test annotation which I have defined here for running these end to end tests  here.  That looks like this here and usual annotation part where I'm allowed to put that.  And a very important thing here is this is the part where to enter the area of junit.  This is the definition of a class which we can look at.  Sorry, just go back and then you see the class which I have using here is e2e extension.  We are starting with defining some callback interfaces here.  Let's start with a before each callback.  That's simply an interface which contains only a single method which is the end to  point before each as we've seen on the previous slide.  If we take a look how it is implemented here, we start with the database container as described  in our lectures here.  Then we start our application in several ways here.  I will explain that later in more detail.  Then start up and wait until it's started up.  The same thing we have to do is to down the application which is done by using the after  each callback here.  After each, then we stop the database here.  This could be much more than just only as database that you can use and stop any of  the components you have used here.  And then I'm ready with that.  So let us start that on the command line first and see how it works.  That will take a few seconds here.  I'm creating here a multi-module build already set so and running that without why a supplemental  profile here.  We will take a look later at the configuration how it looks like.  Okay, start it and you see 13 seconds or something.  So let us take a look what is really happening here.  I've created a separate module where I have the real test here and I have a directory  created here and you see that there is a method name or directory called first test.  And in that directory you will find the application standard out.  That means the startup of your complete application, in this case the Spring Boot application with  all the information you need.  I have standard error which should be usually empty.  I have created a GC log which is the case because I have configured that into the startup  information on the command line of the application.  And then we can take a look how that really works.  If you want to know some other stuff here, you can take a look because I'm using Maven  here in the failsafe report directory.  There is an output text file that represents all the output which has been created during  the startup session here.  There you see the startup here of Docker test containers here.  Let a Docker container start it.  And if you're going down there then you see somewhere here is the startup of the Postgres  database.  And you see this Postgres database.  So it's downloading that.  Take some time here.  And we go downstairs here going down through the file.  And then we can see somewhere the check if our container is running.  That means if our application is running.  We are using the health check of the application and just pulling that against that and waiting  until it's really ready.  Because most of the time most of these services today have a health check and can be easily  used for such things here.  If it is up there then we know it's up there.  And then you see the system output which I have put into the example output here as described  here.  So now how that looks like all the stuff are working in the extension.  So we have two more things here which are very important.  The database container is one of them.  That means we have a class here very simple which contains a start method which uses test  containers to start up a Postgres image here.  We are exposing a port.  We are trying that several times to be a little bit more safe.  We make a timeout in cases there is going something wrong.  Then we are defining some environment variables to define the username, the password and the  database we are already using.  Then delegating to test containers.  We do the same thing for the stop method and some other things here.  Then we can use that and start up that in our extension and then simply wait until it  started.  That is a synchronized call in this case here and we wait until the database container is  completely started up.  You can start in that way for example other stuff if you like like in Redis or whatever  you like here that would be possible as well.  So we created some directories where we store the resulting standard output information and  things like that.  That is created by these constructs here.  We are getting some information from the context here and creating based on that directory  names.  So we can create the storage here in a supplemental directory to make a separation between each  test run.  Then we have application parts which is important to create different TCP iPorts.  Simply using that and using the socket details from Spring Framework and we getting two different  ports here and defining two different ports.  One for the application, one for the actuator here.  The actuator is exactly the point with the health check here.  Make them separate and give them back to my extension here.  So in this case we are using the ports, creating them and then putting them on the command  line to define the server port and the management port.  And also getter methods have been defined in the database container to get the information  from the URL, how it is produced and how it is created.  One word, supplemental word about the mapped port.  If you start a container on the command line and use a dash uppercase p automatically a  TCP iPort is selected which doesn't definitely not clash with other ports.  If you do that manually and do that twice you will get a clash between the ports.  So that can clash sometimes and usually you should let Docker do that here in that way.  Then we have started up our application.  So one thing is needed to do is to start up our real application here.  And that's why I created this application executor called.  That's getting the information of the directory where we should start inside there and how  it is named the application.  Technically it's not doing something special it's just starting up and using the Java home  with the JVM.  Just checking if the existence of the file is there.  You see there are the JVM arguments.  That's the reason why we get in GC log or something like that.  That can be changed of course for your requirements.  Adding some supplemental calling arguments like the Java dash jar or something like that.  That's the start up arguments for Spring Boot.  Printing them out on the logger to have more information in cases of errors.  And then starting via a process builder this part here redirecting error and setting a  working directory here for that.  And after that you can start the application and wait until it's up there.  The thing here is the is ready is simply using the rest endpoint meaning there are actuator  help and check endpoint to wait until it's available application.  That can be done in different ways depending on your application.  This one I think works with mode of the creation because many many applications today use our  actuator for something like Prometheus or something like that as endpoints.  And you can use the health check endpoint for that to wait until the application is started.  Going down after each as well here we wait doing shutting down the database and awaiting  until the database is down.  The application is shut it shut it down by a kill signal and then we wait until it's  completely down.  The details I will take a look later.  So the result is that we get the information here from the output all the time here and  I can see that it started up correctly.  And if I take a look into the output which is created by the show a face if plug in then  you see all this stuff and the health check is done and also the information is printed  out that my method has been executed.  So one thing here is if I do that that way here I can very easily start up a second test  without a big issue here.  Just simply doing that and if I do that a second time here now that will take some time  of course.  So that takes a few seconds here to start up that part here.  So you see it take 24 seconds because we have started two tests that means the complete  application plus the database container has been started up completely two times just  sequentially but that has been done.  So you can see the results here one application startup lock here and a second one is now  created and you see the same thing here.  Now you have the possibility to intercept the information and the startup of the application  via the usual setup which is already there via before each so we can intercept that  and start in before each doing some special stuff if we like to for each of our application  that means for two of them in this case I do that here and just calling the rest endpoint  of my application here and just creating supplemental entries for the database here  just calling the create endpoint here.  I'm just printing out something for real use case you should do something different instead  of printing out something.  So I can rerun that and then you can see the result of that.  I will take a number of seconds these are 30 seconds around 30 seconds which takes the  time here yes so you see 25 seconds and then you see things like that and then you can  see in the output here that if you take a look in that you see the health check here  then the running here or in the lock file that will take a time and then you see that  the health check of the second call is done here and you see that the create endpoint  is called several times here.  Furthermore it's also called the list endpoint here is called and you can see that the information  is giving back from our application so you can use that for whatever purpose you like.  So furthermore there are some special things here to get that internally correctly running  here, there are is a so called internal storage which is used here that I said before.  I store that here into a so called context here which is provided by the JUnitDupeter  framework.  Internally this store is more or less a hash map which is synchronized and you can use  that in multi-threaded environment.  You should use also a namespace for your own extension usually in the name of the class  including the package name and so on and then get that store and into that store you can  put information.  There is a big difference here you can use that and you should use also a unique ID that  is a combination of the engine which is run like JUnitDupeter and things like that and  thus this information includes also the method name that means this information here is stored  by using that which includes also the name of the method which is called for example  first test, second test in our class including the class name of course.  And that means you can store the information of the database container and that means in  consequence that you can completely run this here in parallel so it is possible to do that  if you do that correctly with the store here that you can run all the tests in parallel.  That's a very good thing in particular if you run such complexity and time consuming  end-to-end tests you can run them in parallel if you like but you have to be a little bit  careful what you exactly store and how you exactly extract that.  Usually there is no choice, no way to give some information from before each to after  each and using a local variable is a bad way because it does not work and it's not thread  safe of course and using that storage is the only correct way to do that.  And then you can use that after each for example get an instance of the class database container  for example to stop that also from the application process which is stored at the before each  method as well.  So this makes it possible to run them in parallel.  One more thing is to support parameters here then you have to add an interface implementation  which is called parameter, I'm not parameter resolver sorry and these things are used to  check if the parameters of before each of the test method itself contains particular  parameters which are supported and then you can check the parameters here for example  application port, database port and put these informations from the storage into the methods  which are returning that as an object here and that means it is possible to have these  things here just writing that into your test and inject into your test very easily.  So now let us little bit improve the speed here of that test here.  So I have preferred something a little bit here and now I run that and you see that last  time you run that that took about 25 seconds and now we have two tests running but they  are running in parallel and you can do that much more than of them with much more tests  end to end test if you like but that depends of course on your machine how many cores you  have how much memory you use and things like that and how many components you are studying  up or shutting down.  And it can increase the speed a lot and you see I am coming down to the speed with 15  seconds as before but I am running two in parallel that is of course possible here as  well.  So you have a very easy way to separate that as you see I have implemented the extension  into a complete separate module you can do that in the before each after each method  yourself the first time but it is not a clear separation of concern and not clear separation  of the code.  I think it is better to use separate module for that because that makes it easier to extract  that and put that into a separate project which can be easily reused somewhere else  if you like.  It is also further more possible to make different extension I have combined at the moment the  extension for the database as well as the application.  You can do and make separate extension of that for example one for the database startup  and shutting down and a separate one for the application startup and shutting down and  then you can make it more flexible to combine them but that depends on your use case here.  So let us come back to the slides for example as I said different things here as well as  possible.  And finally I think if you have further questions the Q&A session will be coming there.  I have some links about the documentation which I strongly recommend the user's guide.  I have made a lecture last year on FOSSTEM as well about basics of JUnitDupeter, the  user's guide, extension execution order and things like that and of course a link to the  whole code which can be seen on GitHub here as well.  Thank you very much and we will see in the Q&A session.
endtoendtests_k_0_11747.png endtoendtests_k_11748_14183.png endtoendtests_k_14184_14303.png endtoendtests_k_14304_14579.png endtoendtests_k_14580_14627.png endtoendtests_k_14628_17843.png endtoendtests_k_17844_23663.png endtoendtests_k_23664_23723.png endtoendtests_k_23724_23843.png endtoendtests_k_23844_28091.png endtoendtests_k_28092_30899.png endtoendtests_k_30900_32147.png endtoendtests_k_32148_32303.png endtoendtests_k_32304_34895.png endtoendtests_k_34896_35711.png endtoendtests_k_35712_37703.png endtoendtests_k_37704_37787.png endtoendtests_k_37788_38567.png endtoendtests_k_38568_40151.png endtoendtests_k_40152_41075.png endtoendtests_k_41076_41222.png
Hi to all, this is Kaleinsmoves speaking with a lecture about JUnit Jupiter extensions writing and turn tests. If you like to contact me, you can do that via Twitter, via GitHub or via email. Take a look on this slide. So some words about me. I'm working as a German freelancer, working with different technologies like Java, Ducca, Jenkins, Spring Mode and so on, as you see on the slide. So the agenda for this lecture is about the example application. We would like to enter and test how it looks like and so on. Then we will see how JUnit Jupiter platform looks like, how to use it. Writing some unit tests with JUnit Jupiters, what are the details here and what is needed or not. And then finally we start with the running end to end tests, what is needed to do some real end to end testing. And then we start writing an extension with JUnit Jupiters to run those end to end tests. And finally we have a Q&A session. At the beginning we need some example application which we like to do some end to end testing on. I have decided to use a Spring Mode application, very simple one of course, for simple purposes, has two REST API, end points and things like that and writes to a database. In this case I am using a Postgres database and we will take a look into the code later. Just going to the code now. So here in the code we are starting with the Spring Mode application, main application part, which is very, very simple here in this case. We have two controllers here, we have a simple Hello World controller, call it Hello Controller, very easy. Then we have an employee controller, we have two different end points, one which is create and one which is lists. The first one create will create with each call, with each post call, a single entry into the database. Where the lists endpoint will just list all the entities which are existing in the database. So this is the connection to an external service which we need to run our application here. We have of course our employee entity defined, it's very easy, very simple, very basic, nothing fancy here at the moment. So we are starting that and we have now an application. I have to decide a little bit larger project which comprises of three different modules, application E2E and extension. About E2E and extension we will talk about later and now we will start and take a look at how the JUnit Jupiter platform looks like. So now we are taking a brief look into the architecture of JUnit Jupiter platform which comprises of two parts here in general. There is an engine, usually the JUnit Jupiter engine in the middle, written in green, and the platform. Those two parts are communicating with which it's other. So we have the JUnit Jupiter engine which is responsible for finding tests, identifying what a test is and how it looks like and then give back the information to the platform which then uses the engine, the platform runner to execute the tests. This decouples completely the IDEs and build to's from the identification of tests and finding out what a test is. Sometimes it's useful or necessary to filter out some of the found tests but based on particular filtering criteria that can be done as well and then you have a complete decoupling via the platform from the engine and your real tests. If you take a look on the left side you see the vintage engine which is responsible for running the JUnit4 tests and identifying them of course and that means you can combine JUnit4 and JUnit5 tests into a single project which is possible, very convenient for migration from JUnit4 to JUnit5 only tests. So on the right side you see another option that you can implement your own test engine, identify tests and things like that and if you have done that correctly via the interfaces which are existing in JUnit platform then you have automatically integration into your IDE and build tools without supplemental implementation. So that makes it very easy and very comfortable to use that engine. First of the time you don't need to implement your own engine but it is possible here. So going to unit test. Okay into the unit test. So we have here a simple unit test which contains two unit tests like first test and second test which are annotated with the appropriate test annotation. So sometimes it happens that you need to do before each test something set up something or initialize something. That can be done and simply achieved by using it before each annotation with an appropriate method. If you need to do something going down after that you have to use after each with the appropriate method. If you have a necessary set up to be done before all the tests only once then you can use before all which needed to be a static method based on the part that the instantiation of the test class in this case the standard test class is done for each test separately. So you have to do that into before all or after all if you're going down with something like that. So that would be the first. So now let's assume and think about what is needed to do run really end to end test. We have to start all the parts which are needed for our application for example a database or Redis or something like that. Then we have to of course start the application itself. Then we need to do the testing whatever we do accessing the database accessing rest end points or whatever. And then of course finally we need to shut down the application itself. And last we have to shut down the other parts like the database or Redis or whatever we are using here. Okay now let us translate these steps into real annotations which are used by JNitubitor. You see the part before each we need to start up all the components start up the application. Then doing the test in the real test method which is annotated via a test of course. Then after each we have to shut down the application and shut down all the components we have. If we think about that approach it becomes clear that you are writing a lot of code inside your before each and in your after each method for starting up the application for starting up the dependencies like database like Redis or something like that. And for the after each method as well. So it becomes not very clear where the real test code is and it is a little bit cluttering the test cloud with application components set up shut down code and so on. But on the other hand it is very easy to do so. So we might think of a better approach here. A good solution could be using JNitubit's opportunity to do some so called extensions. JNitubit has a lot of extension points where you can intercept different calls of different methods for example the before each, after each, before test or something like that. Let us take a look on a more clear diagram. So if you take a look here in a simplified order of the calling you have different calls like before all callback or before each callback or before test execution callback and then the parts which are marked orange here. That means the before all callback method is called before the before all of the user code. And furthermore the before each callback is called before the before each call of the user code in your test. So we can intercept these different points here and get an interception into that and for example start with an before each callback and start our application for example and the needed parts here. And in the after each callback we can do the same thing here and say we stopped our application and the needed parts. So that's the way we will go and we will start with that and you will see to go via an extension which is using these things here. So now we are in the code. I'm starting with the example of my end to end test as an example. Here you see the test method which I have defined and which is usually annotated with the add test annotation. But you see also an annotation which you can't be familiar with because I have defined it on my own. This is the e2e test annotation which I have defined here for running these end to end tests here. That looks like this here and usual annotation part where I'm allowed to put that. And a very important thing here is this is the part where to enter the area of junit. This is the definition of a class which we can look at. Sorry, just go back and then you see the class which I have using here is e2e extension. We are starting with defining some callback interfaces here. Let's start with a before each callback. That's simply an interface which contains only a single method which is the end to point before each as we've seen on the previous slide. If we take a look how it is implemented here, we start with the database container as described in our lectures here. Then we start our application in several ways here. I will explain that later in more detail. Then start up and wait until it's started up. The same thing we have to do is to down the application which is done by using the after each callback here. After each, then we stop the database here. This could be much more than just only as database that you can use and stop any of the components you have used here. And then I'm ready with that. So let us start that on the command line first and see how it works. That will take a few seconds here. I'm creating here a multi-module build already set so and running that without why a supplemental profile here. We will take a look later at the configuration how it looks like. Okay, start it and you see 13 seconds or something. So let us take a look what is really happening here. I've created a separate module where I have the real test here and I have a directory created here and you see that there is a method name or directory called first test. And in that directory you will find the application standard out. That means the startup of your complete application, in this case the Spring Boot application with all the information you need. I have standard error which should be usually empty. I have created a GC log which is the case because I have configured that into the startup information on the command line of the application. And then we can take a look how that really works. If you want to know some other stuff here, you can take a look because I'm using Maven here in the failsafe report directory. There is an output text file that represents all the output which has been created during the startup session here. There you see the startup here of Docker test containers here. Let a Docker container start it. And if you're going down there then you see somewhere here is the startup of the Postgres database. And you see this Postgres database. So it's downloading that. Take some time here. And we go downstairs here going down through the file. And then we can see somewhere the check if our container is running. That means if our application is running. We are using the health check of the application and just pulling that against that and waiting until it's really ready. Because most of the time most of these services today have a health check and can be easily used for such things here. If it is up there then we know it's up there. And then you see the system output which I have put into the example output here as described here. So now how that looks like all the stuff are working in the extension. So we have two more things here which are very important. The database container is one of them. That means we have a class here very simple which contains a start method which uses test containers to start up a Postgres image here. We are exposing a port. We are trying that several times to be a little bit more safe. We make a timeout in cases there is going something wrong. Then we are defining some environment variables to define the username, the password and the database we are already using. Then delegating to test containers. We do the same thing for the stop method and some other things here. Then we can use that and start up that in our extension and then simply wait until it started. That is a synchronized call in this case here and we wait until the database container is completely started up. You can start in that way for example other stuff if you like like in Redis or whatever you like here that would be possible as well. So we created some directories where we store the resulting standard output information and things like that. That is created by these constructs here. We are getting some information from the context here and creating based on that directory names. So we can create the storage here in a supplemental directory to make a separation between each test run. Then we have application parts which is important to create different TCP iPorts. Simply using that and using the socket details from Spring Framework and we getting two different ports here and defining two different ports. One for the application, one for the actuator here. The actuator is exactly the point with the health check here. Make them separate and give them back to my extension here. So in this case we are using the ports, creating them and then putting them on the command line to define the server port and the management port. And also getter methods have been defined in the database container to get the information from the URL, how it is produced and how it is created. One word, supplemental word about the mapped port. If you start a container on the command line and use a dash uppercase p automatically a TCP iPort is selected which doesn't definitely not clash with other ports. If you do that manually and do that twice you will get a clash between the ports. So that can clash sometimes and usually you should let Docker do that here in that way. Then we have started up our application. So one thing is needed to do is to start up our real application here. And that's why I created this application executor called. That's getting the information of the directory where we should start inside there and how it is named the application. Technically it's not doing something special it's just starting up and using the Java home with the JVM. Just checking if the existence of the file is there. You see there are the JVM arguments. That's the reason why we get in GC log or something like that. That can be changed of course for your requirements. Adding some supplemental calling arguments like the Java dash jar or something like that. That's the start up arguments for Spring Boot. Printing them out on the logger to have more information in cases of errors. And then starting via a process builder this part here redirecting error and setting a working directory here for that. And after that you can start the application and wait until it's up there. The thing here is the is ready is simply using the rest endpoint meaning there are actuator help and check endpoint to wait until it's available application. That can be done in different ways depending on your application. This one I think works with mode of the creation because many many applications today use our actuator for something like Prometheus or something like that as endpoints. And you can use the health check endpoint for that to wait until the application is started. Going down after each as well here we wait doing shutting down the database and awaiting until the database is down. The application is shut it shut it down by a kill signal and then we wait until it's completely down. The details I will take a look later. So the result is that we get the information here from the output all the time here and I can see that it started up correctly. And if I take a look into the output which is created by the show a face if plug in then you see all this stuff and the health check is done and also the information is printed out that my method has been executed. So one thing here is if I do that that way here I can very easily start up a second test without a big issue here. Just simply doing that and if I do that a second time here now that will take some time of course. So that takes a few seconds here to start up that part here. So you see it take 24 seconds because we have started two tests that means the complete application plus the database container has been started up completely two times just sequentially but that has been done. So you can see the results here one application startup lock here and a second one is now created and you see the same thing here. Now you have the possibility to intercept the information and the startup of the application via the usual setup which is already there via before each so we can intercept that and start in before each doing some special stuff if we like to for each of our application that means for two of them in this case I do that here and just calling the rest endpoint of my application here and just creating supplemental entries for the database here just calling the create endpoint here. I'm just printing out something for real use case you should do something different instead of printing out something. So I can rerun that and then you can see the result of that. I will take a number of seconds these are 30 seconds around 30 seconds which takes the time here yes so you see 25 seconds and then you see things like that and then you can see in the output here that if you take a look in that you see the health check here then the running here or in the lock file that will take a time and then you see that the health check of the second call is done here and you see that the create endpoint is called several times here. Furthermore it's also called the list endpoint here is called and you can see that the information is giving back from our application so you can use that for whatever purpose you like. So furthermore there are some special things here to get that internally correctly running here, there are is a so called internal storage which is used here that I said before. I store that here into a so called context here which is provided by the JUnitDupeter framework. Internally this store is more or less a hash map which is synchronized and you can use that in multi-threaded environment. You should use also a namespace for your own extension usually in the name of the class including the package name and so on and then get that store and into that store you can put information. There is a big difference here you can use that and you should use also a unique ID that is a combination of the engine which is run like JUnitDupeter and things like that and thus this information includes also the method name that means this information here is stored by using that which includes also the name of the method which is called for example first test, second test in our class including the class name of course. And that means you can store the information of the database container and that means in consequence that you can completely run this here in parallel so it is possible to do that if you do that correctly with the store here that you can run all the tests in parallel. That's a very good thing in particular if you run such complexity and time consuming end-to-end tests you can run them in parallel if you like but you have to be a little bit careful what you exactly store and how you exactly extract that. Usually there is no choice, no way to give some information from before each to after each and using a local variable is a bad way because it does not work and it's not thread safe of course and using that storage is the only correct way to do that. And then you can use that after each for example get an instance of the class database container for example to stop that also from the application process which is stored at the before each method as well. So this makes it possible to run them in parallel. One more thing is to support parameters here then you have to add an interface implementation which is called parameter, I'm not parameter resolver sorry and these things are used to check if the parameters of before each of the test method itself contains particular parameters which are supported and then you can check the parameters here for example application port, database port and put these informations from the storage into the methods which are returning that as an object here and that means it is possible to have these things here just writing that into your test and inject into your test very easily. So now let us little bit improve the speed here of that test here. So I have preferred something a little bit here and now I run that and you see that last time you run that that took about 25 seconds and now we have two tests running but they are running in parallel and you can do that much more than of them with much more tests end to end test if you like but that depends of course on your machine how many cores you have how much memory you use and things like that and how many components you are studying up or shutting down. And it can increase the speed a lot and you see I am coming down to the speed with 15 seconds as before but I am running two in parallel that is of course possible here as well. So you have a very easy way to separate that as you see I have implemented the extension into a complete separate module you can do that in the before each after each method yourself the first time but it is not a clear separation of concern and not clear separation of the code. I think it is better to use separate module for that because that makes it easier to extract that and put that into a separate project which can be easily reused somewhere else if you like. It is also further more possible to make different extension I have combined at the moment the extension for the database as well as the application. You can do and make separate extension of that for example one for the database startup and shutting down and a separate one for the application startup and shutting down and then you can make it more flexible to combine them but that depends on your use case here. So let us come back to the slides for example as I said different things here as well as possible. And finally I think if you have further questions the Q&A session will be coming there. I have some links about the documentation which I strongly recommend the user's guide. I have made a lecture last year on FOSSTEM as well about basics of JUnitDupeter, the user's guide, extension execution order and things like that and of course a link to the whole code which can be seen on GitHub here as well. Thank you very much and we will see in the Q&A session.
how to use it. Writing some unit tests with JUnit the JUnit Jupiter engine which is responsible for and if you have done that correctly via the implementation. So that makes it very easy and with the appropriate test annotation. So an appropriate method. If you need to do needed to be a static method based on the part where the real test code is and it is a little application in several ways here. I will explain case the Spring Boot application with all the using the health check of the application and can be easily used for such things here. If it is application parts which is important to create be done in different ways depending on your via the usual setup which is already there via things here to get that internally correctly running here, there are is a so called internal Internally this store is more or less a hash map is stored by using that which includes also the parallel. That's a very good thing in particular end-to-end tests you can run them in parallel if you like but you have to be a little bit careful the database as well as the application. You can