You are on page 1of 12

Technical Whitepaper

Accelerate Unit Testing of Spring Applications with


Parasoft Jtest and its Unit Test Assistant
INTRODUCTION

The Spring framework (along with Spring Boot) is one of the most development time on unit tests. Teams are sacrificing a lot of time
popular Java enterprise software frameworks. Its use in mission- and effort in the attempt to improve quality. With the growth of the
critical applications means it has come under scrutiny for quality and agile process, time is essential and the burden of unit testing is a
security. In general, developers don’t like unit testing, despite its bottleneck for development teams.
proven track record of improving quality.
The test automation tooling that comes with Parasoft Jtest improves
While unit testing is a recognized best practice, 80% of organizations this situation by providing a guided and automated approach to
that do implement it achieve less than 40% code coverage leaving a testing, making it easier and more efficient. This paper describes how
majority of the code, effectively untested at the unit level. For these Parasoft Jtest’s Unit Test Assistant can improve the workflow and
relatively poor results, developers spending roughly 40% of the outcomes for Spring application testing.

PARASOFT JTEST’S UNIT TEST ASSISTANT

Parasoft Jtest’s Unit Test Assistant is an IDE plugin that helps guide users through the unit testing
practice with easy one-click actions for creating, scaling, and maintaining unit tests. By using the
Jtest Unit Test Assistant, users can achieve higher code coverage while cutting in half the time and
effort of unit testing, all while reducing the specialized expertise needed to build a comprehensive
and meaningful suite of Junit test cases.

Parasot Jtest integrates with existing technologies and is not a vendor locked solution. It easily
integrates with the developer’s IDE (Eclipse or IntelliJ), while leveraging existing open source
frameworks (i.e. JUnit for unit test creation and execution; Mockito and PowerMock for mocking).
Since there are no dependencies on Parasoft libraries, it is simple and easy to run the tests created
by Jtest in an existing continuous integration (CI) process.

THE CHALLENGES OF TESTING SPRING APPLICATIONS

The Spring framework comes with nice support for integration testing, but a lot of manual coding
is required to set up test cases properly. Building and maintaining tests for Spring applications
presents developers with a unique set of challenges, including the following:

• The Spring framework must be initialized and configured

• The application usually has third-party library dependencies (persistent storage, external
services, etc.)

• Applications often use built-in Spring features for sessions, security, messaging, etc.
These can be tricky to set up for developers who are new to Spring testing
2

• Application dependencies (i.e. beans) need to be configured appropriately

• These challenges, combined with the fact that writing comprehensive and maintainable test suites
takes a lot of time in general, result in developers not writing enough tests. In turn, this leads to security
vulnerabilities, defects, regressions, in addition to extra time and cost.

Jtest’s Unit Test Assistant helps by making the process of generating, improving, and maintaining JUnit tests far
easier and less time-consuming, so that developers build good tests quickly and get back to what they presumably
love – writing code.

THE SPRING MVC TEST FRAMEWORK

The Spring Framework includes a testing framework that makes testing Controllers, Services, and other
components much easier. It includes functionality for configuring the Spring test container, invoking Controller
handler methods, and validating behavior with custom assertions.

An example Spring MVC Controller:

@Controller
public class TodoController { 

    @Autowired
    private TodoService service;
    @GetMapping(“/”)

    public String findAll(Model model) {


        List<Todo> todos = service.findAll();
        model.addAttribute(“todos”, todos);
        return “todo/list”;
    }
}

This example controller implements a simple REST service to get items from a “to-do” list. It depends on a
TodoService, which contains the business logic.

To test the findAll method, we need a Junit test which does the following:

• Configure the Spring container with the Controller under test, and a TodoService, which TodoController
depends on.

• Send a valid request to the findAll handler method.

• Validate elements of the response, including the return value (“todo/list”) and Model attribute “todos”.

An example Spring MVC Junit test may look like this:

© 2018 Parasoft Corporation


3

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TodoControllerTest {
    @Autowired
    TodoController controller;
 
    @Autowired
    TodoService todoService;
 
    MockMvc mockMvc;
 
    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }
 
    @Configuration
    static class Config {
        @Bean
        public TodoController getTodoController() {
            return new TodoController();
        }
 
        @Bean
        public TodoService getTodoService() {
            return new TodoService();
        }
    }
 
    @Test
    public void testFindAll() throws Exception {
        mockMvc.perform(get(“/”)).andExpect(view().name(“todo/list”));
    }
}

The above example is a very simple test – but there’s still a lot of “boiler-plate” code to write, and a lot going on.
In this example, the Spring framework is configured with a controller and its services, using an inner Configuration
class. The MockMvc functions are used to send a request to the handler method (using perform), and validate
the returned view name using andExpect.

What’s wrong with the above test? Nothing really – but imagine a more complex controller method, with multiple
handler methods that accept more arguments and produce more outputs. Writing the tests would take a lot
more time, especially if good testing coverage is important. In addition, most real tests require significantly more
configuration (XML or class configurations, sessions and environments, security, etc).

GENERATING SPRING TESTS WITH THE UNIT TEST ASSISTANT

Ideally, unit tests should be fully automated, predictable, and maintainable; however, creating tests like this is
time-consuming and requires focused effort and skills. Parasoft Jtest’s Unit Test Assistant helps solve these
problems by removing the tedious and mundane tasks of creating unit tests, freeing the developer to focus on
the business logic of those tests and ultimately creating more meaningful, maintainable test suites. The Unit Test
Assistant helps developers write Spring tests in multiple ways:

1. Auto-generating the boiler-plate code for Spring MVC tests quickly

2. Auto-generating parameterized tests for increased test coverage

3. Mocking dependencies to isolate the helper method and simplify the test

© 2018 Parasoft Corporation


4

4. Collecting coverage data and analyzing test flow at runtime

5. Providing recommendations with quick-fixes to improve tests

Auto-Generating Tests

Generating Spring tests with the Unit Test Assistant is straightforward – simply select a Spring handler method in
the IDE for the controller and choose a test-creation action:

Choosing either Regular Spring or Parameterized Spring auto-generates the boiler-plate Spring MVC test
automatically, including the Configuration classes (and all Beans that the controller depends on). The mockMvc.
perform call is added too, and is preconfigured to invoke the handler method for which the test was created. The
Unit Test Assistant even adds some example assertions that you can uncomment and configure.

The Unit Test Assistant supports test generation using XML or class configuration by setting the
“ContextConfiguration attributes for Spring tests” option in preferences.

Mocking Dependencies

Managing dependencies in unit testing is critical, since much of the complexity and work comes from isolating
a unit under test. The Unit Test Assistant uses Mockito or PowerMockito to mock dependencies by default (you
can disable this in preferences if you don’t want this). Mocking dependencies allows the test to control those
dependencies, isolating the handler method from the rest of the application to focus testing efforts on the handler.
This is covered in more detail later in the paper.

SPRING BOOT

Since Spring Boot provides simplified configuration for Beans, as well as additional annotations for tests, the Unit
Test Assistant generates slightly different tests when it detects Spring Boot in your project. For example, MockMvc
is autowired, dependencies are mocked with @MockBean, and the @SpringBootTest annotation is used.

RUNNING TESTS AND ANALYZING RESULTS

Generated unit tests can be run using any normal Junit runner. The Unit Test Assistant provides toolbar actions
which run the JUnit and analyze the test.

After tests are run, the test execution flow is shown, and recommendations for improving the test are made by the
Unit Test Assistant and reported in the IDE:

© 2018 Parasoft Corporation


5

PROVIDING HANDLER METHOD INPUTS

Handler methods are often configured to accept path, query, or other parameters as arguments to the method.
To test the MVC handler method, MockMvc is used to build the path/query and any other parameters needed to
invoke the method.

The Unit Test Assistant auto-configures the mockMvc.perform call to invoke the handler method. Individual
parameters show up in the test as local variables (or parameters in a parameterized test) which need to be
configured for the test to run properly.

For example:

@Test
public void testGetPerson() throws Throwable {

    // When
    String id = “”; // Unit Test Assistant: Configure an appropriate parameter value
since the tested method depends on it
    ResultActions actions = mockMvc.perform(get(“/people/” + id));

Here, the “id” String needs to be configured – if not, then the path used would be “/people/”, and Spring will not
match the provided path to the appropriate handler method.

The Unit Test Assistant looks for various types of handler method parameters and automatically prepares the test
for them in the following ways:

• HttpSession (adds an example setAttribute() call)

• Headers (adds a header() call),

• Request body (adds a payload variable and content() call)

• Authentication (adds an example instantiation to the setup method, and a principal() call)

Running a test which does not cause the handler method to be invoked produces a recommendation like the
following:

© 2018 Parasoft Corporation


6

VALIDATING HANDLER METHOD OUTPUTS

Depending on what the handler method is supposed to provide to callers, it may return a variety of types. Most
of the time, handler methods return either a ModelAndView (or similar objects like Model or RedirectView) to
serve a page, or ResponseEntity of some kind (sometimes just the raw object to be serialized). This response is
accessible to the Spring MVC Test framework for validation.

For example, the following assertions were added by the Unit Test Assistant for a handler method which returns
a ModelAndView:

  // When
  String id = “1”;
  ResultActions actions = mockMvc.perform(get(“/people/” + id));
 
  // Then
  // actions.andExpect(status().isOk());
  // actions.andExpect(header().string(“”, “”));
  // actions.andExpect(view().name(“”));
  // actions.andExpect(model().attribute(“”, “”));

Once the test is generated, these assertions can be uncommented and populated with values to quickly build
a useful and valuable test. If an assertion fails at runtime, the Unit Test Assistant provides a recommendation
and quick-fix to automatically update the expected value or simply remove the assertion. To quickly set up the
assertion with a proper value, uncomment an assertion, run the test and inspect the results (expecting failure in
this case), and then use a quick-fix to set the correct expected value.

Spring (combined with Spring Boot) is the leading enterprise Java application framework and as such needs an
appropriate level of testing to ensure the quality and security of applications built with it. But unfortunately, this
level of testing is not being achieved currently, mostly due to a lack of time and the amount of manual coding and
maintenance required. The Unit Test Assistant provides not just unit test automation but guided test creation and
dependency management, to accelerate test creation and reduce maintenance.

MOCKING DEPENDENCIES

The Unit Test Assistant provides not just unit test automation and guided test creation covered above, but also
dependency management, to accelerate test creation and reduce maintenance.

© 2018 Parasoft Corporation


7

Complex applications are not built from scratch – they use libraries, APIs, and core projects or services that
are built and maintained by someone else. Spring developers must leverage existing functionality as much as
possible so they can spend time and effort on the business logic of the application. To gain efficiencies it’s best
to leave the reusable implementation to libraries, however, applications end up with lots of dependencies, shown
in orange below:

A Spring service with multiple dependencies

So how do developers focus unit tests on the unit(s) under test (controller and service) if most of its functionality
depends on the behavior of these dependencies? With so many dependencies included in unit test, they end
up being more like integration tests. Better control over the behavior of these dependencies is required and if
dependencies aren’t available, some way to simulate them is needed.

Better isolation of the application from dependencies is needed in order to focus unit tests on application code.
One option is to create specialized “testing” versions of these dependencies. However, using a standardized
library like Mockito provides benefits over this approach for multiple reasons:

• There is no need to write and maintain the special “test” code

• Mocking libraries can track invocations against mocks, providing an extra layer of validation

• Standard libraries like PowerMock provide additional functionality, like mocking static methods, private
methods, or constructors

• Knowledge of a mocking library like Mockito can be re-used across projects, whereas knowledge of
custom testing code can’t be reused

A mocked service replaces multiple dependencies

© 2018 Parasoft Corporation


8

Dependencies in Spring

In general, Spring applications split functionality into Beans. A Controller might depend on a Service Bean, and
the Service Bean might depend on an EntityManager, JDBC connection, or another Bean. Most of the time, the
dependencies from which the code under test needs to be isolated are beans. In an integration test, it makes
sense that all layers should be real – but for unit tests, a decision is needed for which dependencies should be
real and which should be mocks.

Spring allows developers to define and configure beans using either XML, Java, or a combination of both to
provide a mixture of mocked and real beans in an application configuration. Since mock objects need to be
defined in Java, a Configuration class should be used to define and configure the mocked beans.

Using the Unit Test Assistant to Automate Mocking

When the Unit Test Assistant generates a Spring test, all dependencies for a controller are set up as mocks so that
each test gains control over the dependency. When the test is run, the Unit Test Assistant detects method calls
made on a mock object for methods that do not yet have method mocking configured, and recommends that those
methods should be mocked. This quick-fix is used to automatically mock each method.

Here is an example controller which depends on a PersonService:

@Controller
@RequestMapping(“/people”)
public class PeopleController {
 
    @Autowired
    protected PersonService personService;
    @GetMapping
    public ModelAndView people(Model model){
   
        for (Person person : personService.getAllPeople()) {
            model.addAttribute(person.getName(), person.getAge());
        }
        return new ModelAndView(“people.jsp”, model.asMap());
    }
}

And an example test, generated by the Unit Test Assistant:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class PeopleControllerTest {
 
    @Autowired
    PersonService personService;
 
    // Other fields and setup
 
    @Configuration
    static class Config {
 
        // Other beans
 
        @Bean
        public PersonService getPersonService() {
            return mock(PersonService.class);
        }
    }
 

© 2018 Parasoft Corporation


9

    @Test
    public void testPeople() throws Exception {
        // When
        ResultActions actions = mockMvc.perform(get(“/people”));
    }
}

Here, the test uses an inner class annotated with @Configuration, which provides bean dependencies for the
Controller under test using Java configuration. This allows us to mock the PersonService in the bean method. No
methods are mocked yet, when the test is run in Parasoft Jtest, the following recommendation appears:

This means that the getAllPeople() method was called on the mocked PersonService object, but the test does not
yet configure mocking for this method. Choosing the “Mock it” quick-fix option, the test is updated:

    @Test
    public void testPeople() throws Exception {
        Collection<Person> getAllPeopleResult = new ArrayList<Person>();
        doReturn(getAllPeopleResult).when(personService).getAllPeople();
        // When
        ResultActions actions = mockMvc.perform(get(“/people”));

When the test is run again, it passes. The Collection object that gets returned by getAllPeople() should be
populated, however, the challenge of setting up the mocked dependencies is solved.

Note that it’s possible to move the generated method mocking from the test method into the Configuration class’s
bean method. If this is done, it means that each test in the class mocks the same method in the same way. Leaving
the method mocking in the test method means that the method can be mocked differently between different tests.

Mocking with Spring Boot

Spring Boot makes bean mocking even easier. Instead of using an @Autowired field for the bean in the test and a
Configuration class that defines it, simply use a field for the bean and annotate it with @MockBean. Spring Boot
will create a mock for the bean using the mocking framework it finds on the classpath, and inject it the same way
any other bean in the container can be injected. When generating Spring Boot tests with the Unit Test Assistant,
the @MockBean functionality is used instead of the Configuration class.

@SpringBootTest
@AutoConfigureMockMvc
public class PeopleControllerTest {
    // Other fields and setup – no Configuration class needed!
 
    @MockBean
    PersonService personService;
 
    @Test
    public void testPeople() throws Exception {
        ...

© 2018 Parasoft Corporation


10

    }
}

XML vs Java Configuration

In the first example above, the Configuration class provided all the beans to the Spring container. Alternatively,
XML configuration can be used for the test instead of the Configuration class; or a combination of the two. For
example:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ “classpath:/**/testContext.xml” })
public class PeopleControllerTest {
 
    @Autowired
    PersonService personService;
 
    // Other fields and setup
 
    @Configuration
    static class Config {
        @Bean
        @Primary
        public PersonService getPersonService() {
            return mock(PersonService.class);
        }
    }
 
    // Tests
}

Here, the class references an XML configuration file in the @ContextConfiguration annotation (not shown here) to
provide most beans, which could be real beans or test-specific beans. A @Configuration class is also provided,
where PersonService is mocked. The @Primary annotation indicates that even if a PersonService bean is found
in the XML configuration, this test uses the mocked bean from the @Configuration class instead. This type of
configuration can make testing code smaller and easier to manage. It’s possible to Configure the Unit Test
Assistant to generate tests using any specific @ContextConfiguration attributes as needed.

Mocking Static Methods

Sometimes, dependencies are accessed statically. For example, an application might access a third-party service
through a static method call:

public class ExternalPersonService {
    public static Person getPerson(int id) {
       RestTemplate restTemplate = new RestTemplate();
       try {
           return restTemplate.getForObject(“http://domain.com/people/” + id, Person.
class);
        } catch (RestClientException e) {
            return null;
        }
    }
}

© 2018 Parasoft Corporation


11

In the Controller:

    @GetMapping
    public ResponseEntity<Person> getPerson(@PathVariable(“id”) int id, Model model)
    {
        Person person = ExternalPersonService.getPerson(id);
        if (person != null) {
            return new ResponseEntity<Person>(person, HttpStatus.OK);
        }
        return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }

In this example, the handler method uses a static method call to get a Person object from a third-party service.
When we build a JUnit test for this handler method, a real HTTP call would be made to the service each time the
test is run.

Instead, let’s mock the static ExternalPersonService.getPerson() method. This prevents the HTTP call, and allows
us to provide a Person object response that suits our testing needs. The Unit Test Assistant can make it easier to
mock static methods with PowerMockito.

The Unit Test Assistant generates a test for the handler method above which looks like this:

    @Test
    public void testGetPerson() throws Throwable {
        // When
        long id = 1L;
        ResultActions actions = mockMvc.perform(get(“/people/” + id));
 
        // Then
        actions.andExpect(status().isOk());
    }

When the test is run, the HTTP call is being made in the Unit Test Assistant Flow Tree. However, in this example,
the call to ExternalPersonService.getPerson() is mocked instead:

The test is updated to mock the static method for this test using PowerMock:

© 2018 Parasoft Corporation


12

    @Test
    public void testGetPerson() throws Throwable {
        spy(ExternalPersonService.class);
 
        Person getPersonResult = null; // Unit Test Assistant: default value
        doReturn(getPersonResult).when(ExternalPersonService.class,”getPerson”, 
anyInt());
 
        // When
        int id = 0;
        ResultActions actions = mockMvc.perform(get(“/people/” + id));
 
        // Then
        actions.andExpect(status().isOk());
    }

Using the Unit Test Assistant, select the getPersonResult variable and instantiate it, so that the mocked method
call doesn’t return null:

    String name = “”; // Unit Test Assistant: default value


    int age = 0; // Unit Test Assistant: default value
    Person getPersonResult = new Person(name, age);

When the test is run again,  getPersonResult  is returned from the mocked  ExternalPersonService.getPerson() 
method, and the test passes.

Note:  From the flow tree, it’s possible to choose “Add Mockable Method pattern” for static method calls. This
configures the Unit Test Assistant to always mock those static method calls when generating new tests.

CONCLUSION

Spring (combined with Spring Boot) is the leading enterprise Java application framework and as such needs
an appropriate level of testing to ensure the quality and security of applications built with it. Unfortunately, this
level of testing is not being achieved currently, mostly due to a lack of time and the amount of manual coding
and maintenance required. Complex applications often have functional dependencies that complicate and limit a
developer’s ability to unit test their code. Using a mocking framework like Mockito can help developers isolate the
code under test from these dependencies, enabling them to write better unit tests more quickly. The Parasoft Jtest
Unit Test Assistant provides not just unit test automation but guided test creation and dependency management,
to accelerate test creation and reduce maintenance.

ABOUT PARASOFT www.parasoft.com


Parasoft helps organizations perfect today’s highly-connected applications by automating time- Parasoft Headquarters:
consuming testing tasks and providing management with intelligent analytics necessary to focus +1-626-256-3680
on what matters. Parasoft’s technologies reduce the time, effort, and cost of delivering secure, Parasoft EMEA:
reliable, and compliant software, by integrating static and runtime analysis; unit, functional, +31-70-3922000
and API testing; and service virtualization. With developer testing tools, manager reporting/ Parasoft APAC:
analytics, and executive dashboarding, Parasoft supports software organizations with the +65-6338-3628
innovative tools they need to successfully develop and deploy applications in the embedded,
enterprise, and IoT markets, all while enabling today’s most strategic development initiatives Copyright 2018. All rights reserved. Parasoft and all Parasoft products and services listed within are trademarks or
registered trademarks of Parasoft Corporation. All other products, services, and companies are trademarks, regis-
— agile, continuous testing, DevOps, and security. tered trademarks, or servicemarks of their respective holders in the US and/or other countries.

You might also like