반응형

이번 시간에는 테스트 코드를 리팩토링해보는 시간을 가져보겠습니다.

 

 

지난 시간에 작성한 게시글에서 테스트 코드 리팩토링의 필요성에 대해서 간단하게 언급하였지만, 다시 한번 정리해보도록 하겠습니다.

지금 우리는 각 테스트에서 필요한 엔티티 또는 DTO 인스턴스를 직접 생성하고 있습니다.

또한, 각 테스트(테스트 클래스에 작성되는 메소드)마다 작성되는 인스턴스 생성 코드의 중복을 제거하기 위해, 테스트 클래스의 메소드로 추출하여 엔티티 또는 DTO 인스턴스를 생성하고 있습니다.

이렇게 해서 하나의 테스트 클래스 내에 작성되는 인스턴스 생성 코드의 중복은 제거할 수 있었지만, 여러 테스트 클래스에서 걸쳐서 동일한 엔티티 또는 DTO 클래스를 생성하는 코드는 계속해서 중복되고 있습니다.

지금은 테스트가 몇 개 작성되지 않았기 때문에 많은 수의 중복은 나타나고 있지 않지만, 테스트가 늘어남에 따라 동일한 인스턴스 생성 코드의 중복이 지속적으로 증가할 우려가 있습니다.

중복이 나타나고 있으며, 이 중복이 계속 증가한다는 것은, 코드 개선의 여지가 남아있다는 것입니다.

이러한 중복을 제거하기 위해 테스트 코드를 리팩토링 해보도록 하겠습니다.

 

 

우리는 중복을 제거하기 위해, 엔티티와 DTO 인스턴스 생성을 담당해주는 팩토리 클래스를 만들주도록 하겠습니다.

test 디렉토리에 factory 패키지를 생성해주고, factory 패키지 내에 entity 패키지와 dto 패키지를 생성해줍니다.

entity 패키지에 작성되는 팩토리 클래스는, 각 엔티티 인스턴스를 생성해주는 책임을 갖게 됩니다.

dto 패키지에 작성되는 팩토리 클래스는, 각 DTO 인스턴스를 생성해주는 책임을 갖게 됩니다.

 

 

먼저 entity 패키지에 MemberFactory를 작성해줍시다.

package kukekyakya.kukemarket.factory.entity;

public class MemberFactory {

}

 

이제 기존에 작성했던 테스트 클래스들을 살펴보면서, Member 엔티티 인스턴스를 생성하던 코드를 모두 찾아서 MemberFactory에 넣어줍시다.

SignServiceTest, MemberServiceTest, MemberRepositoryTest 에서 createMember 또는 createMemberWith~와 같은 형태의 메소드들을, 모두 MemberFactory 클래스로 옮겨주겠습니다.

 

 

package kukekyakya.kukemarket.factory.entity;

import kukekyakya.kukemarket.entity.member.Member;
import kukekyakya.kukemarket.entity.member.Role;

import java.util.List;

import static java.util.Collections.emptyList;

public class MemberFactory {

    public static Member createMember() {
        return new Member("email@email.com", "123456a!", "username", "nickname", emptyList());
    }

    public static Member createMember(String email, String password, String username, String nickname) {
        return new Member(email, password, username, nickname, emptyList());
    }

    public static Member createMemberWithRoles(List<Role> roles) {
        return new Member("email@email.com", "123456a!", "username", "nickname", roles);
    }

}

메소드는 모두 퍼블릭 스태틱 메소드로 바꿔줍시다.

그리고 기존의 테스트 클래스에 작성된 인스턴스 생성 메소드를 제거하고, 새로운 팩토리 클래스의 메소드를 사용하도록 스태틱 임포트 해주겠습니다.

팩토리 메소드의 원활한 재사용을 위해, 전달받는 파라미터를 제외한 기본 값들은, 의도했던 규칙에 맞게 값을 설정해주도록 하겠습니다.

예를 들어, email은 이메일 양식에 따라서, password는 비밀번호 생성 규칙(알파벳, 숫자, 특수문자 포함, 8자리 이상)에 따라서 설정해줍니다.

 

 

다음으로 entity 패키지에 RoleFactory를 작성해주고, RoleRepositoryTest에서 Role 엔티티 인스턴스 생성 코드를 RoleFactory 클래스로 옮겨줍니다.

package kukekyakya.kukemarket.factory.entity;

import kukekyakya.kukemarket.entity.member.Role;
import kukekyakya.kukemarket.entity.member.RoleType;

public class RoleFactory {
    public static Role createRole() {
        return new Role(RoleType.ROLE_NORMAL);
    }
}

이번에도 퍼블릭 스태틱 메소드로 바꿔줍니다.

Member 팩토리 메소드를 스태틱 임포트했던 것처럼, 기존의 테스트 클래스에 작성된 Role 생성 메소드를 제거하여 새로운 팩토리 클래스의 팩토리 메소드를 스태틱 임포트해줍니다.

 

 

이어서, dto 패키지에 SignInRequestFactory, SignUpRequestFactory, SignInResponse 클래스를 작성해줍니다.

Sign 관련 처리를 위해 각각의 DTO 인스턴스를 생성해줄 수 있도록 합니다.

이 두 종류의 DTO 인스턴스를 생성해주는 메소드를 SignServiceTest, SignInRequestValidationTest, SignUpRequestValidationTest에서 찾아다가 각각에 맞는 팩토리 클래스로 옮겨줍니다.

SignControllerTest, SignControllerAdviceTest, MemberControllerIntegerationTest에서 직접 인스턴스를 생성하는 코드도 팩토리 메소드로 대체해줍니다.

package kukekyakya.kukemarket.factory.dto;

import kukekyakya.kukemarket.dto.sign.SignInRequest;

public class SignInRequestFactory {
    public static SignInRequest createSignInRequest() {
        return new SignInRequest("email@email.com", "123456a!");
    }

    public static SignInRequest createSignInRequest(String email, String password) {
        return new SignInRequest(email, password);
    }

    public static SignInRequest createSignInRequestWithEmail(String email) {
        return new SignInRequest(email, "123456a!");
    }

    public static SignInRequest createSignInRequestWithPassword(String password) {
        return new SignInRequest("email@email.com", password);
    }
}
package kukekyakya.kukemarket.factory.dto;

import kukekyakya.kukemarket.dto.sign.SignUpRequest;

public class SignUpRequestFactory {

    public static SignUpRequest createSignUpRequest() {
        return new SignUpRequest("email@email.com", "123456a!", "username", "nickname");
    }

    public static SignUpRequest createSignUpRequest(String email, String password, String username, String nickname) {
        return new SignUpRequest(email, password, username, nickname);
    }

    public static SignUpRequest createSignUpRequestWithEmail(String email) {
        return new SignUpRequest(email, "123456a!", "username", "nickname");
    }

    public static SignUpRequest createSignUpRequestWithPassword(String password) {
        return new SignUpRequest("email@email.com", password, "username", "nickname");
    }

    public static SignUpRequest createSignUpRequestWithUsername(String username) {
        return new SignUpRequest("email@email.com", "123456a!", username, "nickname");
    }

    public static SignUpRequest createSignUpRequestWithNickname(String nickname) {
        return new SignUpRequest("email@email.com", "123456a!", "username", nickname);
    }
}
package kukekyakya.kukemarket.factory.dto;

import kukekyakya.kukemarket.dto.sign.SignInResponse;

public class SignInResponseFactory {
    public static SignInResponse createSignInResponse(String accessToken, String refreshToken) {
        return new SignInResponse(accessToken, refreshToken);
    }
}

이번에도 퍼블릭 스태틱 메소드로 바꿔줍니다.

Member 팩토리 메소드를 스태틱 임포트했던 것처럼, 기존의 테스트 클래스에 작성된 Sign 관련 DTO 생성 메소드 또는 코드를 제거하여 새로운 팩토리 클래스의 팩토리 메소드를 스태틱 임포트해줍니다.

팩토리 메소드의 원활한 재사용을 위해, 전달받는 파라미터를 제외한 기본 값들은, 의도했던 규칙에 맞게 값을 설정해주도록 하겠습니다.

IDE의 힘을 빌려서(윈도우에서 intellij라면 CTRL + ALT + F), 또 다른 인스턴스 생성 코드가 있는지 점검해봅시다.

 

 

이제 리팩토링이 성공적으로 잘 이루어졌는지, 작성된 테스트를 모두 수행보겠습니다.

모든 테스트 성공

모든 테스트가 성공적으로 수행되었습니다.

 

이제 엔티티 또는 DTO 인스턴스를 생성할 일이 생기더라도, 각 테스트 클래스마다 새로운 팩토리 메소드를 작성할 필요 없이, 우리가 미리 작성해둔 팩토리 클래스를 이용하면 됩니다.

 

 

이번 시간에는, 팩토리 클래스를 정의하여 인스턴스 생성 책임을 넘겨주었습니다.

코드에 커다란 변화가 있지는 않았지만, 앞으로 테스트가 늘어감에 따라 발생할 수 있는 인스턴스 생성 코드의 중복을 미연에 방지하고 제거할 수 있었습니다.

 

다음 시간에는, 리프레시 토큰을 이용하여 액세스 토큰을 재발급하는 로직을 구현하고, 로그인 기능을 마무리 짓도록 하겠습니다.

 

 

* 질문 및 피드백은 환영입니다.

* 전체 소스코드에서는 여기에서 확인해볼 수 있습니다.

https://github.com/SongHeeJae/kuke-market

반응형

+ Recent posts