How to mock custom JWT claims in #WebMvcTest - spring-boot

I am using Spring Boot 2.2.0.RELEASE and my Spring-based backend acts as an OAuth2 Resource server which runs fine in production.
All my REST endpoints are protected :
public class BookingController {
#PreAuthorize("hasAuthority('booking:WRITE')")
#PostMapping(value = "/book")
public ResponseEntity<Void> createBooking(#RequestBody BookModel bookModel, JwtAuthenticationToken jwt) {..}
I wanted to write a unit test against my REST APIs and I would like to mock the JWT token.
I tried the following but I always get the "Access denied message"
My Unit test looks like the following:
#WebMvcTest(controllers = BookingController.class)
public class BookingControllerTests {
#Autowired
private ObjectMapper objectMapper;
#Autowired
MockMvc mockMvc;
#MockBean
JwtDecoder jwtDecoder;
#Test
public void when_valid_booking_then_return_200() {
BookModel bookModel = new BookModel();
mockMvc
.perform(post("/book")
.with(jwt(jwt ->jwt().authorities(new SimpleGrantedAuthority("booking:WRITE"))))
.contentType("application/json")
.content(objectMapper.writeValueAsBytes(bookModel)))
.andExpect(status().isCreated());
}
Somehow the claims which are defined in mockMvc are ignored. See the debug output :
PrePostAnnotationSecurityMetadataSource : #org.springframework.security.access.prepost.PreAuthorize(value=hasAuthority('booking:WRITE')) found on specific method: public org.springframework.http.ResponseEntity BookingController.createBooking(BookModel ,org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken)
o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /book; Attributes: [permitAll]
o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken#eca97305: Principal: org.springframework.security.oauth2.jwt.Jwt#bbd01fb9; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: SCOPE_read
o.s.s.a.v.AffirmativeBased : Voter: org.springframework.security.web.access.expression.WebExpressionVoter#18907af2, returned: 1
o.s.s.w.a.i.FilterSecurityInterceptor : Authorization successful
o.s.s.w.a.i.FilterSecurityInterceptor : RunAsManager did not change Authentication object
o.s.s.w.FilterChainProxy : /book reached end of additional filter chain; proceeding with original chain
o.s.s.a.i.a.MethodSecurityInterceptor : Secure object: ReflectiveMethodInvocation: public org.springframework.http.ResponseEntity BookingController.createBooking(BookModel,org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken); target is of class [BookModel]; Attributes: [[authorize: 'hasAuthority('booking:WRITE')', filter: 'null', filterTarget: 'null']]
o.s.s.a.i.a.MethodSecurityInterceptor : Previously Authenticated: org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken#eca97305: Principal: org.springframework.security.oauth2.jwt.Jwt#bbd01fb9; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: SCOPE_read
o.s.s.a.v.AffirmativeBased : Voter: org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter#67afe909, returned: -1
o.s.s.a.v.AffirmativeBased : Voter: org.springframework.security.access.vote.RoleVoter#79f1e22e, returned: 0
o.s.s.a.v.AffirmativeBased : Voter: org.springframework.security.access.vote.AuthenticatedVoter#6903ed0e, returned: 0
c.s.d.r.e.GlobalExceptionHandler : mapped AccessDeniedException to FORBIDDEN
org.springframework.security.access.AccessDeniedException: Access is denied
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-5.2.0.RELEASE.jar:5.2.0.RELEASE]

In your lambda for mocking the JWT you are calling the post processor twice by using the parentheses twice .with(jwt(jwt ->jwt()...))
Instead, try
mockMvc
.perform(post("/book")
.with(jwt().authorities(new SimpleGrantedAuthority("booking:WRITE"))))

If you need the security context up, then you are not writing an unit-test.
Anyway, why not to use #WithMockUser?
Here you can see a snapshot of how to use it in an Integration Test which is mocking the Business Layer.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("api")
public class GetAllProfilesControllerITest {
#MockBean
private GetAllProfilesDAO getAllProfilesDAO;
#MockBean
private ProfileControllerMapper profileControllerMapper;
#Inject
private WebApplicationContext context;
private MockMvc mockMvc;
private static final String ENDPOINT = "/profiles";
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#WithMockUser(authorities = "MINION")
#Test
public void should_returnUnauthorized_when_cantView() throws Exception {
//when
mockMvc.perform(get(ENDPOINT))
//then
.andExpect(status().isUnauthorized());
}
#WithMockUser(authorities = {"VIEW", "CREATE"})
#Test
public void should_returnOk_when_canView() throws Exception {
//when
mockMvc.perform(get(ENDPOINT))
//then
.andExpect(status().isOk());
}
#WithMockUser(authorities = "PUPPY")
#Test
public void should_returnOk_when_puppy() throws Exception {
//when
mockMvc.perform(get(ENDPOINT))
//then
.andExpect(status().isOk());
}
}

MvcResult result =
mvc.perform(
MockMvcRequestBuilders.get("/v1/path/example")
.with(jwt(token -> token.claim("claimKey","claimValue"))
.accept(MediaType.APPLICATION_JSON))
.andReturn();
to use this you have to use mock mvc. so annotate your test class with #AutoConfigureMockMvc
and inject MockMvc
#Autowired private MockMvc mvc;

Related

Getting "reactor.netty.http.client.PrematureCloseException: Connection prematurely closed BEFORE response" while mocking a third part API

My Spring boot API is consuming a third part API using WebClient
DemoAPIController
#RestController
public class DemoAPIController
{
#Autowired
DemoService demoService;
#GetMapping("/mytest")
public Mono<UserType> getUserType()
{
return demoService.getUserType();
}
}
DemoService.java
#Component
public class DemoService
{
#Value("${base.url}")
private String baseUrl;
public Mono<UserType> getUserType()
{
System.out.println("baseUrl:" + baseUrl);
WebClient webClient = WebClient.builder().baseUrl(baseUrl)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader("apikey", "test").build();
Mono<UserType> testResult = webClient.get()
.uri("/valic/valic-security-data-api/v1/utility/users/ID873366777/usertype?appName=EAO").retrieve()
.bodyToMono(UserType.class);
testResult.subscribe(value -> System.out.println(value.getUserType()));
return testResult;
}
}
I have mocked the third part API using MockWebServer. When I try to test the API using mockMvc.perform(...). My assertions are working as expected. But while shutting down the MockWebServer, I am getting the following exception
2019-11-29 16:02:43.308 INFO 13048 --- [127.0.0.1:53970] okhttp3.mockwebserver.MockWebServer : MockWebServer[443] received request: GET /valic/valic-security-data-api/v1/utility/users/ID873366777/usertype?appName=EAO HTTP/1.1 and responded: HTTP/1.1 200 OK
2019-11-29 16:03:18.362 INFO 13048 --- [ckWebServer 443] okhttp3.mockwebserver.MockWebServer : MockWebServer[443] done accepting connections: socket closed
2019-11-29 16:03:18.385 WARN 13048 --- [ctor-http-nio-1] r.netty.http.client.HttpClientConnect : [id: 0x186c45c1, L:0.0.0.0/0.0.0.0:53970 ! R:localhost/127.0.0.1:443] The connection observed an error
reactor.netty.http.client.PrematureCloseException: Connection prematurely closed BEFORE response
2019-11-29 16:03:18.389 INFO 13048 --- [127.0.0.1:53970] okhttp3.mockwebserver.MockWebServer : MockWebServer[443] connection from /127.0.0.1 failed: java.net.SocketException: Socket closed
2019-11-29 16:03:20.509 INFO 13048 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
My Test case
#ExtendWith(SpringExtension.class)
#WebAppConfiguration()
#ContextConfiguration(classes = WebclientApplication.class)
#SpringBootTest
public class EmployeeServiceMockWebServerTest
{
public static MockWebServer mockWebServer;
private ObjectMapper MAPPER = new ObjectMapper();
public MockMvc mockMvc;
public MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
public MediaType contentTypeURLEncoded = new MediaType(MediaType.APPLICATION_FORM_URLENCODED.getType(),
MediaType.APPLICATION_FORM_URLENCODED.getSubtype(), Charset.forName("utf8"));
#Autowired
private WebApplicationContext webApplicationContext;
#BeforeAll
static void setUp() throws IOException
{
mockWebServer = new MockWebServer();
mockWebServer.start(443);
}
#AfterAll
static void tearDown() throws IOException
{
mockWebServer.shutdown();
}
#BeforeEach
void initialize()
{
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
System.out.println("Hostname:" + mockWebServer.getHostName());
System.out.println("Port:" + mockWebServer.getPort());
}
#Test
void getEmployeeById() throws Exception
{
UserType userType = new UserType();
userType.setUserType("PAR-Mock");
ServiceMessage serviceMessage = new ServiceMessage();
serviceMessage.setCode("200");
serviceMessage.setType("OK");
serviceMessage.setDescription("From Mock");
userType.setServiceMessage(serviceMessage);
mockWebServer.enqueue(
new MockResponse().setBody(MAPPER.writeValueAsString(userType)).addHeader("Content-Type", "application/json"));
mockMvc.perform(get("/mytest").contentType(contentType)).andExpect(status().isOk());
RecordedRequest recordedRequest = mockWebServer.takeRequest();
assertEquals("GET", recordedRequest.getMethod());
assertEquals("/logic/logic-security-data-api/v1/utility/users/ID873366777/usertype?appName=EAO",
recordedRequest.getPath());
}
}
I tried all the solution mentioned in other start overflow posts but still I am getting the same error. Any help is appreciated.
I fixed the issue by commenting the below line.
testResult.subscribe(value -> System.out.println(value.getUserType()));
I created WebClient object using the below code to get additional logging
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(HttpClient.newConnection().compress(true).wiretap(true)))
.baseUrl(baseUrl).defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader("apikey", "somekey").build();
I was able to see two requests.Then after commenting, everything worked as expected.

Spring Boot 2, Spring Security 5 and #WithMockUser

Since I migrated to Spring Boot 2.0.5 from 1.x, with no mean to disable security, I can't get test roles to work on mock MVC tests :
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class ApplicationsControllerShould {
...
#Autowired
private MockMvc mockMvc;
private ObjectMapper mapper = new ObjectMapper();
#Test
#WithMockUser(roles = "ADMIN")
public void handle_CRUD_for_applications() throws Exception {
Application app = Application.builder()
.code(APP_CODE).name(APP_NAME)
.build();
mockMvc.perform(post("/applications")
.accept(MediaType.APPLICATION_JSON_UTF8)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(mapper.writeValueAsString(app)))
.andExpect(authenticated())
.andExpect(status().isOk()); // failure 403!
...
My controller endpoint isn't even protected!
#RestController
#RequestMapping("/applications")
public class ApplicationsController {
...
#PostMapping
public Application addApplication(#RequestBody Application application) {
Assert.isTrue(!applicationsDao.existsById(application.getCode()), "Application code already exists: " + application.getCode());
return applicationsDao.save(application);
}
}
So I have in the test a session (#authenticated fails when #WithMockUser is commented out) and a role by the way (ROLE_ADMIN is visible in traces) but my request is being rejected and I don't understand what I did wrong.
Thx for any idea!
Ok... the good old CSRF stuff, then...
logging.level.org.springframework.security=DEBUG
2018-10-02 10:11:41.285 DEBUG 12992 --- [ main] o.s.security.web.csrf.CsrfFilter : Invalid CSRF token found for http://localhost/applications/foo
Application app = Application.builder()
.code(APP_CODE).name(APP_NAME)
.build();
mockMvc.perform(post("/applications").with(csrf()) // oups...
.accept(MediaType.APPLICATION_JSON_UTF8)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(mapper.writeValueAsString(app)))
.andExpect(authenticated())
.andExpect(status().isOk()); // there we go!

Testing Spring Boot Eureka Server 404

I'm trying to test authentication in my Spring Boot Eureka Server. To do so, I perform a GET on /eureka/apps. I get a 404 instead of 200.
#RunWith(SpringRunner.class)
#WebAppConfiguration
#SpringBootTest(classes = Application.class)
public class GlobalSecurityTest {
#Autowired
private WebApplicationContext wac;
#Autowired
private FilterChainProxy springSecurityFilterChain;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
.addFilter(springSecurityFilterChain).build();
}
#Test
public void givenRoleDiscoveryClient_whenGetEureka_then200() throws Exception {
mockMvc.perform(get("/eureka/apps").header(HttpHeaders.AUTHORIZATION, TOKEN_DISCOVERY_CLIENT)
.andExpect(status().isOk());
}
}
Eureka starts correctly as the logs prove:
2018-04-12 23:07:39.308 INFO 80833 --- [ Thread-12] e.s.EurekaServerInitializerConfiguration : Started Eureka Server
2018-04-12 23:07:39.315 INFO 80833 --- [ main] GlobalSecurityTest : Started GlobalSecurityTest in 7.255 seconds (JVM running for 8.007)
...
2018-04-12 23:07:39.822 DEBUG 80833 --- [ main] o.s.security.web.FilterChainProxy : /eureka/apps/REGISTRY reached end of additional filter chain; proceeding with original chain
2018-04-12 23:07:39.831 DEBUG 80833 --- [ main] w.c.HttpSessionSecurityContextRepository : SecurityContext 'org.springframework.security.core.context.SecurityContextImpl#0: Authentication: StateTokenAuthentication{principalTokenState=be.charliebravo.ibpt.qos3.commons.security.models.ClientState#50b624da, tokenStates={}}' stored to HttpSession: 'org.springframework.mock.web.MockHttpSession#50b4e7b2
2018-04-12 23:07:39.833 DEBUG 80833 --- [ main] o.s.s.w.a.ExceptionTranslationFilter : Chain processed normally
2018-04-12 23:07:39.833 DEBUG 80833 --- [ main] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
java.lang.AssertionError: Status
Expected :200
Actual :404
My security config:
#Configuration
public class WebSecurityConfig {
#Configuration
#Order(3)
public static class DiscoveryClientSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private StateTokenHttpSecurityConfigurer stateTokenHttpSecurityConfigurer;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/eureka/**").authorizeRequests()
.anyRequest().hasRole(Role.DISCOVERY_CLIENT.toString())
.and().exceptionHandling().authenticationEntryPoint(new Http401UnauthorizedEntryPoint());
stateTokenHttpSecurityConfigurer.configure(http);
}
}
}
The Eureka server works fine when I run the application instead of the test.
Don't use MockMvc, because it is limited to testing the web layer, but Eureka mappings aren't registered there. Instead, use TestRestTemplate.
Remove #WebAppConfiguration and add weEnvironment setting in #SpringBootTest
#SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
Autowire TestRestTemplate and local server port
#Autowired
private TestRestTemplate restTemplate;
#LocalServerPort
private int localServerPort;
Perform the request
#Test
public void givenRoleDiscoveryClient_whenGetEurekaPage_then200() throws Exception {
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.AUTHORIZATION, TOKEN_DISCOVERY_CLIENT);
HttpEntity entity = new HttpEntity<>(null, headers);
String endpoint = "https://localhost:" + localServerPort + "/eureka/apps";
ResponseEntity responseEntity = restTemplate.exchange(endpoint, HttpMethod.GET, entity, String.class);
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
And off you go.

Mock SecurityContextHolder / Authentication always returning null

I'm aware this question gets asked a lot, but maybe I have some things that are particular to this. I'm trying to do some integration tests on a Spring Boot application that supports REST (not Spring MVC) and for some reason SecurityContextHolder.getContext().getAuthentication() always returns null, even when using #WithMockUser on the test. I'm not certain if this has to do with using profiles on the configuration classes, but so far we haven't had troubles with this.
Class
#Override
public ResponseEntity<EmployeeDTO> meGet() {
Principal principal = SecurityContextHolder.getContext().getAuthentication();
logger.debug("Endpoint called: me({})", principal);
EmployeeDTO result;
// Get user email from security context
String email = principal.getName(); // NPE here
// ...
}
Test
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"eureka.client.enabled:false"})
#WithMockUser
#ActiveProfiles(value = "test")
public class MeControllerTest extends IntegrationSpringBootTest {
#Autowired
private TestRestTemplate restTemplate;
#MockBean
private SecurityContext securityContext;
#MockBean
private Authentication authentication;
#MockBean
private EmployeeRepository employeeRepository;
#BeforeClass
public static void setUp() {
}
#Before
#Override
public void resetMocks() {
reset(employeeRepository);
}
#Test
public void meGet() throws Exception {
when(securityContext.getAuthentication()).thenReturn(authentication);
securityContext.setAuthentication(authentication);
when(authentication.getPrincipal()).thenReturn(mockEmployee());
SecurityContextHolder.setContext(securityContext);
when(employeeRepository.findByEmail(anyString())).thenReturn(mockEmployee());
ResponseEntity<EmployeeDTO> employeeDTOResponseEntity =
this.restTemplate.getForEntity("/me", EmployeeDTO.class);
// ...
}
If I return a mock Principal instead of mockEmployee() the test cannot even start because this happens:
org.springframework.beans.factory.BeanCreationException: Could not inject field: private org.springframework.security.core.Authentication com.gft.employee.controller.MeControllerTest.authentication; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'org.springframework.security.core.Authentication#0' is expected to be of type 'org.springframework.security.core.Authentication' but was actually of type '$java.security.Principal$$EnhancerByMockitoWithCGLIB$$657040e6'
Additional clarifications: This Spring Boot app also uses OAuth2 for authorization, but it must be turned off for these tests. That's why we use profiles. Omitting the #ActiveProfiles annotation gives us a 401 Unauthorized error against the endpoint request.
I could use PowerMock but I would like to avoid it if possible.
Easier Way of writing Junit for Authentication SecurityContextHolder would be to mock them. Following is the working implementation of it.
You can add the mock classes as per your need and then set context of SecurityContextHolder and then use when() to further mock and return proper mock value.
AccessToken mockAccessToken = mock(AccessToken.class);
Authentication authentication = mock(Authentication.class);
SecurityContext securityContext = mock(SecurityContext.class);
when(securityContext.getAuthentication()).thenReturn(authentication);
SecurityContextHolder.setContext(securityContext);
when(SecurityContextHolder.getContext().getAuthentication().getDetails()).thenReturn(mockSimpleUserObject);
I ended up using MockMvc despite the app not being Spring MVC-based. Additionally, I separated the SecurityContext calls into another service, but before doing that I could assert that the #WithMockUser annotation was working properly.
What's key for this to work is using these snippets at class level:
#WebMvcTest(MeController.class)
#Import({ControllerConfiguration.class, BeanConfiguration.class})
public class MeControllerTest {
// ...
}
Using #WebMvcTest facilitates not having to initialize a SecurityContext in the first place. You don't even have to call springSecurity(). You can just just the mockMvc.perform() operations as usual, and any calls to the SecurityContext will return whatever mocked user you specify, either with #WithMockUser or mocking the service that handles such a call.
This sample code is working for me. This code is using JUnit 5.
#SpringBootTest(classes = Application.class)
#AutoConfigureMockMvc //need this in Spring Boot test
public class LoginControllerIntegrationTest {
// mockMvc is not #Autowired because I am customizing it #BeforeEach
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#Mock
DefaultOidcUser principal;
#BeforeEach
public void beforeEach() {
Authentication authentication = mock(OAuth2AuthenticationToken.class);
// Mockito.whens() for your authorization object
SecurityContext securityContext = mock(SecurityContext.class);
when(securityContext.getAuthentication()).thenReturn(authentication);
when(authentication.getPrincipal()).thenReturn(principal);
SecurityContextHolder.setContext(securityContext);
// setting mockMvc with custom securityContext
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
#Test
public void given_any_OAuth2AuthenticationToken_when_login_then_redirect_to_logout() throws Exception {
final String loginName = "admin";
// given
// manipulate the principal as needed
when(principal.getAttribute("unique_name")).thenReturn(loginName);
// #formatter:off
// when
this.mockMvc.perform(get("/login"))
.andDo(print())
//then
.andExpect(status().isFound())
.andExpect(redirectedUrl("/logout"));
// #formatter:off
}
}

Injecting SecurityContextHolder via Autowired

I want to log information of my requests by using Spring-AOP. Afterwards I want to cover this scenario with tests. Normally i would use the SecurityContextHolder simply but since i dont have the spring context in my Unit-Test I need to mock this element somehow. Since i want to avoid mocking private methods I therefore passed the SecurityContextHolder via Autowired annotation, I was exptecing this to be a Singleton anyways somehow.
#PrepareForTest(SecurityContextHolder.class)
public class AuditLoggerTest extends PowerMockTestCase {
#Mock
AuditLogPersistenceService persistenceService;
#Mock
SecurityContextHolder securityContextHolder;
#Mock
SecurityContext securityContext;
#Mock
Authentication authentication;
AuditLogger auditLogger;
#BeforeClass
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#BeforeMethod
public void setUpBeforeEachTest(){
auditLogger = new AuditLogger(persistenceService, securityContextHolder);
Object principal = new String("123");
PowerMockito.mockStatic(SecurityContextHolder.class);
when(securityContextHolder.getContext()).thenReturn(securityContext);
when(securityContext.getAuthentication()).thenReturn(authentication);
when(authentication.getPrincipal()).thenReturn(principal);
}
Now the initilization of my class looks like this
#Aspect
#Component
public class AuditLogger {
private final AuditLogPersistenceService auditLogPersistenceService;
private final SecurityContextHolder securityContextHolder;
/**
* Common constructor raising its beans
* #param auditLogPersistenceService
*/
#Autowired
public AuditLogger(
final AuditLogPersistenceService auditLogPersistenceService,
final SecurityContextHolder securityContextHolder) {
this.securityContextHolder = securityContextHolder;
this.auditLogPersistenceService = auditLogPersistenceService;
}
It works fine on Unit tests but once I turn on the Spring context in my functional tests which actually do REST request via restassured. I get this error message.
Parameter 1 of constructor in com.service.audit.logging.AuditLogger required a bean of type 'org.springframework.security.core.context.SecurityContextHolder' that could not be found.
Consider defining a bean of type 'org.springframework.security.core.context.SecurityContextHolder' in your configuration.
So can I Autowire the SecurityContextHolder or do I have to use the Static implementation ?
In conclusion the most simple way is to inject a mocked Context into the SecurityContextHolder which is like this. Then the SecurityContextHolder is not in the constructor anymore
public class AuditLoggerTest extends PowerMockTestCase {
#Mock
AuditLogPersistenceService persistenceService;
#Mock
SecurityContext securityContext;
#Mock
Authentication authentication;
AuditLogger auditLogger;
#BeforeClass
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#BeforeMethod
public void setUpBeforeTest(){
auditLogger = new AuditLogger(persistenceService);
SecurityContextHolder.setContext(securityContext);
Object principal = new String("123");
when(securityContext.getAuthentication()).thenReturn(authentication);
when(authentication.getPrincipal()).thenReturn(principal);
}

Resources