Csorba Zoltán

JUnit Tools for Spring

Update Site URL

Aim of this project

Create a tool for helping the developers to write simple and maintainable unit tests with minimal effort on the tedious part and let them focus on the important part of the tests.

This plugin promotes the “Test First” approach. Create a well structured test method skeleton from the method signature with

Additional features:

Spring support:

Important note / disclaimer

This plugin has reasonable default settings for creating test method skeletons, but you’ll need to extend and fine tune those settings based on the ever changing requirements of your specific project. The plugin has a configurable set of default values for the input parameters, imports, naming conventions and so on. Feel free adjust these settings according to your needs. Please remember, that testing is a continuous effort that is part of the development task. The test implementation evolves together with the production code, just like the tools that we use.

Samples

You can find the samples below on GitHub. Feel free to clone this repository and play around with the test generation functionality of the plug-in.

Testing static methods

This is the simplest case as the test class does not need any specific annotation, and no need to initialize the tested class either.

Base class Test class
Static method in base class Test for static method

Note, that the gherkin style comments are separating the test method body to distinct blocks that clearly shows what are prerequisites, what is tested and what do we check.

Testing a method with simple input parameters / return value

For simple input parameters (i.e. primitive types) the plug-in is setting some predefined default values instead of leaving them uninitialzed. Same goes for the return value, that an assertEquals will be generated with a predefined default value.

For example

Base class Test class
Method with primitive parameters Test initializing primitive parameters

There will be a preference page for the plug-in to set up the default values you prefer, although it’s just a formality as you’ll need to customize the values anyway according to the logic of the method you’re testing. The main goal of this functionality to reduce the manual effort on writing the initialization steps and the assert clause.

Testing a method without return value

When the method has no return value (i.e. void), then you’ll need to test for some “side effect”, like these

assertThat(param.getChangedField()).isEqualTo("new value");
verify(otherService).otherMethod("some value");
assertThrows(SomeException.class, () -> underTest.someMethod("some value");
assertDoesNotThrow(() -> underTest.someMethod("some value");

Since it’s vary what the side effects can be, the plug-in only generates a TODO comment only. For example

Base class Test class
Method without return value Test for method without return value

Testing a method with complex input parameters

One of the most tedious part of writing a unit test is the preparation of input parameters when mocking is not sufficient. Mocking an input parameter is only useful when you have an interface or a class that is not a Java Bean (i.e. a service or function).

On the other hand, when you need to deal with data then you need to prepare that item with all it’s fields that are relevant for that method. This can require a big effort and lot of extra work.

See the example in the case above, where the method had the DemoObject input parameter. The generated test initializing the parameter with the TestValueFactory.

Test for method without return value

Using TestValueFactory is a nice workaround for this problem. It prefills all fields of given data object (e.g. Java Bean) with consistent and predictable values. So you can rely on that the input data is always the same and you only need to take care of the fields that are special related to that function. For example

But in most of the cases it’s enough to have the default values generated by the factory.

If the input parameter is not a primitive value, then the generator assumes that the TestValueFactory should be used instead and generates to code accordingly (see below).

Testing a method with complex return value

When the tested method is producing a complex data structure in the response, then it can be tedious to test all of details of that object. Either you’re expecting that only a handful of properties were changing as a “side-effect” of the method execution, or all of the fields are important because it the method is creating a new object based upon the input parameters (e.g. a converter from an entity to a DTO).

Base class Test class
Method with complex return value Test for method with complex return value

In my opinion, this makes the test maintenance way easier and this way the test can detect (and protect from) accidental changes resulting from changes in the data model. It may sound controversial, but actually this can help a lot.

For example, the data model is changing in the application and the developer would forgot that this change affects a certain service or converter that is consuming or producing the changed element. The test would show automatically that the output is different from the previously known and expected form. This at least raises the important question: does this class need to be changed also in order to handle the new field, or the field removed is still needed, etc. The answer might be “yes it’s okay”, then update the JSON file in the test-resources folder and done. But in several cases the answer is “oops, I forgot about that this needs to be handled here as well”.

Also note, that the usual testing that is focusing only on the known fields would not detect this problem.

The same solution can be used to check if a dependent service was triggered with the right object. For example, if the method supposed to save an entity to the database with specific field values, then use the following pattern.

	ArgumentCaptor<SomeEntity> entityCaptor = ArgumentCaptor.forClass(SomeEntity.class);
	verify(someRepo).save(entityCaptor.capture());
	assertThat(TestUtils.objectToJson(entityCaptor.getValue()))
		.isEqualTo(TestUtils.readTestFile("dbtestfiles/SomeEntity_saved.json"));

Spring tests are slightly different than the usual basic or mocked test classes. These tests are instantiating a Spring context when they are running, that comes with it’s pros and cons. Advantages:

Disadvantages:

Formal differences:

Good practices:

Example: Testing a Spring service

One important part of testing a class that has some dependencies (i.e. injected fields), that these fields need to be mocked properly.

You can use either Mockito or Spring extensions to execute these tests. There are slight differences in how the class and the fields are annotated, as described above.

Base class Test class with MockitoExtension Test class with SpringExtension
Test class with dependencies Test dependencies with Mockito Test dependencies with Spring

Note, that @ExtendWith(SpringExtension.class) is superfluous, that’s why it’s not generated.

Example: Testing a Spring controller with different endpoints

A special case of testing Spring components when we need to write tests for REST controllers or other public endpoints of the Spring application.

The plug-in generates an enhanced test method for these endpoints that were implemented in methods annotated with some RequestMapping annotation (e.g. GetMapping). The test class is going to initialize a MockMVC component and the test methods are calling the HTTP endpoint instead of direct method calls.

Base class Test class
Controller class with endpoints Test class for Controller with endpoints

Note, it’s better keeping these tests in a separate test class considering the “integrated flavour” and that these a running slightly slower than the other tests. The checking of the results can be also trickier than with the mocked tests. Use the integrated tests only for checking the functionality that depends on the framework (i.e. is the endpoint available instead of resulting a HTTP 404 error, are the default values for the parameters correctly handled, the values correctly parsed from a JSon payload, etc).

Release notes

v1.3.1 EasyMock improvements

v1.3.0 configuration improvements

v1.2.9 bug fixes and configuration improvements

v1.2.8 bug fixes and configuration improvements

v1.2.7 improve assert generation

v1.2.6 improve parameter value generation

v1.2.5 switch between code and test

v1.2.4 documentation and test generation improvements

v1.2.3 sample test util

V1.2.2 rest controllers

V1.2.1 enhanced test methods

V1.2.0 basic functionality