Skip to content

Testing the Web Layer :: 스프링 부트 애플리케이션과 MVC 컨트롤러를 어떻게 테스트하는지 알아봅니다.

Notifications You must be signed in to change notification settings

aeomhs/tl-testing-web

 
 

Repository files navigation

이번 가이드는 스프링 애플리케이션을 제작하고 JUnit을 사용하여 테스트를 진행합니다.

What You Will Build

간단한 스프링 부트 애플리케이션을 빌드하고 JUnit으로 테스트해볼 예정입니다. 이미 개인적으로 단위 테스트를 작성하고 실행해보셨을거라 생각합니다. 그래서 이번 가이드에서는 스프링 테스트와 스프링 부트를 사용하여 스프링과 코드간의 상호작용을 테스트하는 것에 집중하고자 합니다. 애플리케이션 컨텍스트가 성공적으로 로드되는지 간단하게 테스트해보는 것을 시작으로 스프링의 `MockMvc`를 사용하여 웹 계층을 테스트하는 것까지 진행할 예정입니다.

Starting with Spring Initializr

For all Spring applications, you should start with the Spring Initializr. The Initializr offers a fast way to pull in all the dependencies you need for an application and does a lot of the setup for you. This example needs only the Spring Web dependency. The following image shows the Initializr set up for this sample project:

initializr
Note
The preceding image shows the Initializr with Maven chosen as the build tool. You can also use Gradle. It also shows values of com.example and testing-web as the Group and Artifact, respectively. You will use those values throughout the rest of this sample.

The following listing shows the pom.xml file that is created when you choose Maven:

link:initial/pom.xml[role=include]

The following listing shows the build.gradle file that is created when you choose Gradle:

link:initial/build.gradle[role=include]

Create a Simple Application

아래와 같은 스프링 애플리케이션의 새로운 컨트롤러를 생성합니다. (from src/main/java/com/example/testingweb/HomeController.java)

link:complete/src/main/java/com/example/testingweb/HomeController.java[role=include]
Note
위 예제는 GET 혹은 PUT, POST`에 대해서 명세하지 않았습니다. 기본적으로 `@RequestMapping`은 모든 HTTP 기능들을 담당(map)합니다. `@GetMapping 혹은 `@RequestMapping(method=GET)`과 같이 작성하여 범위를 지정할 수 있습니다.

Run the Application

스프링 생성자(The Spring Initializr)는 main() 메서드를 갖는 애플리케이션 클래스를 생성합니다. 이번 가이드는 The Spring Initializr creates an application class (a class with a main() method) for you. For this guide, you need not modify this class. The following listing (from src/main/java/com/example/testingweb/TestingWebApplication.java) shows the application class that the Spring Initializr created:

link:complete/src/main/java/com/example/testingweb/TestingWebApplication.java[role=include]

@SpringBootApplication is a convenience annotation that adds all of the following:

  • @Configuration: Tags the class as a source of bean definitions for the application context.

  • @EnableAutoConfiguration: Tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.

  • @EnableWebMvc: Flags the application as a web application and activates key behaviors, such as setting up a DispatcherServlet. Spring Boot adds it automatically when it sees spring-webmvc on the classpath.

  • @ComponentScan: Tells Spring to look for other components, configurations, and services in the the com.example.testingrestdocs package, letting it find the HelloController class.

The main() method uses Spring Boot’s SpringApplication.run() method to launch an application. Did you notice that there is not a single line of XML? There is no web.xml file, either. This web application is 100% pure Java and you did not have to deal with configuring any plumbing or infrastructure. Spring Boot handles all of that for you.

Logging output is displayed. The service should be up and running within a few seconds.

Test the Application

Now that the application is running, you can test it. You can load the home page at http://localhost:8080. However, to give yourself more confidence that the application works when you make changes, you want to automate the testing.

Note
Spring Boot assumes you plan to test your application, so it adds the necessary dependencies to your build file (build.gradle or pom.xml).

The first thing you can do is write a simple sanity check test that will fail if the application context cannot start. The following listing (from src/test/java/com/example/testingweb/TestingWebApplicationTest.java) shows how to do so:

link:complete/src/test/java/com/example/testingweb/TestingWebApplicationTest.java[role=include]

The @SpringBootTest annotation tells Spring Boot to look for a main configuration class (one with @SpringBootApplication, for instance) and use that to start a Spring application context. You can run this test in your IDE or on the command line (by running ./mvnw test or ./gradlew test), and it should pass. To convince yourself that the context is creating your controller, you could add an assertion, as the following example (from src/test/java/com/example/testingweb/SmokeTest.java) shows:

link:complete/src/test/java/com/example/testingweb/SmokeTest.java[role=include]

Spring interprets the @Autowired annotation, and the controller is injected before the test methods are run. We use AssertJ (which provides assertThat() and other methods) to express the test assertions.

Note
A nice feature of the Spring Test support is that the application context is cached between tests. That way, if you have multiple methods in a test case or multiple test cases with the same configuration, they incur the cost of starting the application only once. You can control the cache by using the @DirtiesContext annotation.

It is nice to have a sanity check, but you should also write some tests that assert the behavior of your application. To do that, you could start the application and listen for a connection (as it would do in production) and then send an HTTP request and assert the response. The following listing (from src/test/java/com/example/testingweb/HttpRequestTest.java) shows how to do so:

link:complete/src/test/java/com/example/testingweb/HttpRequestTest.java[role=include]

Note the use of webEnvironment=RANDOM_PORT to start the server with a random port (useful to avoid conflicts in test environments) and the injection of the port with @LocalServerPort. Also, note that Spring Boot has automatically provided a TestRestTemplate for you. All you have to do is add @Autowired to it.

Another useful approach is to not start the server at all but to test only the layer below that, where Spring handles the incoming HTTP request and hands it off to your controller. That way, almost of the full stack is used, and your code will be called in exactly the same way as if it were processing a real HTTP request but without the cost of starting the server. To do that, use Spring’s MockMvc and ask for that to be injected for you by using the @AutoConfigureMockMvc annotation on the test case. The following listing (from src/test/java/com/example/testingweb/TestingWebApplicationTest.java) shows how to do so:

link:complete/src/test/java/com/example/testingweb/TestingWebApplicationTest.java[role=include]

In this test, the full Spring application context is started but without the server. We can narrow the tests to only the web layer by using @WebMvcTest, as the following listing (from src/test/java/com/example/testingweb/WebLayerTest.java) shows:

@RunWith(SpringRunner.class)
@WebMvcTest
link:complete/src/test/java/com/example/testingweb/WebLayerTest.java[role=include]

The test assertion is the same as in the previous case. However, in this test, Spring Boot instantiates only the web layer rather than the whole context. In an application with multiple controllers, you can even ask for only one to be instantiated by using, for example, @WebMvcTest(HomeController.class).

So far, our HomeController is simple and has no dependencies. We could make it more realistic by introducing an extra component to store the greeting (perhaps in a new controller). The following example (from src/main/java/com/example/testingweb/GreetingController.java) shows how to do so:

link:complete/src/main/java/com/example/testingweb/GreetingController.java[role=include]

Then create a greeting service, as the following listing (from src/main/java/com/example/testingweb/GreetingService.java) shows:

link:complete/src/main/java/com/example/testingweb/GreetingService.java[role=include]

Spring automatically injects the service dependency into the controller (because of the constructor signature). The following listing (from src/test/java/com/example/testingweb/WebMockTest.java) shows how to test this controller with @WebMvcTest:

link:complete/src/test/java/com/example/testingweb/WebMockTest.java[role=include]

We use @MockBean to create and inject a mock for the GreetingService (if you do not do so, the application context cannot start), and we set its expectations using Mockito.

Summary

Congratulations! You have developed a Spring application and tested it with JUnit and Spring MockMvc and have used Spring Boot to isolate the web layer and load a special application context.

About

Testing the Web Layer :: 스프링 부트 애플리케이션과 MVC 컨트롤러를 어떻게 테스트하는지 알아봅니다.

Resources

Contributing

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 94.5%
  • Shell 5.5%