이 장에서는 TDD 와 Unit Test의 차이점에 대해 소개했다.
TDD 는
1. 항상 실패하는 Unit Test 작성
2. 테스트가 통과하는 프로덕션 코드 작성
3. 트스트가 통과하면 프로덕션 코드 리팩토링
이라고 한다면
Unit Test 는 TDD 1단계인 기능 단위의 테스트 코드 작성을 의미한다. 이 장에서는 Unit Test 위주로 설명이 나와 있다.
@SpringBootApplication 은 스프링 부트의 자동 설정, 스프링 Bean 읽기와 생성을 자동으로 생성한다.
항상 서버에 톰캣을 설치할 필요가 없고 언제 어디서나 같은 환경에서 스프링 부트를 배포할 수 있게된다.
package com.springboot.book.springbootwebservice.web;
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";
}
}
package com.springboot.book.springbootwebservice.web;
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 hello_return() throws Exception {
String hello = "hello";
mvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string(hello));
}
}
Bean : Spring IoC(Inversion of Control) 컨테이너에 의해 인스턴스화, 조립 및 관리되는 객체
종속성 주입에 사용된다.@Autowired private MockMvc mvc;
이 코드는 mvc 라는 필드에 MockMvc 인스턴스를 삽입한다.
- MovckMvc 클래스는 Spring MVC 테스트 프레임워크의 일부이며 HTTP 요청 및 응답을 시뮬레이트 하여 Spring MVC 애플리케이션을 테스트하는 방법을 제공한다.
- 단위 테스트의 맥락에서 MockMvc를 사용하면 완전한 웹 서버를 시작하지 않고도 Spring MVC 컨트롤러의 동작을 독립적으로 테스트할 수 있다.
spring boot unit test initializationError .... ControllerAdvice
책의 프로젝트와 달리 spring boot 프로젝트 생성시 java 17을 사용하도록 설정했었는데 jUnit 은 4를 사용하는등 conflict 가 발생한 것 같다.
그래서 @RunWith 는 jUnit4를, @ExtendWith 는 jUnit5를 사용하는데 다음과 같이 수정하여 실행하니 테스트코드가 정상적으로 작동했다.
package com.springboot.book.springbootwebservice.web;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
//import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
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;
@ExtendWith(SpringExtension.class)
//@RunWith(SpringRunner.class)
@WebMvcTest
@ComponentScan(basePackageClasses = HelloController.class)
public class HelloControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void hello가_리턴된다() throws Exception {
String hello = "hello";
mvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string(hello));
}
}
compile('org.projectlombok:lombok')
package com.springboot.book.springbootwebservice.web.dto;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public class HelloResponseDto {
private final String name;
private final int amount;
}
variable not initialized in the default constructor
원인 : 롬북이 정상적으로 작동하고 있지 않다.
- gradle 버전 확인
- gradle 5.x 이상의 경우
- 결과
=> 게터, 세터, constructor 를 annotation 하나만으로 자동으로 생성해준다는게 굉장히 매력적이다.
package com.springboot.book.springbootwebservice.web.dto;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class HelloResponseDtoTest {
@Test
public void 롬복_기능_테스트() {
//given
String name = "test";
int amount = 1000;
//when
HelloResponseDto dto = new HelloResponseDto(name, amount);
//then
assertThat(dto.getName()).isEqualTo(name);
assertThat(dto.getAmount()).isEqualTo(amount);
}
}
package com.springboot.book.springbootwebservice.web;
import com.springboot.book.springbootwebservice.web.dto.HelloResponseDto;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
@GetMapping("/hello/dto") // 추가
public HelloResponseDto helloDto(@RequestParam("name") String name,
@RequestParam("amount") int amount) {
return new HelloResponseDto(name, amount);
}
}
package com.springboot.book.springbootwebservice.web;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
//import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
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;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.hamcrest.Matchers.is;
@ExtendWith(SpringExtension.class)
//@RunWith(SpringRunner.class)
@WebMvcTest
@ComponentScan(basePackageClasses = HelloController.class)
public class HelloControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void hello가_리턴된다() throws Exception {
String hello = "hello";
mvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string(hello));
}
@Test // 추가
public void helloDto가_리턴된다() throws Exception {
String name = "hello";
int amount = 1000;
mvc.perform(
get("/hello/dto")
.param("name", name)
.param("amount", String.valueOf(amount)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name", is(name)))
.andExpect(jsonPath("$.amount", is(amount)));
}
}