How to Write Junit Test Cases on Dto in Java
About |
A working developer's guide to open source tools and frameworks for Java application development.
JUnit 5 tutorial, part 2: Unit testing Spring MVC with JUnit 5
Unit test a Spring MVC service, controller, and repository with JUnit 5, Mockito, MockMvc, and DBUnit
Spring MVC is one of the most popular Java frameworks for building enterprise Java applications, and it lends itself very well to testing. By design, Spring MVC promotes the separation of concerns and encourages coding against interfaces. These qualities, along with Spring's implementation of dependency injection, make Spring applications very testable.
This tutorial is the second half of my introduction to unit testing with JUnit 5. I'll show you how to integrate JUnit 5 with Spring, then introduce you to three tools that you can use to test Spring MVC controllers, services, and repositories.
download
Download the source code for example applications used in this tutorial. Created by Steven Haines for JavaWorld.
Integrating JUnit 5 with Spring 5
For this tutorial, we are using Maven and Spring Boot, so the first thing that we need to do is add the JUnit 5 dependency to our Maven POM file:
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.6.0</version> <scope>test</scope> </dependency>
Just like we did in Part 1, we'll use Mockito for this example. So, we're going to need to add the JUnit 5 Mockito library:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>3.2.4</version> <scope>test</scope> </dependency>
@ExtendWith and the SpringExtension class
JUnit 5 defines an extension interface, through which classes can integrate with JUnit tests at various stages of the execution lifecycle. We can enable extensions by adding the @ExtendWith
annotation to our test classes and specifying the extension class to load. The extension can then implement various callback interfaces, which will be invoked throughout the test lifecycle: before all tests run, before each test runs, after each test runs, and after all of the tests have run.
Spring defines a SpringExtension
class that subscribes to JUnit 5 lifecycle notifications to create and maintain a "test context." Recall that Spring's application context contains all of the Spring beans in an application and that it performs dependency injection to wire together an application and its dependencies. Spring uses the JUnit 5 extension model to maintain the test's application context, which makes writing unit tests with Spring straightforward.
After we've added the JUnit 5 library to our Maven POM file, we can use the SpringExtension.class
to extend our JUnit 5 test classes:
@ExtendWith(SpringExtension.class) class MyTests { // ... }
The example, in this case, is a Spring Boot application. Fortunately the @SpringBootTest
annotation already includes the @ExtendWith(SpringExtension.class)
annotation, so we only need to include @SpringBootTest
.
Adding the Mockito dependency
To properly test each component in isolation and simulate different scenarios, we're going to want to create mock implementations of each class's dependencies. Here is where Mockito comes in. Include the following dependency in your POM file to add support for Mockito:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>3.2.4</version> <scope>test</scope> </dependency>
After you've integrated JUnit 5 and Mockito into your Spring application, you can leverage Mockito by simply defining a Spring bean (such as a service or repository) in your test class using the @MockBean
annotation. Here's our example:
@SpringBootTest public class WidgetServiceTest { /** * Autowire in the service we want to test */ @Autowired private WidgetService service; /** * Create a mock implementation of the WidgetRepository */ @MockBean private WidgetRepository repository; ... }
In this example, we're creating a mock WidgetRepository
inside our WidgetServiceTest
class. When Spring sees this, it will automatically wire it into our WidgetService
so that we can create different scenarios in our test methods. Each test method will configure the behavior of the WidgetRepository
, such as by returning the requested Widget
or returning an Optional.empty()
for a query for which the data is not found. We'll spend the remainder of this tutorial looking at examples of various ways to configure these mock beans.
The Spring MVC example application
To write Spring-based unit tests, we need an application to write them against. Fortunately, we can use the example application from my Spring Series tutorial "Mastering Spring framework 5, Part 1: Spring MVC." I used the example application from that tutorial as a base application. I modified it with a stronger REST API so that we'd have a few more things to test.
The example application is a Spring MVC web application with a REST controller, a service layer, and a repository that uses Spring Data JPA to persist "widgets" to and from an H2 in-memory database. Figure 1 is an overview.
Unit testing a Spring service
Let's start by reviewing how to test a Springservice, because that is the easiest component in our MVC application to test. Examples in this section will allow us to explore JUnit 5's integration with Spring without introducing any new testing components or libraries, though we will do that later in the tutorial.
We'll begin by reviewing the WidgetService
interface and the WidgetServiceImpl
class, which are shown in Listing 1 and Listing 2, respectively.
Listing 1. The Spring service interface (WidgetService.java)
package com.geekcap.javaworld.spring5mvcexample.service; import com.geekcap.javaworld.spring5mvcexample.model.Widget; import java.util.List; import java.util.Optional; public interface WidgetService { Optional<Widget> findById(Long id); List<Widget> findAll(); Widget save(Widget widget); void deleteById(Long id); }
Listing 2. The Spring service implementation class (WidgetServiceImpl.java)
package com.geekcap.javaworld.spring5mvcexample.service; import com.geekcap.javaworld.spring5mvcexample.model.Widget; import com.geekcap.javaworld.spring5mvcexample.repository.WidgetRepository; import com.google.common.collect.Lists; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Optional; @Service public class WidgetServiceImpl implements WidgetService { private WidgetRepository repository; public WidgetServiceImpl(WidgetRepository repository) { this.repository = repository; } @Override public Optional<Widget> findById(Long id) { return repository.findById(id); } @Override public List<Widget> findAll() { return Lists.newArrayList(repository.findAll()); } @Override public Widget save(Widget widget) { // Increment the version number widget.setVersion(widget.getVersion()+1); // Save the widget to the repository return repository.save(widget); } @Override public void deleteById(Long id) { repository.deleteById(id); } }
WidgetServiceImpl
is a Spring service, annotated with the @Service
annotation, that has a WidgetRepository
wired into it through its constructor. The findById()
, findAll()
, and deleteById()
methods are all passthrough methods to the underlying WidgetRepository
. The only business logic you'll find is located in the save()
method, which increments the version number of the Widget
when it is saved.
The test class
In order to test this class, we need to create and configure a mock WidgetRepository
, wire it into the WidgetServiceImpl
instance, and then wire the WidgetServiceImpl
into our test class. Fortunately, that's far easier than it sounds. Listing 3 shows the source code for the WidgetServiceTest
class.
Listing 3. The Spring service test class (WidgetServiceTest.java)
package com.geekcap.javaworld.spring5mvcexample.service; import com.geekcap.javaworld.spring5mvcexample.model.Widget; import com.geekcap.javaworld.spring5mvcexample.repository.WidgetRepository; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Arrays; import java.util.List; import java.util.Optional; import static org.mockito.Mockito.doReturn; import static org.mockito.ArgumentMatchers.any; @SpringBootTest public class WidgetServiceTest { /** * Autowire in the service we want to test */ @Autowired private WidgetService service; /** * Create a mock implementation of the WidgetRepository */ @MockBean private WidgetRepository repository; @Test @DisplayName("Test findById Success") void testFindById() { // Setup our mock repository Widget widget = new Widget(1l, "Widget Name", "Description", 1); doReturn(Optional.of(widget)).when(repository).findById(1l); // Execute the service call Optional<Widget> returnedWidget = service.findById(1l); // Assert the response Assertions.assertTrue(returnedWidget.isPresent(), "Widget was not found"); Assertions.assertSame(returnedWidget.get(), widget, "The widget returned was not the same as the mock"); } @Test @DisplayName("Test findById Not Found") void testFindByIdNotFound() { // Setup our mock repository doReturn(Optional.empty()).when(repository).findById(1l); // Execute the service call Optional<Widget> returnedWidget = service.findById(1l); // Assert the response Assertions.assertFalse(returnedWidget.isPresent(), "Widget should not be found"); } @Test @DisplayName("Test findAll") void testFindAll() { // Setup our mock repository Widget widget1 = new Widget(1l, "Widget Name", "Description", 1); Widget widget2 = new Widget(2l, "Widget 2 Name", "Description 2", 4); doReturn(Arrays.asList(widget1, widget2)).when(repository).findAll(); // Execute the service call List<Widget> widgets = service.findAll(); // Assert the response Assertions.assertEquals(2, widgets.size(), "findAll should return 2 widgets"); } @Test @DisplayName("Test save widget") void testSave() { // Setup our mock repository Widget widget = new Widget(1l, "Widget Name", "Description", 1); doReturn(widget).when(repository).save(any()); // Execute the service call Widget returnedWidget = service.save(widget); // Assert the response Assertions.assertNotNull(returnedWidget, "The saved widget should not be null"); Assertions.assertEquals(2, returnedWidget.getVersion(), "The version should be incremented"); } }
The WidgetServiceTest
class is annotated with the @SpringBootTest
annotation, which scans the CLASSPATH
for all Spring configuration classes and beans and sets up the Spring application context for the test class. Note that WidgetServiceTest
also implicitly includes the @ExtendWith(SpringExtension.class)
annotation, through the @SpringBootTest
annotation, which integrates the test class with JUnit 5.
The test class also uses Spring's @Autowired
annotation to autowire a WidgetService
to test against, and it uses Mockito's @MockBean
annotation to create a mock WidgetRepository
. At this point, we have a mock WidgetRepository
that we can configure, and a real WidgetService
with the mock WidgetRepository
wired into it.
Testing the Spring service
The first test method, testFindById()
, executes WidgetService
's findById()
method, which should return an Optional<Widget>
that contains a Widget
. We begin by creating a Widget
that we want the WidgetRepository
to return. We then leverage the Mockito API to configure the WidgetRepository::findById
method. The structure of our mock logic is as follows:
doReturn(VALUE_TO_RETURN).when(MOCK_CLASS_INSTANCE).MOCK_METHOD
In this case, we're saying: Return an Optional
of our Widget
when the repository's findById()
method is called with an argument of 1 (as a long
).
Next, we invoke the WidgetService
's findById
method with an argument of 1. We then validate that it is present and that the returned Widget
is the one that we configured the mock WidgetRepository
to return.
How to Write Junit Test Cases on Dto in Java
Source: https://www.infoworld.com/article/3543268/junit-5-tutorial-part-2-unit-testing-spring-mvc-with-junit-5.html
0 Response to "How to Write Junit Test Cases on Dto in Java"
Post a Comment