3

I have a Rest controller with a Device (Device must be resolvem, I'm using spring-mobile-device) as a Parameter. The unit test gave me a status 415.

Here is the Code of

@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> authenticationRequest(@RequestBody AuthenticationRequestDto authenticationRequest,
        Device device) throws AuthenticationException {

    Authentication authentication = this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
            authenticationRequest.getUsername(), authenticationRequest.getPassword()));
    SecurityContextHolder.getContext().setAuthentication(authentication);

    UserDetails userDetails = this.userDetailsService.loadUserByUsername(authenticationRequest.getUsername());

    String token = this.tokenGenerator.generateToken(userDetails, device);

    return ResponseEntity.ok(new AuthenticationResponseDto(token));
}

Unit test

    ResultActions res = mockMvc.perform(post("/auth", authentication, device).contentType(TestUtil.APPLICATION_JSON_UTF8)
            .content(TestUtil.convertObjectToJsonBytes(authentication)));
    res.andExpect(status().isOk());
gdiazs
  • 100
  • 8
  • It might be interesting to put the relevant code of `AuthenticationController` and `AuthenticationControllerTest` in this question. If you ever change the code in the repository, it would invalidate the question (and the answer) for future readers. – g00glen00b Mar 03 '17 at 23:41
  • I'll put the code. Thanks – gdiazs Mar 04 '17 at 01:44
  • Make sure that you've added the `@EnableWebMvc` annotation somewhere (probably in a config class), this will be necessary in order for Mock MVC to work. – g00glen00b Mar 04 '17 at 12:59
  • Actually you right. @EnableMvc was missing. Its working right now, but now give me a status 500 because Device parameter in controller comes null. Maybe using a resolver argument – gdiazs Mar 07 '17 at 01:43
  • Probably because the beans you registered to make Spring mobile work, should also be applied to your testing configuration. – g00glen00b Mar 07 '17 at 06:42

2 Answers2

1

Well basically I was wrong with my configuration. It is mandatory configure the Web Config for testing in same way that production configuration but are grammatically different. Well I learned a lot about MockMVC config with this problem.

Here's the solution if you want do unit testing with spring mobile.

First Class

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {WebTestConfig.class})
@WebAppConfiguration
public class WebTestConfigAware {

  @Autowired
  private WebApplicationContext context;

  protected MockMvc mockMvc;

  @Autowired
  private FilterChainProxy springSecurityFilterChain;

  @Before
  public void setup() {
    mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
    DeviceResolverRequestFilter deviceResolverRequestFilter = new DeviceResolverRequestFilter();

    mockMvc = MockMvcBuilders.webAppContextSetup(context)
        .addFilters(this.springSecurityFilterChain, deviceResolverRequestFilter).build();
  }

}

Second class

@Configuration
@EnableWebMvc
@Import({RootTestConfig.class, WebCommonSecurityConfig.class})
public class WebTestConfig  extends WebMvcConfigurerAdapter{


  @Override
  public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    argumentResolvers.add(new ServletWebArgumentResolverAdapter(new DeviceWebArgumentResolver()));
    argumentResolvers.add(new SitePreferenceHandlerMethodArgumentResolver());
  }
}

and Test Class

public class AuthenticationControllerTest extends WebTestConfigAware {

  @Test
  public void testAuthenticationRequest() throws Exception {
    AuthenticationRequestDto authentication = new AuthenticationRequestDto();
    authentication.setUsername("admin");
    authentication.setPassword("Test1234");

    String jsonAuthentication = TestUtil.convertObjectToJsonString(authentication);

    ResultActions res = mockMvc.perform(post("/auth")
        .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE).content(jsonAuthentication));

    res.andExpect(status().isOk());

  }
gdiazs
  • 100
  • 8
0

In your test class you are improperly constructing your request

// a couple of issues here explained below
ResultActions res = mockMvc.perform(post("/auth", authentication, device).contentType(TestUtil.APPLICATION_JSON_UTF8)
                .content(TestUtil.convertObjectToJsonBytes(authentication)));

post("/auth", authentication, device) authentication and device are interpreted as path URI so they are not needed here, your controller URI does not have any path URI variables. If your intent is to pass 2 objects as the body of the request then you need to modify your test request and your controller request handler. You cannot pass 2 objects as the body of a request, you need to encapsulate both objects in one like

class AuthenticationRequest {
     private AuthenticationRequestDto authenticationDto;
     private Device device;

     // constructor, getters and setters
}

In your controller

@RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<?> authenticationRequest(@RequestBody AuthenticationRequest request) throws AuthenticationException {
    AuthenticationRequestDto authenticationDto = request.getAuthenticationDto();
    Device device = request.getDevice();

    // ....
}

Also in you test you need to pass a JSON object string, you are converting it to bytes (this is why you are getting a 415):

// note the change in the TestUtils, the method being called is convertObjectToJsonString (you'll need to add it)
ResultActions res = mockMvc.perform(post("/auth").contentType(TestUtil.APPLICATION_JSON_UTF8)
        .content(TestUtil.convertObjectToJsonString(new Authenticationrequest(authentication, device))));
artemisian
  • 2,976
  • 1
  • 22
  • 23
  • Hi, thanks. You Right! about my URI problem but Normally when I deploy a app, using POSTMAN I just send the Authentication (user and password) and the device is resolve by Spring configuration. I don't know If I need more config for the MockMvc or just try to mock it – gdiazs Mar 03 '17 at 18:40
  • Great! Can you please accept my answer so others know this works if they face a similar issue? – artemisian Mar 03 '17 at 18:42
  • Sure. But I change the comment. Any Idea? – gdiazs Mar 03 '17 at 18:47
  • In that case then just convert the json to a string instead of a byte[] TestUtil.convertObjectToJsonString – artemisian Mar 03 '17 at 18:58
  • I did, but same status. [AuthenticationControllerTest](https://github.com/neonds/common-security/blob/master/src/test/java/com/guillermods/common/security/web/controller/AuthenticationControllerTest.java) – gdiazs Mar 03 '17 at 19:20
  • Can you just pass MediaType.APPLICATION_JSON_UTF8_VALUE instead of TestUtil.APPLICATION_JSON_UTF8_VALUE and try? – artemisian Mar 03 '17 at 19:52
  • If that doesn't work then try adding @SpringBootTest in your WebAppConfigurationAware class. – artemisian Mar 03 '17 at 20:02
  • It will works? I had configured it in a classic way. I really appreciate your comments by the way – gdiazs Mar 03 '17 at 20:29