How to Junit a RestController But with Spring Mobile (spring-mobile-device)

1k views Asked by At

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());
2

There are 2 answers

0
gdiazs On BEST ANSWER

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());

  }
8
artemisian On

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))));