• 8 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 18/02/2022

Verify Your Implementation by Testing Your Tokens

You should test throughout your entire development time with the debugger or adding small test methods to your web application to test functionality.

Set Up Your App for Testing

When you are testing, there are two types of bugs you will need to look out for. There is the obvious and the not so obvious.

  • Obvious: You build and execute your code, and it won’t compile because you have the wrong syntax or are missing a method somewhere.

  • Not so obvious: Your code compiles and executes, and then later you find out that when you log in, you end up on the wrong landing page.

One way to avoid many of these bugs is to create test methods for all of your methods during development to make sure everything works along the way. This type of testing is called subcutaneous testing because you are testing a specific functionality or method instead of the entire web application as a whole.  

The great thing about the Spring Boot web app is that it already comes included with the test libraries that you need for your Spring Boot app and Spring Security methods. Let’s take a look at our pom.xml file.

The Spring Boot Test and Spring Security Test Dependencies
The Spring Boot test and Spring Security test dependencies

We have OAuth 2.0, Spring Web, and Spring Security dependencies, but did you notice the spring-boot-starter-test, and spring-security-test dependencies? 

Using these dependencies, you can use some specially formulated libraries for Spring Web and Spring Security using Java’s JUnit test libraries.

You continuously get updates on your computer because of all the bugs found and fixed daily. Spring is no different.  If you go to the Spring Framework GitHub page, you will see there are issues brought up and fixed daily.  The framework is continuously changing with new options and updates, so you always have to make sure you have the latest updates on your IDE, especially when you start setting up your test methods.

Let's add the JUnit 5 test libraries:

  • Add the   junit-jupiter-engine  dependency for additional JUnit 5 test libraries. 

  •  Right-click on pom.xml -> Maven -> Add Dependency.

  • Type in   junit-jupiter-engine  in the search box as shown below:

Add JUnit5 Dependency
Add JUnit 5 dependency
  • Click on the result for   junit-jupiter-engine, and OK.

The dependency and plugin should show up in your pom.xml file.

Test Your REST Controller With JUnit 5

Remember how I was pulling my hair out when I couldn’t get a homepage to show up after the user logged in? You can use a simple test class to see if your REST Controller opens up using JUnit. 

Go to your Project Explorer, and look for the src/test/java folder. It has been created to hold all of your test classes. 

You see that Spring Boot has automatically generated a test file. Since the name of my main class file is SpringSecurityAuth, my test class is called SpringSecurityAuthApplicationTests.

Your SpringSecurityAuthApplicationTests Test method
Your SpringSecurityAuthApplicationTests test method

Your generated file should look like this:

SpringSecurityAuthApplicationTests.java
SpringSecurityAuthApplicationTests.java

Notice the import files. Import  org.junit.jupiter.api.Test corresponds to a test library in JUnit 5, and will allow you to use the  @Test notation to designate your method as a test.

import org.springframework.boot.test.context.SpringBootTest  is a test library for Spring Boot MVC applications. This will require the  @SpringBootTest annotation.

What is  contextLoads() about?  

Whenever you open up your Spring Boot web application, you load an instance of the WebApplicationContext, which extends to your ApplicationContext. WebApplicationContext is a library that readies your servlets to create an instance of a web application. The ApplicationContext runs your Spring Boot web application from your main() method in your  SpringSecurityApplication.java file. A test like  @WebMvcTest injects the dependencies for the web layer and loads the WebApplicationContext.  @SpringBootTest integrates your application with the web layer, so it loads both the WebApplicationContext and the ApplicationContext.  

This is important to know because if your ApplicationContext doesn’t load, your web application won’t open up at all.

Can you guess what I did wrong in the situation below? 

Here is an example of this chaos:

What did not initialize and why?
What did not initialize and why?

So now let’s test the controller you created by making a small addition to the SpringSecurityAuthApplicationTests.java file.

First, let’s add these imports:

import org.junit.jupiter.api.Test;

import org.junit.jupiter.api.extension.ExtendWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.test.context.junit.jupiter.SpringExtension;

import com.openclassrooms.controller.LoginController;

Notice that I added the import for the controller with the package name        (com.openclassrooms.controller), and the class name (LoginController).  This is important because you are testing the REST controller in the LoginController.java class.

First, add the  @ExtendWith(SpringExtension.class). The  @ExtendWith  notation adds JUnit 5’s extensive architecture to use for your tests. One of these is the SpringExtension class, which adds the test context. In short, it adds the dependencies to your tests, so you don’t have to. It should look like this now.

@ExtendWith(SpringExtension.class)
@SpringBootTest
public class SpringSecurityAuthApplicationTests {
    @Test
    public void contextLoads()  {
    }
}

You want to create an instance of the BasicController that you can test with the @Autowired annotation. Autowired will implicitly add the dependency injection for you to ensure smooth configuration without manually adding constructor arguments and property tags for the BasicController class.

@ExtendWith(SpringExtension.class)
@SpringBootTest
public class SpringSecurityAuthApplicationTests {

    @Autowired
    private LoginController controller;
    @Test
    public void contextLoads(){
    }
}

Now add the   assertThat()  method to your LoginController instance to match it to your desired result, which is  isNotNull(). This means that when you run the instance of your controller, it should not be null. 

package com.openclassrooms;

import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.openclassrooms.controller.LoginController;

@SpringBootTest
class SpringSecurityAuthApplicationTests {

@Autowired
private LoginController controller;

@Test
void contextLoads()throws Exception {
    assertThat(controller).isNotNull();
     }
    
 }

You can run this test on JUnit 5 by right-clicking on the main project -> Run as -> JUnit Test.

When you execute this code, your JUnit result should show in a tab next to your Console tab. A successful test would show no errors or failures.

JUnit 5 test on SpringSecurityAuthApplicationTests
JUnit 5 test on SpringSecurityAuthApplicationTests

So that was just a test to see if the controller worked. Let’s create a test of some of the methods for authentication on the default login page.

Test Authentication Using MockMvc

Now let’s look at a test using your login credentials. It’s a good test with the default  formLogin() that you can use if you have users connected to a database. You can also create a dummy account to use for testing (remember to remove it for security before it goes into production).

Now route to your src/java/test folder where your other test class is. There should be a package already for your test files. Right-click on that package and New -> Class. You can call it MockMvcTests. 

MockMVC creates a mock version of your web app and runs these methods in there, so it doesn’t interrupt the functionality of your web app. It uses the elements of SpringBootTest and JUnit 5, but now you can create an instance of your full web application configuration just for testing.

First, add the annotation for  @ExtendWith(SpringExtension.class) and the   @AutoconfigureMockMvc. This works best with  SpringBootTest  because we are testing the integration of the web application with the web server. I want to fine-tune it by injecting the dependencies for the MockMvc. 

@ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureMockMvc
public class MockMvcTests {
    @Autowired
    private MockMvc mvc;
    
    @Autowired
    private WebApplicationContext context;
}

Now set up @BeforeWith  advice to build the mock copy before running the test methods. You need the mock copy of your web application to be created before you perform @Test annotated methods.   

We are using the class MockMvcBuilders to evaluate the response codes on your test MVC app. The method is called  setup(), and it uses the webAppContextSetup() to add the web layer,  apply(springSecurity())  to add the Spring Security filter chain and build() methods to create a new instance of type MockMvc.  Basically, this will create a web application you can use for tests.

As you can deduce, the Spring Security filter chain dependency is injected into this instance, hence adding the security ruleset for Spring Security to the MVC instance.

@BeforeWith
public void setup() {
     mvc = MockMvcBuilders
    .webAppContextSetup(context)
    .apply(springSecurity())
    .build();
}

Now let’s add a few methods to test it by creating three methods.

  1. Test the mock object in the web layer.

  2. Test that a correct login authenticates the user.

  3. Test that the incorrect password keeps the user unauthenticated.

These tests use the  perform(),  andDo(), and  andExpect()  methods. They all have test response codes that will be fed into MockMvcBuilders to evaluate if its a pass, error, or failure. 

The   perform()  method create a  GET  request out of the test method. The ResultActions class contains the  andDo()  to perform a general action, and  andExpect()  to expect a certain result. The SecurityMockMvcResultMatchers class checks ResultActions methods with the  authenticated()  and  unauthenticated()  methods.

Step 1: Test the mock object in the web layer.

@Test
public void shouldReturnDefaultMessage() throws Exception {
mvc.perform(get("/login")).andDo(print()).andExpect(status().isOk());
}

Step 2: Test authentication by adding correct credentials using the authenticated() method.

@Test
public void userLoginTest() throws Exception {
    mvc.perform(formLogin("/login").user("springuser").password("spring123")).andExpect(authenticated());
}

Step 3: Test that incorrect credentials leave user unauthenticated with unauthenticated() method.

@Test
public void userLoginFailed() throws Exception {
mvc.perform(formLogin("/login").user("springuser").password("wrongpassword")).andExpect(unauthenticated());
}

Now check and see if you get a pass for all three of your methods:

MockMvcTests Passed!
MockMvcTests passed!

It's all green - nice work!

Let's Recap!

To make sure you do testing right, follow these steps:

  • Update your dependencies to have the latest JUnit library.

  • Use Spring Security and Spring Boot test methods.

  • Test your application and the web layer with SpringBootTest. 

  • Use MockMvc to create a copy of your web application for tests.

  • Test your authentication using a MockMvc application.

In the next chapter, you will learn a very simple way to customize some of Spring Security's default exception handlers like a pro.

Exemple de certificat de réussite
Exemple de certificat de réussite