Anyone can write code. A salty developer will read that sentence and think "Yeah, anyone can break code too!" Luckily, unit tests can help prevent code breakage. Unit tests protect feature requirements from breaking as new features get added. Unit tests can also be useful to protect an API endpoint from change.

Today, we will look at an API endpoint written using Spring MVC and how to unit test it with Spring Test's Mock MVC and Mockito.

Given an Entity Object below:

public class widget{

  private Long id;
  private String description;

  // getters & setters
}

And it's respective DTO

public class WidgetRestApiModel {

  private Long id;
  private String description;

  public static WidgetRestApiModel fromWidget(Widget widget) {
    WidgetRestApiModel widgetRestModel = new WidgetRestApiModel();
    widgetRestModel.setId(widget.getId());
    widgetRestModel.setDescription(widget.getDescription());
    return widgetRestApiModel;
  }

  // getters and setters
}

Using Spring MVC we can define the API endpoints to create and get a Widget:

I assume the reader is already familiar with Spring MVC. If this is not the case is it simple to infer the purpose of each method.

@RestContoller
public class WidgetController {

  @Autowired
  private WidgetService widgetService;


  /**
   * Create a new widget
   *
   * @param widgetCreationModel the model to create a widget
   * @return the newly created widget with an  id
   */
  @RequestMapping(method = RequestMethod.POST, value = "widgets")
  public ResponseEntity postWidgets(@Valid @RequestBody WidgetCreationModel widgetCreationModel) {
    Widget widget =  widgetService.createwidget(widgetCreationModel);
    return new ResponseEntity<>(widgetRestApiModel.fromwidget(widget), HttpStatus.OK);
  }

  /**
   * Get a widget by Id
   *
   * @param widgetId Long the widget id to get
   * @return a widget
   */
  @RequestMapping(method = RequestMethod.GET, value = "widgets/{widgetId}")
  public ResponseEntity getWidget(@PathVariable("widgetId") Long widgetId) {
    return new ResponseEntity<>(widgetRestApiModel.fromwidget(widgetService.findwidgetById(widgetId)), HttpStatus.OK);
  }
}

The unit test will test the the GET widget endpoint

Unit test for GET

  /**
   * Test GET widget
   */
  @Test
  public void getWidget() throws Exception {
    Widget widget = TestUtils.generateWidget(3L);
    Mockito.when(widgetServiceMock.findWidgetById(widget.getId())).thenReturn(widget);

    ResultActions resultActions = mockMvc.perform(get("/widgets/" + widget.getId()))
        .andExpect(status().isOk())
        .andExpect(content().contentType(TestUtils.APPLICATION_JSON_UTF8));

    WidgetRestApiModel widgetRestApiModel = mapper.readValue(resultActions.andReturn().getResponse().getContentAsString(), widgetRestApiModel.class);

    Assert.assertEquals(widget.getId(), widgetRestApiModel.getId());
    Assert.assertEquals(widget.getDescription(), widgetRestApiModel.getDescription());

    verify(widgetServiceMock, times(1)).findwidgetById(widget.getId());
  }

Dissecting the above unit test. You see that a Widget is generated using a TestUtils class. The TestUtils class is a simple utility class for unit testing that constructs needed objects with static methods. In this case, the generateWidget class creates a Widget with an id of 3. This Widget instance will be used throughout the unit test to assert proper behavior.

public class TestUtils {

  ...

  public static Widget generateWidget(Long id) {
    Widget widget = new Widget();
    widget.setId(id);
    widget.setDescription("widget description " + id);
    return widget;
  }

}

A TestUtils class like this is great when unit testing applications. It allows the developer to create objects in deterministic way and saves enormous amount of effort through simple code reuse.

Next, the call to the WidgetService::findWidgetById needs to be mocked. Mocking is a way of limiting the scope of the unit test. In the above, the WidgetService::findWidgetByid method will be mocked, meaning it will be given a predetermined object to return instead of actually executing. As a result, the unit test will only be testing logic in the controller method and not the service method. Applying scope to a unit test gives the unit test greater precision. The mock framework being used is Mockito.

Mockito is a "Tasty mocking framework for unit tests in Java." (Their words not mine.) It allows developers to mock objects and in this case the WidgetService. The line method tells Mockito to return the widget previously instantiated when the findWidgetById method is called.

Finally, it is time to "start" the test. MockMvc will perform a GET request on the "/widgets" endpoint for the id of the widget created. MockMvc is told to expect an HTTP Status and expect the content type to be JSON. Once this line is executed the unit test will enter the method WidgetController::getWidget.

Now, to test the result of the method, the unit test must convert the JSON object back to a WidgetRestApiModel and Assert the values are as expected.

mapper is an instance of a Jackson Object MapperJackson Object Mapper. It will convert the response from the controller method to a WidgetRestApiModel. This object can then be used in standard JUnit assertions.

Finally, Mockito allows the unit test to verify the number of times each mocked method was called.

In Summation this unit test:

  • verifies the existence of the API endpoint /widgets/{widgetId}
  • verifies that the correct HTTP status is returned along with the proper content type
  • verifies the conversion from an entity object to a DTO rest object does not change the data
  • verifies that the service layer was used properly

Writing a unit test for the POST method will be left as a exercise to the reader.

Happy Unit Testing, Charles

Mark Curphey, Vice President, Strategy Mark Curphey is the Vice President of Strategy at CA Veracode. Mark is the founder and CEO of SourceClear, a software composition analysis solution designed for DevSecOps, which was acquired by CA Technologies in 2018. In 2001, he founded the Open Web Application Security Project (OWASP), a non-profit organization known for its Top 10 list of Most Critical Web Application Security Risks. Mark moved to the U.S. in 2000 to join Internet Security Systems (acquired by IBM), and later held roles including director of information security at Charles Schwab, vice president of professional services at Foundstone (acquired by McAfee), and principal group program manager, developer division, at Microsoft. Born in the UK, Mark received his B.Eng, Mechanical Engineering from the University of Brighton, and his Masters in Information Security from Royal Holloway, University of London. In his spare time, he enjoys traveling, and cycling.

Love to learn about Application Security?

Get all the latest news, tips and articles delivered right to your inbox.

 

 

 

contact menu