스프링부트와 AWS로 혼자 구현하는 웹서비스-테스트코드 작성

qq·2023년 9월 9일
0

클러치 프로젝트에서 테스트를 하지 않고 서비스를 구현하였는데 리팩토링에 들어가면서 단위 테스트를 도입해보려고 한다

테스트 코드란?

TDD는 테스트가 주도하는 개발이고 테스트 코드를 먼저 작성하는 것부터 시작한다

  • 항상 실패하는 테스트를 먼저 작성(RED)
  • 테스트가 통과하는 프로덕션 코드를 작성하고(GREEN)
  • 테스트가 통과하면 프로덕션 코드를 리팩토링(REFACTOR)

단위테스트는 TDD의 첫 번째 단계인 기능 단위의 테스트 코드를 작성하는 것을 이야기한다. TDD와 달리 테스트 코드를 꼭 먼저 작성 해야하는 것이 아니라 순수하게 테스트 코드만 작성하는 것을 이야기한다.

단위테스트의 장점

  • 단위 테스트는 개발단계 초기에 문제를 발견하게 도와준다
  • 단위 테스트는 개발자가 나중에 코드를 리팩토링하거나 라이브러리 업그레이드 등에서 기존 기능이 올바르게 작동하는지 확인할 수 있다.
  • 단위 테스트는 기능에 대한 불확실성을 감소시킬 수 있다

저자가 제시한 개발 방식

테스트 코드를 작성하지 않았을 때

1. 코드를 작성하고
2. 프로그램을 실행한 뒤
3. Postman과 같은 API 테스트 도구로 HTTP요청하고
4. 요청 결과를 System.out.println()으로 눈으로 검증하고
5. 결과가 다르면 다시 프로그램을 중지하고 코드를 수정한다

이 방법은 현재 내가 개발을 하면서 택하고 있는 방법이고 항상 번거롭다고 느꼈었다. 이번 학습을 통해 테스트 코드를 작성하면서 효율적인 개발 환경을 구축해보고 싶다.

실습

일반적으로 패키지 명은 웹사이트 주소의 역순

@SpringBootApplication으로 인해 스프링 부트의 자동 설정, 스프링 빈 읽기와 생성을 모두 자동으로 설정. 특히 이 어노테이션이 있는 위치부터 읽어가기 때문에 이 클래스는 항상 프로젝트의 최상단에 위치해야 한다

내장 WAS는 별도로 외부에 WAS를 두지 않고 애플리케이션을 실행할 때 내부에서 WAS를 실행하는 것을 이야기한다 -> 항상 서버에 톰캣을 설치할 필요 없이 Jar파일로 실행하면 된다

스프링 부트에서는 내장 WAS를 사용하는 것을 권장하고 있다. 이유는 "언제 어디서나 같은 환경에서 스프링 부트를 배포"할 수 있기 때문이다.


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를 각 메소드마다 선언했던 것을 한번에 사용할 수 있게 해준다고 생각하면 된다

위 코드가 제대로 작동하는지 테스트를 해보았다.
일반적으로 테스트 클래스는 대상 클래스 이름에 Test를 붙인다

  1. 클래스명에 커서를 대고 ctrl + enter를 누른다
  2. HelloControllerTest 클래스를 생성한다 (나는 JUnit5사용)
@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = 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));
    }
}

@ExtendWith(SpringExtension.class): 스프링 부트 테스트와 JUnit 사이에 연결자 역할을 한다

WebMvcTest:여러 스프링 테스트 어노테이션 중 Web에 집중할 수 있는 어노테이션이다.

@Autowired:스프링이 관리하는 Bean을 주입받는다

private MockMvc mvc: 웹 API를 테스트 할 때 사용하고 Spring MVC 테스트의 시작점이다.

mvc.perform(get("/hello")): MockMVC를 통ㅇ해 /hello주소로 HTTP GET요청을 한다

.andExpect(status().isOk()):mvc.perform의 결과를 검증한다. 응답 본문의 내용을 검증한다. Controller에서 "hello"를 리턴하기 때문에 이 값이 맞는지 검증한다

Test해보면 이런 화면을 볼 수 있다

Test가 성공했으니 Build를 해보면 localhost:8080에서 성공적으로 Build가 된것을 볼 수 있다

롬복 적용하기

@Getter
@RequiredArgsConstructor
public class HelloResponseDto {
    private final String name;
    private final int amount;
}

@Getter: 선언된 모든 필드의 get메서드를 생성해준다
@RequiredArgsConstructor:선언된 모든 final 필드가 포함된 생성자를 생성해준다. final이 없는 필드는 생성자에 포함되지 않는다.

public class HelloResponseDtoTest {
    @Test
    public void 롬복_기능_테스트(){
        String name = "test";
        int amount = 1000;

        //when
        HelloResponseDto dto = new HelloResponseDto(name,amount);

        //then
        org.assertj.core.api.Assertions.assertThat(dto.getName()).isEqualTo(name);
        org.assertj.core.api.Assertions.assertThat(dto.getAmount()).isEqualTo(amount);
    }
}

assertThat: assertj라는 테스트ㅡ 검증 라이브러리의 검증 메소드이다. 검증하고 싶은 대상을 메소드 인자로 받는다. 메소드 체이닝이 지원되어 isEqualTo와 같이 메소드를 이어서 사용할 수 있다.

isEqualTo: assertj의 동등 비교 메소드이다. assertThat에 있는 값과 isEqualTo의 값을 비교해서 같을 때만 성공

여기서 Junit의 기본 assertThat을 안쓰는 이유는 assertj는 coreMatchers와 달리 추가적으로 라이브러리가 필요하지 않고 자동완성이 좀 더 확실하게 지원된다

    @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)))
                .andExp![](https://velog.velcdn.com/images/almondbreez0_3/post/f1f363b7-dec5-4fb7-bc37-6a6521a3ad3c/image.jpg)
![](https://velog.velcdn.com/images/almondbreez0_3/post/b830c198-77d5-4fee-a8d0-947ff5cdab1f/image.jpg)
![](https://velog.velcdn.com/images/almondbreez0_3/post/6ca7944c-8a4e-404c-a461-10860860d0d6/image.jpg)
ect(jsonPath("$.amount",is(amount)));
    }

여기까지 생성한 test함수 모두 Test통과함을 볼 수 있다

profile
백엔드 개발자

0개의 댓글