Level 7 - Milestone 9
Milestone 9 - Our First Unit Test
In this section we will start adding tests for our application.
Before completion of this milestone, students will:
- Add test class for the HomeController
- Add a unit test for the method that redirects to the Swagger UI.
Why Unit Testing?
Up until this point, we have been manually testing our application. That is to say that whenever
we made a change, we have simply rerun the application and tested that it was working by
executing
some
requests ourselves, and checking that the response is what we expected. In reality, this may be
sufficient
for programs of limited size. For example, in the early League levels, the programs were simple
enough that
we could manually test them in a reasonable amount of time, and be left with a reasonable
confidence that
everything was in working order.
However, as applications grow, it becomes more and more time-consuming to test every part of the
application
each time we make a change. While our application currently may have only one endpoint, you can
imagine that if it were to grow to having 30 endpoints, it would be unreasonably burdensome to
test each and every one
of them whenever
a change is made.
Luckily, this is exactly the type of thing computers are good at: repeatedly doing the same task
over
and over again, in exactly the same way. This is the true value of unit tests: not only can they
be run quickly
and repeatedly, but by doing so, we can be confident that every part of the application is still
working as expected.
Removing the Default Test Class
You can go ahead and delete the "CheetahSearchApplicationTests" class that was automatically created within you test package, as we will be creating our own classes to hold our tests.
Creating the HomeControllerTest class
When dealing with a class that doesn't require hundreds of lines of unit tests, it is best to
simply mirror the package structure of the source code in the test source set. That means that
our class located at:
src/main/ ... /presentation/HomeController.java
will have a corresponding test class:
src/test/ ... /presentation/HomeControllerTest.java
If you are using IntelliJ, this can be easily accomplished by visiting the class you wish to
generate
a test for, and hitting command+shift+t (mac) or control+shift+t (windows). This will open a
dialog, within
which you will want to select the "setup/@Before" checkbox, as well as selecting the methods
for which we
want to generate tests. After doing so, the test class will be created in the appropriate place.
Setting up the Test Class
Before we can write the tests, there are a couple things we need to add to the test class. First of all, we are going to need an instance of the class that is being tested so that we are able to call the methods within that class. This is as simple as declaring a member variable in the test class. One thing we need to be careful of is ensuring that our tests don't effect each other. This brings us to the setup method which is annotated "@BeforeEach". As the annotation implies, this method will be run before each test is run, allowing us to reinitialize any objects that may have been changed by the previous test. To that end, we will initialize our instance of HomeController inside of this setup method.
Renaming the home() Test Method
Even if you selected for the IDE to generate the test method for you, you probably will want to
start by renaming the method. We will use the Behavior Driven Design (BDD) naming convention for
our methods.
A method that follows the BDD naming convention will be of the form:
given<Precondition>_when<Method>_then<ExpectedResult>
The given section of the method name can be left off if there are no specific preconditions
that our test is taking into account. Once case in which using the "given" part of the name may
be useful is when testing that a method throws an exception when it is given an invalid input.
In the current case, we can simply name the method:
whenHome_thenReturnRedirect
Naming the method like this clearly conveys what functionality is broken in the case of the
test failing.
Completing the home() Test
One strategy for structuring the test method is to add comments inside of it that mirror the naming convention we used:
@Test void whenHome_thenReturnRedirect() { //given //when //then }
You may find that this makes it easier to conceptualize what needs to be done to complete the method. Using this strategy, it's often easiest to start under the "when", where we will simply invoke the method that we wish to test.
@Test void whenHome_thenReturnRedirect() { //given //when String actual = homeController.home(); //then }
This will often reveal what variables we need to create under the "//given", typically values needed as parameters for the method we are trying to call. Under the "//given", we can also create a variable that will hold the expected result from them method we are testing. In this case, we can copy and paste the redirect String for our HomeController class:
@Test void whenHome_thenReturnRedirect() { //given String expected = "redirect:swagger-ui.html"; //when String expected = homeController.home(); //then }
Finally, we need to add the assertion, or what could be considered the actual test. Under the "//then", we will assert that the expected result is equal to the actual result:
@Test void whenHome_thenReturnRedirect() { //given String expected = "redirect:swagger-ui.html"; //when String expected = homeController.home(); //then assertEquals(expected, actual); }
Now if you run the test, you should see that the test passes.
Summary of Code Changes for this Milestone
-
Cheetah-Search
- src
- main
- java
- org.jointheleague.level7.cheetah
- config
- ApiDocConfig.java
- presentation
- HomeController.java
- LocController.java
- service
- LocService.java
- repository
- dto
- Result.java
- LocResponse.java
- LocRepository.java
- resources
- application.yml
- test
- java
- org.jointheleague.level7.cheetah
- presentation
- build.gradle