일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 트랜지스터
- 오늘도
- 스프링부트
- 개발자기술면접
- 그래도일단
- 어찌저찌해냄
- 스프링부트와 AWS로 혼자 구현하는 웹 서비스
- 스프링 부트와 AWS로 혼자 구현하는 웹 서비스
- 스프링부트 테스트코드
- Flexbox
- CS
- 자바스크립트
- 기술면접
- 스프링부트와 AWS로 혼자 구현하는 웹서비스
- jpa
- 테스트코드
- 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 2장
- 운영체제
- 내가해냄
- AWS EC2 구현
- Today
- Total
개발 공부
스프링 부트와 AWS로 혼자 구현하는 웹 서비스 2장 - 테스트 코드 본문
1장 정리글
https://paradiseiswhereiam.tistory.com/131
스프링 부트와 AWS로 혼자 구현하는 웹 서비스 1장 (책과 다른 부분 수정)
2019년도에 나온 책이라 그런지 업데이트 된 부분이 중간중간 있어 블로그에 정리해 보려고 한다. 현재까지 수정한 부분은 다음과 같으며 순서대로 작성하였다. 1. p33 build.gradle 맨 위에 위치할 코
paradiseiswhereiam.tistory.com
테스트 코드란?
✔️ TDD(테스트가 주도하는 개발)
: 항상 실패하는 테스트 먼저 작성 ➡️ 테스트가 통과하는 프로덕션 코드 작성 ➡️ 테스트 통과하면 프로덕션 코드 리팩토링
✔️단위 테스트
: TDD의 첫 번째 단계인 기능 단위의 테스트 코드를 작성하는 것
⚫ 왜 테스트 코드가 필요한가?
테스트 코드를 쓰기 전에는 코드 작성 후,
< 프로그램을 톰캣으로 실행 후, API 테스트 도구로 HTTP 요청, 요청 결과를 System.out.println()으로 눈으로 검증, 결과가 다르면 다시 톰캣 중지 후 코드 수정>
이 꺾쇠 안의 과정을 코드를 수정할 때마다 써야 했다.
수정된 기능을 이렇게 톰캣을 실행시켜 직접 확인하지 않으면 확인할 수 없었기 때문이다.
테스트 코드는 구현 도구에서 바로 실행 가능하므로
톰캣을 쓸 필요가 없으며 톰캣 실행에 드는 시간도 아낄 수 있다.
그리고 System.out.prinln()을 써서 사람이 직접 검증하지 않아도 되고,
개발자가 만든 기능을 안전하게 보호해 준다. (테스트 코드를 수행한 기능은 잘 작동하는 기능이라고 보장 가능.)
Hello Controller 테스트 코드 작성하기
패키지명은 따로 적지 않겠습니다!
1. Application.java
package com.jojoldu.book.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication //스프링 부트의 자동설정, 스프링 Bean읽기와 생성 모두 자동으로 설정
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
앞으로 만들 프로젝트의 메인 클래스가 될 클래스.
@SpringBootApplication이 있는 위치부터 설정을 읽어가기 때문에 이 클래스는 항상 프로젝트의 최상단에 위치해야 함
그리고 SpringApplication.run으로 인해 내장 WAS가 실행
➡️ 톰캣이 필요없어지게 됨, 스프링 부트로 만들어진 Jar 파일로 실행하면 됨.
⚫왜 내장 WAS를 사용하는가?
: 스프링 부트에서도 권장하고 있고, 언제 어디서나 같은 환경에서 스프링 부트를 배포할 수 있기 때문!
ex. 새로운 서버가 추가 될 때마다 n대의 서버에 설치 된 WAS의 버전을 일일히 올려야 됨
2. HelloController.java
간단하게 API 만들기
package com.jojoldu.book.springboot.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";
}
}
@RestController : 컨트롤러를 JSON으로 반환하는 컨트롤러로 만들어 줌
이전의 @ResponseBody를 각 메소드마다 선언했던 것을 한번에 사용할 수 있게 해주는 것
@GetMapping: HTTP Method인 Get의 요청을 받을 수 있는 API를 만들어 줌
이것으로 이 프로젝트는 /hello로 요청이 오면 "hello"를 반환하는 기능을 갖게 됨
3. HelloControllerTest.java
테스트 코드 작성은 test 디렉토리에 같은 이름의 패키지와 끝에 Test를 붙인 클래스 이름의 java 파일을 만들어 실행
package com.jojoldu.book.springboot;
import com.jojoldu.book.springboot.web.HelloController;
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.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
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)
@WebMvcTest(controllers = HelloController.class)
public class HelloControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void helloIsReturn() throws Exception {
String hello = "hello";
mvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string(hello));
}
}
perform(get, status, content에 오류 생기는 분들은 import 덜해서 생긴 거니까
위 코드의 import static~으로 시작하는 저 3줄의 코드 추가해주시길!
@ExtendWith(SpringExtension.class): 테스트 클래스 또는 메소드의 동작을 확장하기 위한 어노테이션
옵션으로 @ExtendWith(SpringExtension.class)와 @ExtendWith(MockExtension.class)가 많이 쓰이는데 책에서는 전자를 사용한다.
SpringExtension.class는 Spring TextContext Framework를 JUnit 5의 주피터 프로그래밍 모델에 통합해서 사용한다.
MockExtension.class는 JUnit4 Mockito와 동등한 JUnit Jupiter이며, 구현하는 인터페이스가 SpringExtension.class보다 (당연하게도)적어서 Mokito와 관련된 MockContext기반에서 좀 더 가볍게 진행 가능하다.
@WebMvcTest: 여러 스프링 테스트 어노테이션 중, Web(Spring MVC)에 집중할 수 있는 어노테이션
(@Controller 등을 쓸 수 있으나 @Service, @Component, @Repository는 못 씀)
@AutoWired: 스프링이 관리하는 빈을 주입 받음
@private MockMvc mvc: 웹 API를 테스트할 때 사용. 스프링 MVC 테스트의 시작점 (이 클래스를 통해 HTTP GET, POST 등에 대한 API 테스트 가능)
@mvc.perform(get("/hello")): MockMvc를 통해 /hello 주소로 HTTP GET 요청
@.andExpect(status().isOk()): mvc.perform의 결과를 검증. HTTP Header의 Status를 검증 (200, 404, 500 등의 상태 검증)
@.andExpect(content().string(hello)): mvc.perform 결과를 검증. 응답 본문 내용을 검증. Controller에서 "hello"를 리턴하기 때문에 이 값이 맞는지 검증
Run 하면 테스트 통과하는 걸 볼 수 있다.
이렇게 테스트 코드 통과 후! 그래도 찝찝하면 그때 Application.java 파일로 이동하여 수동으로 실행해본다.
그럼 실행 로그에 커다란 스프링 로고와 함께 잘 되었다는 것이 뜨고
아랫줄의 오른쪽 부근을 보면 톰캣 서버가 8080포트로 실행되었다는 것도 로그에 출력됨.
실행이 끝났다면 웹 브라우저를 열어서 localhost:8080/hello 로 접속!
그러면 웹 브라우저의 흰 바탕에 hello 문자열이 떠 있는 걸 확인할 수 있다.
Hello Controller 코드를 롬복으로 전환하기
롬복 설치는 앞에 의존성 주입할 때만 주의하면 문제없이 진행할 수 있다.
⚫대규모의 프로젝트였다면 이렇게 쉽게 롬복으로 전환이 가능했을까?에 대해 생각해 보기
➡️절대 쉽지 않았을 것이다. 하지만 쉽게 변경할 수 있는 이유는 테스트 코드가 우리의 코드를 지켜주기 때문이다.
롬복으로 변경 후, 문제가 생기는 부분은 테스트 코드를 돌려보면 알 수 있다!
롬복 전환을 위해 dto 패키지를 추가하여 파일을 만든다.
4. HelloResponseDto.java
package com.jojoldu.book.springboot.web.dto;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public class HelloResponseDto {
private final String name;
private final int amount;
}
@Getter: 선언된 모든 필드의 get메소드를 생성
@RequiredArgsConstructor: 선언된 모든 final 필드가 포함된 생성자를 생성. final이 없는 필드는 생성자에 포함되지 X
5. HelloResponseDtoTest.java
package com.jojoldu.book.springboot.web.dto;
import com.jojoldu.book.springboot.web.web.dto.HelloResponseDto;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class HelloResponseDtoTest {
@Test
public void lombokTest() {
//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);
}
}
assertThat: asserttj라는 테스트 검증 라이브러리의 검증 메소드.
검증하고 싶은 대상을 메소드 인자로 받음.
⚫왜 Junit의 기본 assertThat이 아닌 asserttj의 assertThat인가?
: 추가적으로 CoreMatchers 라이브러리가 필요하지 않고, Matcher 라이브러리의 자동완성이 좀 더 확실하게 지원됨.
isEqualTo: assertj의 동등 비교 메소드. assertThat에 있는 값과 비교해서 같을 때만 성공
6. HelloController.java에 코드 추가
@GetMapping("/hello/dto")
public HelloResponseDto helloDto(@RequestParam("name") String name,
@RequestParam("amount") int amount) {
return new HelloResponseDto(name, amount);
}
@RequestParam: 외부에서 API로 넘긴 파라미터를 가져오는 어노테이션
➡️외부에서 name이란 이름으로 넘긴 파라미터를 메소드 파라미터 name (String name)에 저장하게 됨
name과 amount는 API를 호출하는 곳에서 넘겨준 값들이다.
7. HelloControllerTest.java
package com.jojoldu.book.springboot.web;
import com.jojoldu.book.springboot.web.web.HelloController;
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.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
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)
@WebMvcTest(controllers = HelloController.class)
public class HelloControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void helloReturn() throws Exception {
String hello = "hello";
mvc.perform(get("/hello"))
.andExpect(status().isOk())
.andExpect(content().string(hello));
}
public void helloDtoReturn() 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)));
}
}
param: API 테스트 할 때 사용될 요청 파라미터를 설정.
값은 String 값만 허용되므로 숫자/날짜 등의 데이터도 문자열로 변경해서 등록.
jsonPath: JSON 응답값을 필드별로 검증할 수 있는 메소드.
$를 기준으로 필드명 명시. (여기서는 name과 amount 검증)
참고
https://velog.io/@geunwoobaek/Spring-Junit5-Test%EC%A0%95%EB%A6%AC
'Spring > Spring Boot' 카테고리의 다른 글
[문제해결] Execution failed for task ':test'.> No tests found for given includes: (0) | 2022.08.23 |
---|---|
스프링 부트와 AWS로 혼자 구현하는 웹 서비스 3장 - JPA (0) | 2022.08.22 |
[문제 해결] Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test (0) | 2022.08.09 |
스프링 부트와 AWS로 혼자 구현하는 웹 서비스 1장 (책과 다른 부분 수정) (0) | 2022.08.08 |
테스트 케이스 작성 (0) | 2022.06.25 |