Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test a Spring Controller with JUnit that depends of Spring Security

I have a Spring Application and I am building JUnit tests to test a certain Controller.

The problem is that inside the Controller I call this code:

final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
final String userName = authentication.getName();

In other words, I need to authenticate before calling this Controller. I wrote a JUnit test with this code:

private MockMvc mockMvc;

    @Test
    public void getPageTest() throws Exception{
        final ProcessFileController controller = new ProcessFileController();
        mockMvc = standaloneSetup(controller).build();

    mockMvc.perform(get(URI.create("/processFile.html")).sessionAttr("freeTrialEmailAddress", "")).andExpect(view().name("processFile"));
        }

And when I run it gives me a NullPointerException right on the final String userName = authentication.getName(); because my authentication is null since I did not login.

The question is: Is there a way to mock the authentication? All ideas are welcome.

Thank you.

like image 418
José Mendes Avatar asked Jan 07 '23 05:01

José Mendes


2 Answers

Spring Security version 4 introduced some neat improvements regarding this.

First make sure you have the testing framework in the classpath for tests, with Maven it looks like:

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-test</artifactId>
  <version>4.0.4.RELEASE</version>
  <scope>test</scope>
</dependency>

Useful imports:

import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;

Test setup:

mockMvc = webAppContextSetup(applicationContext).apply(springSecurity()).build();

(I think you need to the WebApplicationContext rather than a single controller.)

Then the test something like:

mockMvc.perform(get(...).with(user("username").roles("USER"))).andExpect(...);
like image 157
holmis83 Avatar answered Jan 15 '23 15:01

holmis83


Ideally you would use @AuthenticationPrincipal but if that isn't an option you need to setup the SecurityContext with an Authentication instance that will then be available in the test.

You could you a static method in a helper class to do this.

public static void setupSecurityContext(String username, String password, String... groups)
{
  List<GrantedAuthority> authorities = new ArrayList<>();
  for (String group : groups)
  {
    authorities.add(new SimpleGrantedAuthority(group));
  }

  UserDetails user = new UserDetails(username, password, authorities);
  UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user, password);
  SecurityContextHolder.getContext().setAuthentication(token);
}

Then in the test you can simply call

SecurityHelper.setupSecurityContext("user", "password", "g1", "g2");

like image 26
Mark Avatar answered Jan 15 '23 14:01

Mark