I get An Authentication object was not found in the SecurityContext error when I call a #Service after calling a #RestController through MockMvc in a test method annotated with #WithMockUser.
Calling in the opposite order works fine as does calling the service and controller individually. The #Service is clearing the SecurityContextHolder. The only workaround I can think of is to set the context manually. Am I missing something?
I need to call the service to mutate state and then test that the controller returns the correct state.
#Service
public class SimpleService {
#Secured("ROLE_SIMPLE_USER")
public void doThis(Long id) {
System.out.println("dothis : " + id);
}
#Secured("ROLE_SIMPLE_USER")
public void doThat(Long id) {
System.out.println("dothat : " + id);
}
}
#RestController
#RequestMapping("/api")
#Secured("ROLE_SIMPLE_USER")
public class SimpleController {
#Autowired
private SimpleService simpleService;
#PostMapping(value = "{id}/dothis")
public ResponseEntity dothis(#PathVariable("id") Long id) {
simpleService.doThis(id);
return ResponseEntity.status(HttpStatus.OK).build();
}
#PostMapping(value = "{id}/dothat")
public ResponseEntity dothat(#PathVariable("id") Long id) {
simpleService.doThat(id);
return ResponseEntity.status(HttpStatus.OK).build();
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
#ActiveProfiles({"devh2"})
public class SimpleControllerTest {
#Autowired
private SimpleService simpleService;
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void setUpLiabilities() {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
#WithMockUser(username="simple.user", password = "******", roles = {"SIMPLE_USER"})
public void callViaControllers() throws Exception {
mockMvc.perform(post("/api/1/dothis").with(csrf())).andExpect(status().isOk());
mockMvc.perform(post("/api/1/dothat").with(csrf())).andExpect(status().isOk());
}
#Test
#WithMockUser(username="simple.user", password = "******", roles = {"SIMPLE_USER"})
public void callViaService() throws Exception {
simpleService.doThis(1L);
simpleService.doThat(1L);
}
#Test
#WithMockUser(username="simple.user", password = "******", roles = {"SIMPLE_USER"})
public void callViaServiceThenControllers() throws Exception {
simpleService.doThis(1L);
mockMvc.perform(post("/api/1/dothat").with(csrf())).andExpect(status().isOk());
}
#Test
#WithMockUser(username="simple.user", password = "******", roles = {"SIMPLE_USER"})
public void callViaControllerThenService() throws Exception {
mockMvc.perform(post("/api/1/dothis").with(csrf())).andExpect(status().isOk());
// ***FAILS*** because SecurityContextPersistenceFilter clears the SecurityContext ???
simpleService.doThat(1L);
}
#Test
#WithMockUser(username="simple.user", password = "******", roles = {"SIMPLE_USER"})
public void callViaControllerThenServiceWithWorkaround() throws Exception {
mockMvc.perform(post("/api/1/dothis").with(csrf())).andExpect(status().isOk());
setSecurityContextAndThenCallDoThat(1L, "simple.user", "******", "ROLE_SIMPLE_USER");
}
// Workaround - set context manually
private void setSecurityContextAndThenCallDoThat(long id, String username, String password, String... roles) {
try {
final SecurityContextImpl holder = new SecurityContextImpl();
holder.setAuthentication(new TestingAuthenticationToken(username, password, roles));
SecurityContextHolder.setContext(holder);
this.simpleService.doThat(id);
} finally {
SecurityContextHolder.clearContext();
}
}
}
Related
i added a mocked user this my class
#TestConfiguration
public class UserDetailsMock {
final String apiAccessGroup = "00000000-0000-0000-0000-000000000000";
#Bean
#Primary
public UserDetailsService userDetailsService() {
User ericssonUser = new User("ericsson-worker", "secret", Arrays.asList());
User msApiUser = new User("msApiUser", "StrongPass", Arrays.asList(new SimpleGrantedAuthority(apiAccessGroup)));
return new InMemoryUserDetailsManager(Arrays.asList(
ericssonUser, msApiUser
));
}
}
and this my test Class any help please!!!!
#SpringBootTest(
classes = UserDetailsMock.class
)
#AutoConfigureMockMvc
public class MuninServiceAuthorizationTest {
#Autowired
MockMvc mockMvc;
List<MockHttpServletRequestBuilder> endpoints;
#BeforeEach
public void setUp() throws IOException {
MockitoAnnotations.openMocks(this);
endpoints = Arrays.asList(
MockMvcRequestBuilders.post("/migrate/foss").with(csrf()),
MockMvcRequestBuilders.get("/swagger/ui/**"));
}
#Test
#WithUserDetails("msApiUser")
void userWithGroupWhichIsAllowedToUseApiShouldGet200() throws Exception {
for (MockHttpServletRequestBuilder mockHttpServletRequestBuilder : endpoints) {
mockMvc.perform(mockHttpServletRequestBuilder)
.andExpect(status().isOk());
}
}
}
I generated simple microservices application with Jhipster I wrote simple controller like hello world,
When I am trying to test through test method it is always giving Unauthorized error and the test fails.
Controller:
#RestController
#RequestMapping("/api")
public class TestController{
#GetMapping("/test/{Id}")
public String TestGetData(#PathVariable int Id) {
return "Here is your data!";
}
}
Testclass:
#SpringBootTest(classes = HerstellerController.class)
#AutoConfigureMockMvc
public class TestIT {
#Autowired
private MockMvc mockMvc;
private static final long ONE_MINUTE = 60000;
private String token;
private Key key;
private TokenProvider tokenProvider;
#BeforeEach
public void setup() {
tokenProvider = new TokenProvider( new JHipsterProperties());
key = Keys.hmacShaKeyFor(Decoders.BASE64
.decode("xxxx"));
ReflectionTestUtils.setField(tokenProvider, "key", key);
ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", ONE_MINUTE);
}
#Test
public void TestData() throws Exception {
token=tokenProvider.createToken(createAuthentication(),false);
String id="1";
String expData = "Here is your data!";
String result = mockMvc.perform(get("/api/test/"+ id)
.header("Authorization","Bearer " + token))
.andExpect(status().isOk())
.andReturn()
.getResponse()
.getContentAsString();
System.out.println("\nResult:\n"+result);
}
private Authentication createAuthentication() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ADMIN));
return new UsernamePasswordAuthenticationToken("admin", "admin", authorities);
}
Changed the securityconfig also like this
.antMatchers("/api/**").permitAll()
.antMatchers("/api/**").anonymous()
I added authentication to securityContext in setup of Testclass!, It works!
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(createAuthentication());
SecurityContextHolder.setContext(securityContext);
I have a small issue with mocking methods . Where mock should return an predefined object, it returns null. here is test set up:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerTest {
#Autowired
WebApplicationContext webContext;
#MockBean
UserTransferService userTransferService;
#MockBean
UserService userService;
MockMvc mockMvc;
private User user = DummyObjects.getDummyUser();
private User modifiedUser = DummyObjects.getModifiedUser();
private UserTO userTO = DummyObjects.getDummyUserTO();
private UserTO modifiedUserTO = DummyObjects.getModifiedUserTO();
#Before
public void setUp() throws Exception {
List<User> users = new ArrayList<>();
users.add(modifiedUser);
users.add(user);
given(this.userTransferService.getTO(user)).willReturn(userTO);
given(this.userTransferService.getObject(userTO)).willReturn(user);
given(this.userTransferService.getTO(modifiedUser)).willReturn(modifiedUserTO);
given(this.userTransferService.getObject(modifiedUserTO)).willReturn(modifiedUser);
given(this.userService.findAll()).willReturn(users);
given(this.userService.save(user)).willReturn(user);
given(this.userService.removeById(1)).willReturn(true);
given(this.userService.getById(1)).willReturn(user);
given(this.userService.getById(2)).willReturn(null);
given(this.userService.modify(modifiedUser)).willReturn(modifiedUser);
given(this.userService.findByLogin(user.getLogin())).willReturn(user);
given(this.userService.findByLogin("AAA")).willReturn(null);
mockMvc = MockMvcBuilders
.webAppContextSetup(webContext)
.apply(springSecurity())
.build();
}
test itself:
#Test
public void shouldAddUser() throws Exception {
mockMvc.perform(post("/api/user")
.content(TestingUtility.asJsonString(userTO))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.with(csrf().asHeader()))
.andExpect(status().is(200))
.andExpect(content().json(TestingUtility.asJsonString(userTO)));
}
and Controller method:
#RequestMapping(method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public UserTO modifyUser(#RequestBody UserTO userTO, UsernamePasswordAuthenticationToken principal) throws IllegalAccessException{
User user = userTransferService.getObject(userTO);
if (principal.getName().equals(userTO.getLogin()) || permissionService.hasPermission(principal,Permission.SU)) {
return userTransferService.getTO(userService.modify(user));
} else {
throw new IllegalAccessException("You are not allowed to modify user");
}
}
User is null, but UserTO is filled. So mocked method UserTransferService.getObject(userTO) is not working properly.
I really searched and followed the steps of creating a unit test class for spring MVC controller, however unit test is running with a green pass flag but the framework uses the original service class and it calls to the database. I mocked the class and used #InjectMocks together with MockitoAnnotations.initMocks(this). Still when the test runs, the controller uses original service object rather than the mocked object. I really appreciate if somebody can help me in this regards.
Here is UserManager(service class), UserRegisterController(controller), TestUserRegisterController (Test class) classes with a picture of the Eclipse package structure
Service :
#Service
public class UserManager {
protected Map<String, String> getAllCertificates() {
Map<String, String> allCertificates = new HashMap<String, String>();
//call to database
return allCertificates;
}
protected User getUser(int userId) {
//create session
User user = session.get(User.class, userId);
//close session
return user;
}
}
Controller :
#Controller
public class UserRegisterController {
#Autowired
private UserManager manager;
#InitBinder
public void initBinder(WebDataBinder binder) {
//do some work
}
#RequestMapping(value = "/user.html", method = RequestMethod.GET)
public ModelAndView getUser(#RequestParam(value="userId", defaultValue="-1") String userId) {
User user1;
user1 = this.manager.getUser(Integer.parseInt(userId));
if (user1 == null) {
user1 = new User();
}
ModelAndView view = new ModelAndView("User", "user1", user1);
view.addObject("allCertificatesMap", this.manager.getAllCertificates());
return view;
}
#ModelAttribute
public void setModelAttribute(Model model) {
model.addAttribute("PageHeader", "lable.pageHeader");
}
}
Test class :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("test-spring-dispatcher-servlet.xml")
#WebAppConfiguration
public class TestUserRegisterController {
#Mock
private UserManager userManager;
#InjectMocks
private UserRegisterController userRegisterController;
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
// Process mock annotations
MockitoAnnotations.initMocks(this);
User user2 = new User();
user2.setUserId(10006);
user2.setUserName("Reza");
user2.setHobby("Quadcopter");
user2.setPhone("4032376295");
when(this.userManager.getUser(10006)).thenReturn(user2);
when(this.userManager.getAllCertificates()).thenReturn(new HashMap<String, String>());
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void getUser() {
try {
this.mockMvc.perform(get("/user.html").param("userId", "10006"))
.andExpect(status().isOk())
.andExpect(forwardedUrl("/WEB-INF/jsp/User.jsp"))
.andExpect(MockMvcResultMatchers.view().name("User"))
.andExpect(model().attributeExists("allCertificatesMap"));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Package hierarchy
Use #RunWith(MockitoJUnitRunner.class) to get the #InjectMocks and other annotations to work
I am using Jhipster with Oauth2 implementation and mongodb as a database.
I am trying to test a resource with OAuth2. But I got always an error message "Access Denied" and status code 401. I am looking for an JUnit example with OAuth2. Thank you!
Manuel
/**
* Test class for the InvoiceResource REST controller.
*
* #see InvoiceResource
*/
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
public class InvoiceResourceIntTest {
...
private MockMvc restInvoiceMockMvcWebApp;
#PostConstruct
public void setup() {
MockitoAnnotations.initMocks(this);
this.restInvoiceMockMvcWebApp = MockMvcBuilders.webAppContextSetup(context).alwaysDo(MockMvcResultHandlers.print())
.apply(SecurityMockMvcConfigurers.springSecurity()).build();
}
#Before
public void initTest() {
// Create currentuser
currentUser = new User();
currentUser.setActivated(CURRENTUSER_ACTIVATED);
currentUser.setFirstName(CURRENTUSER_FIRSTNAME);
currentUser.setLastName(CURRENTUSER_LASTNAME);
currentUser.setEmail(CURRENTUSER_EMAIL);
Set<Authority> authorities = new HashSet<>();
Authority authority = new Authority();
authority.setName(AuthoritiesConstants.ADMIN);
currentUser.setAuthorities(authorities);
currentUser.setPassword(passwordEncoder.encode(CURRENTUSER_PASSWORD));
userRepository.save(currentUser);
}
#Test
// #WithMockUser(username = CURRENTUSER_EMAIL, password = CURRENTUSER_PASSWORD, roles = { "ADMIN" })
public void getAllInvoices() throws Exception {
// Initialize the database
invoice.setDeletedAt(LocalDate.now());
invoiceRepository.save(invoice);
invoice.setId(null);
invoice.setDeletedAt(null);
invoiceRepository.save(invoice);
// Get all the invoices
restInvoiceMockMvcWebApp.perform(get("/api/invoicessort=id,desc")
.with(user(CURRENTUSER_EMAIL).password(CURRENTUSER_PASSWORD.roles("ADMIN")))
.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON)).andExpect(jsonPath("$", hasSize(1)))
}
You can get a token from the token resource, and use that token in your tests, here a complete example.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class PermissionTest {
#Autowired
WebApplicationContext context;
#Autowired
FilterChainProxy springSecurityFilterChain;
MockMvc mvc;
#Before
public void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(context)
.addFilter(springSecurityFilterChain).build();
}
#Test
public void shouldHavePermission() throws Exception {
mvc.perform(get("/api/resource")
.header("Authorization", "Bearer " + getAccessToken("user", "123"))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
private String getAccessToken(String username, String password) {
MockHttpServletResponse response = mvc
.perform(post("/oauth/token")
.header("Authorization", "Basic "
+ new String(Base64Utils.encode(("appclient:password")
.getBytes())))
.param("username", username)
.param("password", password)
.param("grant_type", "password"))
.andReturn().getResponse();
return new ObjectMapper()
.readValue(response.getContentAsByteArray(), OAuthToken.class)
.accessToken;
}
#JsonIgnoreProperties(ignoreUnknown = true)
private static class OAuthToken {
#JsonProperty("access_token")
public String accessToken;
}
}