Book Spring Boot 2: Best Practices for Professionals

imageHello, habrozhiteli! Want to increase your effectiveness in developing enterprise and cloud Java applications?

Increase the speed and ease of developing microservices and complex applications, eliminating the worries of Spring configuration.

Use Spring Boot 2 and Spring 5 framework tools like WebFlux, Security, Actuator, and the Micrometer framework, which provides a new way to collect metrics.

Spring Testing Framework

One of the main ideas of the Spring framework is to encourage developers to create simple and loosely coupled classes, programming interfaces, so that the software becomes more reliable and better extensible. The Spring framework provides tools that simplify unit and integration testing (in fact, if you really program interfaces, you don’t need to test the functionality of Spring applications). In other words, it is necessary that the application can be tested using JUnit or TestNG testing systems based on objects (created simply using the new operator – without Spring or any other container).

The Spring framework includes several packages designed for unit or integration testing of applications. For unit testing, several simulation objects are intended (Environment, PropertySource, JNDI, Servlet; reactive testing utilities ServerHttpRequest and ServerHttpResponse), with which you can perform isolation testing of code.

One of the most commonly used testing capabilities in the Spring framework is integration testing. Its main tasks:

  • Managing Spring IoC container caching between test runs
  • transaction management;
  • Dependency injection of test object instances
  • creation of base classes designed specifically for Spring.

The Spring framework provides an easy way to test by integrating into the application context tests (ApplicationContext). The Spring testing module offers several ways to use ApplicationContext programmatically and using annotations.

  • Abstract BootstrapWith. Class level annotation for configuring the bootstrap of the TestContext Spring framework.
  • @ContextConfiguration annotation. Defines class level metadata that defines how to load and configure ApplicationContext for integration tests. This is a must-have annotation for your classes, because this is where ApplicationContext loads all component definitions.
  • Abstract @WebAppConfiguration. A class-level annotation indicating that the application context loaded for the integration test should be WebApplicationContext.
  • Annotation @ActiveProfile. A class level annotation indicating which of the component definition profiles should be active when loading the ApplicationContext for the integration test.
  • Abstract @TestPropertySource. A class level annotation designed to specify the locations of property files and embedded properties that are added to the PropertySource object set in Environment for the ApplicationContext loaded for the integration test.
  • Abstract @DirtiesContext. Indicates that the used ApplicationContext was “dirty” during the test (for example, modified or damaged by changing the state of a single component) and should be closed.

These are far from all annotations, there are many others (@TestExecutionListeners, Commit, Rollback, @BeforeTransaction, @AfterTransaction, Sql, SqlGroup, SqlConfig, Timed, Repeat, @IfProfileValue, etc.).

As you can see, there are many testing options with the Spring framework. Usually the @RunWith annotation is always used, linking together all the elements of the testing framework. For instance:

@RunWith(SpringRunner.class)
@ContextConfiguration({"/app-config.xml", "/test-data-access-config.xml"})
@ActiveProfiles("dev")
@Transactional
public class ToDoRepositoryTests {

        @Test
        public void ToDoPersistenceTest(){
                //...
        }
}

Now let’s see how to use the Spring Testing Framework and what features Spring Boot provides.

Spring Boot Testing Framework

Spring Boot leverages the power of the Spring Testing Framework, expanding on the old and adding new annotations and features that make testing easier for developers.

To start using all the features of Spring Boot testing, you just need to add the spring-boot-starter-test dependency to the application with the test (scope test) scope. The Spring Initializr service has already added this dependency.

The spring-boot-starter-test dependency provides the ability to use several testing frameworks that are very consistent with the Spring Boot testing capabilities: JUnit, AssertJ, Hamcrest, Mockito, JSONassert, and JsonPath. Of course, there are other testing frameworks that work fine with the Spring Boot Test module; just the corresponding dependencies need to be specified manually.

Spring Boot provides the @SpringBootTest annotation to simplify testing Spring applications. Typically, when testing a Spring application, you need to add a few annotations to test a specific application feature or functionality, but not in Spring Boot – although for testing you still need to specify the @RunWith annotation (SpringRunner.class); if this is not done, any new Spring Boot testing annotations will be ignored. The @SpringBootTest annotation has options useful for testing web applications, such as RANDOM_PORT and DEFINED_PORT.

The following code snippet is the Spring Boot test framework.

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {

        @Test
        public void exampleTest() {
                 ...
        }
}

Web Application Endpoint Testing

Spring Boot provides a convenient way to test endpoints: a simulation environment called the MockMvc class:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class MockMvcToDoTests {

        @Autowired
        private MockMvc mvc;

        @Test
        public void toDoTest() throws Exception {
              this.mvc
              .perform(get("/todo"))
              .andExpect(status().isOk())
              .andExpect(content()
                   .contentType(MediaType.APPLICATION_JSON_UTF8));
        }
}

You can also use the TestRestTemplate class.

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ToDoSimpleRestTemplateTests {

      @Autowired
      private TestRestTemplate restTemplate;

      @Test
      public void toDoTest() {
            String body = this.restTemplate.getForObject("/todo", String.class);
            assertThat(body).contains("Read a Book");
      }
}

This code shows a test that starts a full server and uses an instance of the TestRestTemplate class to access the / todo endpoint. Here we assume that the String object is returned (this is not the best way to test JSON return; don’t worry, we will show how to use the TestRestTemplate class correctly).

Component imitation

The Spring Boot testing module provides an @MockBean annotation that describes a Mockito simulation object for a component in ApplicationContext. In other words, you can create an imitation of a new Spring component or replace an existing definition by adding this annotation. Remember: all this happens inside the ApplicationContext.

@RunWith(SpringRunner.class)
@SpringBootTest
public class ToDoSimpleMockBeanTests {

        @MockBean
        private ToDoRepository repository;

        @Test
        public void toDoTest() {
              given(this.repository.findById("my-id"))
                    .Return(new ToDo("Read a Book"));
              assertThat(
                    this.repository.findById("my-id").getDescription())
                    .isEqualTo("Read a Book");
        }
}

Spring Boot Test Slices

One of the most important features of Spring Boot is running tests without the need for any specific infrastructure. The Spring Boot testing module includes so-called slices designed to test specific parts of an application without using a server or DBMS.

@JsonTest annotation

The Spring Boot testing module has an @JsonTest annotation that simplifies serialization / deserialization of JSON objects and checks to see if everything works correctly. @JsonTest automatically configures the supported JSON display tool, depending on the library found on the class path: Jackson, GSON, or JSONB.

@RunWith(SpringRunner.class)
@JsonTest
public class ToDoJsonTests {

      @Autowired
      private JacksonTester json;

      @Test
      public void toDoSerializeTest() throws Exception {
            ToDo toDo = new ToDo("Read a Book");

            assertThat(this.json.write(toDo))
            .isEqualToJson("todo.json");
            assertThat(this.json.write(toDo))
            .hasJsonPathStringValue("@.description");

            assertThat(this.json.write(toDo))
            .extractingJsonPathStringValue("@.description")
            .isEqualTo("Read a Book");
      }

      @Test
      public void toDoDeserializeTest() throws Exception {
            String content = "{"description":"Read a Book","completed":
                  true }";
            assertThat(this.json.parse(content))
                  .isEqualTo(new ToDo("Read a Book", true));
            assertThat(
               this.json.parseObject(content).getDescription())
            .isEqualTo("Read a Book");
      }
}

To test controllers without using a full-fledged server, you can use the Spring Boot proposed annotation @WebMvcTest, which automatically configures the Spring MVC infrastructure and restricts the list of viewed components to the following: Controller, ControllerAdvice, @JsonComponent, Converter, GenericConverter, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver; thanks to this, you will know if your controllers work as expected.

It is important to understand that components marked as Component are not visible when using this annotation, but you can use the @MockBean annotation if necessary.

@RunWith(SpringRunner.class)
@WebMvcTest(ToDoController.class)
public class ToDoWebMvcTest {

      @Autowired
      private MockMvc mvc;

      @MockBean
      private ToDoRepository toDoRepository;

      @Test
      public void toDoControllerTest() throws Exception {
            given(this.toDoRepository.findById("my-id"))
                  .Return(new ToDo("Do Homework", true));
                  this.mvc.perform(get("/todo/my-id").accept(MediaType.APPLICATION_
                     JSON_UTF8))
                        .andExpect(status().isOk()).andExpect(content().
                           string("{"id":"my-id","description":"Do Homework",
                           "completed":true}"));
      }
}

@WebFluxTest annotation

For reactive controllers, Spring Boot provides the @WebFluxTest annotation. This annotation automatically configures the Spring WebFlux module infrastructure and only searches for Controller, ControllerAdvice, @JsonComponent, Converter, GenericConverter and WebFluxConfigurer.

It is important to understand that components marked as Component are not visible when using this annotation, but you can use the @MockBean annotation if necessary.

@RunWith(SpringRunner.class)
@WebFluxTest(ToDoFluxController.class)
public class ToDoWebFluxTest {

      @Autowired
      private WebTestClient webClient;

      @MockBean
      private ToDoRepository toDoRepository;

      @Test
      public void testExample() throws Exception {
             given(this.toDoRepository.findAll())
                         .Return(Arrays.asList(new ToDo("Read a Book"), new
                               ToDo("Buy Milk")));
             this.webClient.get().uri("/todo-flux").accept(MediaType.
                  APPLICATION_JSON_UTF8)
                       .exchange()
                       .expectStatus().isOk()
                       .expectBody(List.class);
      }
}

@DataJpaTest annotation

For testing JPA applications, the Spring Boot testing module provides the @DataJpaTest annotation, which automatically configures the built-in databases located in RAM. It searches for Entity and does not load any Component components. In addition, it provides a test-oriented helper class TestEntityManager, very similar to the JPA EntityManager class.

@RunWith(SpringRunner.class)
@DataJpaTest
public class TodoDataJpaTests {
  
      @Autowired
      private TestEntityManager entityManager;

      @Autowired
      private ToDoRepository repository;

      @Test
      public void toDoDataTest() throws Exception {
            this.entityManager.persist(new ToDo("Read a Book"));
            Iterable toDos = this.repository.
                   findByDescriptionContains("Read a Book");
            assertThat(toDos.iterator().next()).toString().contains("Read a Book");
      }
}

Please note that when testing with @DataJpaTest, built-in DBMSs in RAM are used. For testing with a real database, you must provide the test class with the annotation @AutoConfigureTestDatabase (replace = Replace.NONE).

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace=Replace.NONE)
public class TodoDataJpaTests {
        //...
}

@JdbcTest annotation

This annotation is very similar to @DataJpaTest; the only difference is that it runs exclusively on JDBC tests. It automatically configures the built-in DBMS located in the RAM and the JdbcTemplate class, while skipping all the annotated Component classes.

@RunWith(SpringRunner.class)
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class TodoJdbcTests {

      @Autowired
      private NamedParameterJdbcTemplate jdbcTemplate;

      private CommonRepository repository;

        @Test
        public void toDoJdbcTest() {
              ToDo toDo = new ToDo("Read a Book");

            this.repository = new ToDoRepository(jdbcTemplate);
            this.repository.save(toDo);

            ToDo result = this.repository.findById(toDo.getId());
            assertThat(result.getId()).isEqualTo(toDo.getId());
        }
}

@DataMongoTest annotation

For testing MongoDB applications, the Spring Boot testing module provides the @DataMongoTest annotation. It automatically configures the built-in, memory-based Mongo server, if available; if not, you need to add the necessary properties of spring.data.mongodb. *. It also tunes the MongoTemplate class and looks for @Document annotations. Component components are skipped.

@RunWith(SpringRunner.class)
@DataMongoTest
public class TodoMongoTests {

      @Autowired
      private MongoTemplate mongoTemplate;

      @Test
      public void toDoMongoTest() {
          ToDo toDo = new ToDo("Read a Book");
          this.mongoTemplate.save(toDo);

          ToDo result = this.mongoTemplate.findById(toDo.getId(),ToDo.class);
          assertThat(result.getId()).isEqualTo(toDo.getId());
      }
}

If you need an external MongoDB server (non-embedded, in-memory) add the excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class parameter to the @DataMongoTest annotation.

@RunWith(SpringRunner.class)
@DataMongoTest(excludeAutoConfiguration = EmbeddedMongoAutoConfiguration.class)
public class ToDoMongoTests {
        // ...
}

@RestClientTest annotation

Another important annotation is @RestClientTest, designed to test REST clients. This annotation automatically makes settings to support Jackson, GSON, and JSONB, and also sets up the RestTemplateBuilder class and adds support for MockRestServiceServer.

@RunWith(SpringRunner.class)
@RestClientTest(ToDoService.class)
public class ToDoRestClientTests {

      @Autowired
      private ToDoService service;

      @Autowired
      private MockRestServiceServer server;

      @Test
      public void toDoRestClientTest()
                  throws Exception {
             String content = "{"description":"Read a Book","completed":
                   true }";
            this.server.expect(requestTo("/todo/my-id"))
             .andRespond(withSuccess(content,MediaType.APPLICATION_JSON_UTF8));
            ToDo result = this.service.findById("my-id");
            assertThat(result).isNotNull();
            assertThat(result.getDescription()).contains("Read a Book");
      }
}

There are many other slices available for use. The main thing to remember: for testing, a complete infrastructure with all running servers is optional. Slices make it easier to test Spring Boot applications.

about the author

Felipe Gutierrez (Felipe Gutierrez) is a software architect who holds bachelor’s and master’s degrees in computer engineering from the Institute of Technology and Higher Education in Monterrey, Mexico. Gutierrez has over 20 years of IT experience and has developed programs for companies from a variety of vertically integrated industries such as government, retail, healthcare, education and banking. He currently works at Pivotal, specializing in PAS and PKS for Cloud Foundry, Spring framework, native cloud applications Spring, Groovy and RabbitMQ, among other technologies. He has also been a software architect for large companies such as Nokia, Apple, Redbox, and Qualcomm. Gutierrez is the author of Spring Boot Messaging (Apress, 2017) and Introducing Spring Framework (Apress, 2014).

About science editors

Original edition
Manuel Jordan Helera (Manuel Jordan Elera) – a self-taught developer and researcher, loves to learn new technologies for his experiments and their new combinations. Manuel won the Springy Award Community Champion and Spring Champion 2013. He devotes a little free time to reading the Bible and composing guitar music. Manuel is known by the internet pseudonym dr_pompeii. He has edited many books scientifically, including Pro Spring, 4th Edition (Apress, 2014) 1, Practical Spring LDAP (Apress, 2013), Pro JPA 2, 2nd Edition (Apress, 2013), and Pro Spring Security (Apress , 2013).

Russian edition
Valery Alekseevich Dmitrushchenkov Has been working in IT for over 35 years. He developed software for many companies and industries, managed many complex projects, including the development, implementation and maintenance of automated systems. He participated in the creation of large projects for government bodies implemented by international organizations: the United Nations, USIAD, World Bank in Russia, Kosovo, Moldova and Armenia.

»More details on the book can be found at publishing site

Table of contents

Excerpt

For Khabrozhiteley 25% discount on the coupon – Spring boot

Upon payment of the paper version of the book, an electronic book is sent by e-mail.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *