[이동준] 테스트 코드 작성하기

Updated:

🧶테스트 코드 작성하기

Java 디렉토리를 마우스 오른쪽 버튼으로 클릭하여, [New -> Package]를 차례로 선택해서 새로운 패키지를 생성한다.

테스트 코드 작성 - 1

일반적으로 패키지명은 웹 사이트 주소의 역순으로 한다. 예를 들어 admin.java.com 이라는 사이트라면 패키지명은 com.java.admin으로 하면 된다.

freelec 프로젝트를 생성했을 때 사용한 Group id인 ‘com.webservice.book’를 이용해 패키지명을 ‘com.webservice.book.web’으로 지정하겠다.

테스트 코드 작성 - 2

이제 새로 생성한 패키지 아래에 Java 클래스를 생성한다. 패키지와 마찬가지로 [New -> Java class]를 차례로 선택한다.

클래스의 이름은 MainApp으로 지정했다.

테스트 코드 작성 - 3

MainApp 클래스의 코드는 다음과 같이 작성한다.

package com.webservice.book.web;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MainApp {
    public static void main(String args[]){
        SpringApplication.run(MainApp.class, args);
    }

}

스프링 부트는 main 메소드가 선언된 클래스를 기준으로 실행되기 때문에 MainApp 클래스는 이 프로젝트의 메인 클래스이다.

@SprinBootApplication으로 인해 스프링 부트의 자동 설정, 스프링 Bean 읽기와 생성이 모두 자동으로 설정된다.


🎲@SpringBootApplication

@SpringBootApplication은 자동 설정을 해주기 위한 기본 어노테이션으로, 코드를 열어보면 아래와 같다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	// 중략..
}

여기서 중요한 것은 스프링 고유 어노테이션 @EnableAutoConfiguration, @ComponentScan, @SpringBootConfiguration이다.


🎲EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	// 중략..
}

스프링 부트의 Application Context 설정을 자동으로 수행한는 어노테이션으로,META-INF/spring.factories에 정의되어 있는 configuration 대상 클래스들을 빈으로 등록해준다.

🧩하지만, 어째서인지 내가 만든 스프링 부트 gradle 프로젝트에선 META-INF가 보이지 않았다. 그래서 다른 블로그의 글을 참고해서 작성했다.

spring.factories를 열어 보면, 엄청나게 많은 클래스들이 스프링 부트 기본 auto configuration 대상으로 나열이 되어 있다.

// ...
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
// ...

이 리스트에 있는 클래스들은 @Configuration 어노테이션이 없어도 자동으로 빈으로 등록된다. 추가로 개발자가 직접 이 spring.factories에 클래스를 적어 넣으면 그 클래스 역시 auto configuration 대상에 포함된다.


🎲ComponentScan

@Component 어노테이션 및 streotype(@Service, @Repository, @Controller)어노테이션이 부여된 클래스들을 자동으로 Scan하여 Bean으로 등록해주는 역할을 하는 어노테이션이다.


main 메소드에서 실행하는 SpringApplication.run으로 인해 내장 WAS;Web Application Server를 실행한다. 내장 WAS란 별도로 외부에 WAS를 두지 않고 Application을 실행할 때 내부에서 WAS를 실행하는 것을 이야기 한다. 이렇게 되면 항상 서버에 Tomcat과 같은 외부 WAS를 설치할 필요가 없고, 스프링 부트로 만들어진 Jar 파일(실행 가능한 Java 패키징 파일)로 실행하면 된다.

꼭 스프링 부트에서만 내장 WAS를 사용할 수 있는 것은 아니지만, 스프링 부트에서는 내장 WAS를 사용하는 것을 권장하고 있다. 이유는 ‘언제 어디서나 같은 환경에서 스프링 부트를 배포’할 수 있기 때문이다. 외장 WAS를 쓴다고 하면 모든 서버는 WAS의 종류와 버전, 설정을 일치시켜야만 한다. 새로운 서버가 추가되면 모든 서버가 같은 WAS 환경을 구축해야만 한다. 이렇게 될 경우 실수할 여지도 많고 시간이 많이 소모되기 때문에 큰 작업이 될 수도 있다. 하지만 이렇게 내장 WAS를 사용할 경우 이 문제를 모두 해결할 수 있다.

Application 클래스에 대한 설명이 끝났으니, 테스트를 위한 Controller를 만들어 보자. 이번에는 현재 패키지 하위에 controller라는 패키지를 만들어 보겠다. 방법은 위와 동일하다. 앞으로 컨트롤러와 관련된 클래스들은 모두 이 패키지에 담겠다. 그리고 테스트해 볼 컨트롤러 클래스를 생성하자 클래스의 이름은 HelloController로 지정하겠다.

package com.webservice.book.web.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello(){
        return "hello";
    }
}

@RestController : 컨트롤러를 JSON을 반환하는 컨트롤러로 만들어 준다. 예전에는 @ResponseBody를 각 메소드마다 선언했던 것을 한 번에 사용할 수 있게 해준 것이라 생각하자

@GetMapping : HTTP Method인 Get의 요청을 받을 수 있는 API를 만들어 준다. 예전에는 @RequestMapping(method = RequestMethod.GET)으로 사용되었다. 이제 이 프로젝트는 /hello로 요청이 오면 문자열 hello를 반환하는 기능을 가지게 되었다.

작성한 코드가 제대로 작동하는 지 테스트를 하겠다. WAS를 실행하지 않고, 테스트 코드로 먼저 검증해보겠다. src/test/java 디렉토리에 앞에서 생성했던 패키지를 그대로 다시 생성하자 그리고 테스트 코드를 작성할 클래스를 생성한다. 일반적으로 테스트 클래스는 대상 클래스 이름에 Teset를 붙인다. 그러므로 여기서는 HelloControllerTest로 생성한다.

테스트 코드 작성 - 4

package com.webservice.book.web.controller;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = HelloController.class)
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void returnHello() throws Exception{
        String hello = "hello";

        mvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string(hello));
    }
}

@RunWith(SpringRunner.class)

  1. 테스트를 진행할 때 JUnit에 내장된 실행자 외에 다른 실행자를 실행시킨다.
  2. 여기서는 SPringRunner라는 스프링 실행자를 사용한다.
  3. 즉, 스프링 부트 테스트와 JUnit 사이에 연결자 역할을 한다.

@WebMvcTest

  1. 여러 스프링 테스트 어노테이션 중, Web(Spring MVC)에 집중할 수 있는 어노테이션이다.
  2. 선언할 경우 @Controller, @ControllerAdvice 등을 사용할 수 있다.
  3. 단, @Service, @Component, @Repository 등은 사용할 수 없다.
  4. 여기서는 컨트롤러만 사용하기 때문에 선언한다.

@Autowired

  1. 스프링이 관리하는 빈(Bean)을 주입 받는다.

private MockMvc mvc

  1. 웹 API를 테스트할 때 사용한다.
  2. 스프링 MVC 테스트의 시작점이다.
  3. 이 클래스를 통해 HTTP GET, POST등에 대한 API를 테스트 할 수 있다.

mvc.perform(get(“/hello”))

  1. MockMvc를 통해 /hello 주소로 HTTP GET 요청을 한다.
  2. 체이닝이 지원되어 아래와 같이 여러 검증 기능을 이어서 사용할 수 있다.

.andExpect(status().isOk())

  1. mvc.perform의 결과를 검증한다.
  2. HTTP Header의 Status를 검증한다.
  3. 우리가 흔히 알고 있는 200, 404, 500등의 상태를 검증한다.
  4. 여기선 OK 즉, 200인지 아닌지를 검증한다.

.andExpect(content().string(hello))

  1. mvc.perform의 결과를 검증한다.
  2. 응답 본문의 내용을 검증한다.
  3. Controller에서 “hello”를 리턴하기 때문에 이 값이 맞는 지 검증한다.

테스트 코드 작성 - 5

Leave a comment