Home Tdd 과정
Post
Cancel

Tdd 과정

TDD

test코드를 먼저 작성 후에 빨간 줄이 뜨는 메소드를 실행 전에 실제 코드에 작성해주면 되는 형식이 tdd

관련 글 : TDD란?


annotation

tdd를 작성하면서 새로 알게 된 부분들은 설명이 있고 원래 알고 있는 어노테이션은 간략하게 적었다.


test

@SpringBootTest : 통합 테스트를 제공하는 기본적인 어노테이션

여러 단위 테스트를 하나의 통합된 테스트로 수행할 때 사용한다.

단, 어플리케이션의 설정을 모두 로드하기 때문에 어플리케이션의 규모가 클수록 느려진다.


@DataJpaTest : SpringBoot에서 JPA를 테스트 할 수 있도록 제공하는 어노테이션

단위 테스트가 끝날 때 마다 자동으로 DB를 롤백시켜준다.

인메모리 DB를 사용한다.

실제 DB에서 테스트를 하고 싶다면 @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) 어노테이션을 추가한다.


  • 참고
Annotation설명Bean
@SpringBootTest통합 테스트, 전체Bean 전체
@WebMvcTest단위 테스트, Mvc 테스트MVC 관련된 Bean
@DataJpaTest단위 테스트, Jpa 테스트JPA 관련 Bean
@RestClientTest단위 테스트, Rest API 테스트일부 Bean
@JsonTest단위 테스트, Json 테스트일부 Bean



@Disabled : 테스트를 실행하지 않게 설정하는 어노테이션


@AutoConfigureMockMvc : 이 어노테이션을 통해 MockMvc를 Builder 없이 주입 받을 수 있다.


@ExtendWith(SpringExtension.class) : JUnit5의 lifecycle에 test에서 사용할 기능을 확장할 때 사용된다.

Spring TestContext Framework와 Junit5와 통합하여 사용하게 된다.


@Import(BoardServiceImpl.class) : BoardServiceImpl도 설정파일임을 명시


@MockBean : @WebMvcTest를 이용한 테스트에서 사용할 수 있다.

@WebMvcTest는 Controller를 테스트할 때 주로 이용되며,

단일 클래스의 테스트를 진행하므로 @MockBean을 통해 가짜 객체를 만들어 준다.

→ Controller 객체까지만 생성되고 Service 객체는 생성하지 않는다.


@MockBean은 위와 같이 Bean 컨테이너에 객체(Service)가 있어야 다른 객체(Controller)와 협력할 수 있는데,

객체를 만들 수 없는 경우 @WebMvcTest에 사용할 수 있다.



SpringBootTest에서 Mock 사용(테스트 더블)

mock이란 가짜 객체라고 불리며, 행위를 검증하기 위해 사용되는 객체이다.

테스트 더블 : 테스트를 진행하기 어려운 경우 테스트를 대신 진행할 수 있게 만드는 객체
*Mock 객체의 상위호환


SpringBootTest에서 테스트 더블 → Mockito

Mockito는 아래와 같은 동작들을 할 수 있다.

■ Mock 만들기(CreateMock)

■ Mock의 동작 지정 (Stub)

■ Mock의 사용(Exercise)

■ 검증(Verify)


@Mock@MockBean은 Mock 객체를 선언할 때 쓰이는 어노테이션이다.

Spring Boot Container가 테스트 시에 필요하고,

Bean이 Container에 존재한다면 @MockBean을 사용하고 아닌 경우에는 @Mock을 사용한다.





model

@entity : JPA가 관리하는 클래스


@Data : @ToString, @EqualsAndHashCode, @Getter, @Setter, @RequiredArgsConstructor이 포함된 어노테이션

*개별 어노테이션의 설정 값을 기본 값이 아닌 값을 사용할 때는 @Data 대신 개별 어노테이션을 사용한다.


@Builder

@Builder : 매개변수가 많아지면 코드를 읽기 어렵고 어떤 순서로 매개변수를 넣어야하는지 헷갈리는데
@Builder를 활용하면 가독성이 좋아진다.


Registry registry = new Registry("hello", "hi", "uni", "etc")

Registry 클래스는 생성자 파라미터를 4개를 받지만 파라미터로 받아야하는 값이 많아지면

각 값들이 어떤 값을 의미하는지 이해하기 힘들다.


이를 @Builder 패턴으로 구현하면 무슨 값을 의미하는지 파악하기 쉽다.

만약 파라미터가 5개인 경우에 5개 파라미터를 모두 사용하는 경우에는 직접 설정하는 대신 @Builder만 작성해도 된다.

5개 중에 한 두개만 사용하는 경우에는 직접 설정한다.

모델명.테이블 명(설정할 변수 명) *모델명 생략

1
2
3
4
5
6
7
8
public Registry toEntity(){
    return Registry.builder()
        .id(registryId)
        .title(registryTitle)
        .main(registryMain)
        .writer(registryWriter)
        .build();
}




*생성자 체인

생성자 체인은 this 또는 super 키워드를 사용해서 생성자에서 다른 생성자를 호출하는 기술

Java에서 생성자의 이름을 직접 호출할 수 없으므로 this와 super 키워드를 사용한다.


this는 동일한 클래스의 생성자에서 다른 생성자를 호출할 때 사용한다.

super는 자식 클래스 생성자에서 부모 클래스 생성자를 호출할 때 사용한다.

1
2
3
4
5
6
public Registry(RegistryDto registryDto){ 
    this.registryId = registryDto.getRegistryId();
    this.registryTitle = registryDto.getRegistryTitle();
    this.registryMain = registryDto.getRegistryMain();
    this.registryWriter = registryDto.getRegistryWriter();
}

this는 entity를 의미

dto를 이용해서 체인 기술을 사용했다.





Error

Controller를 tdd Post 코드를 작성 하면서 오류를 해결한 과정을 적어봤다.



☝🏻 DTO

image

처음에 내가 dto에 값을 담아준 것으로 생각하고 왜 에러가 떴는지 몰랐다.

알고보니 dto에 값을 담아준 것이 아니라 이 자리에 BoardDto가 들어와야 한다고 알려준 것이다.

그리고 dto로 설정에서 dto에 담아서 보내줘야 한다.

그래서 new BoardDto를 작성해준 것이다.



GET도 dto로 작성?


내가 작성한 get 코드는 아래처럼 dto를 사용하지 않았다.

1
2
3
given(boardService.getRegistry("23")).willReturn(
    new Board("23", "title", "main", "writer")                       
);

그 이유는 본 코드를 보면 알 수 있는데 파라미터를 post에서는 dto를 담고 있고

get에서는 String BoardId를 담고 있었기 때문에 dto가 아닌 값을 주어서 dto를 사용하지 않은 것이다.

무조건 dto를 쓰는 것이 아니라 어떻게 짜냐에 따라 쓸 수 있고 안쓸 수도 있다.




✌🏻 메모리 주소가 달라서 생기는 오류

1번에서 말한 대로 수정한 후에 실행을 했더니 에러가 나타났다.

image


ERROR : java.lang.AssertionError: No value at JSON path "$.boardId"

new Dto → any(Dto.class)로 해결

1
2
given(boardService.saveRegistry(new BoardDto("23","title", "main", "writer")))
    .willReturn(new Board("23","title", "main", "writer"));

⬇️

1
2
given(boardService.saveRegistry(any(BoardDto.class)))
    .willReturn(new Board("23","title", "main", "writer"));

new BoardDto(~~)any(BoardDto.class)로 변경했다.


RegistryDto 클래스 타입으로 들어오는 값을 사용한다는 의미로

new 로 새로운 객체를 만들어서 작성하지 않고 구현체 객체를 사용해 메모리 주소가 다르지 않아서 오류가 해결 된 것 같다.

본 코드처럼 dto는 들어오는 값을 쓴다라는 느낌



출처

[Java] Lombok @Data 어노테이션
빌더 패턴(Builder pattern)을 써야하는 이유, @Builder
Builder란?
[Java]생성자 체인(Constructor Chaining)
Spring Junit5 Test정리
[Spring Boot]스프링부트 사용하기 - Structuring Your Code / Configuration Classes(a.k.a Bean 설정) / Auto-configuration
[Mock과 Mocktio] @Mock @MockBean
JPA repository.save is Null (NullPointerException) - feat. @RunWith & @SpringBootTest & @DataJpaTest

This post is licensed under CC BY 4.0 by the author.