ParameterizedTest

AKMUPLAY·2023년 12월 4일

DO-SOPT-SERVER

목록 보기
1/3
post-thumbnail

테스트 코드를 작성하다보면 아래와 같이 중복되는 코드를 작성할 때가 있다.

	@Test
    @DisplayName("멤버의 이름이 2자 미만이라면 예외를 발생시킨다.")
    void validateMemberNameLengthNotLessThanTwo() {
        // given
        String name = "1";

        // when, then
        assertThatThrownBy(() -> new Member(name))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("사용자의 이름의 길이가 적절하지 않습니다.");
    }

    @Test
    @DisplayName("멤버의 이름이 10자를 초과한다면 예외를 발생시킨다.")
    void validateMemberNameLengthNotMoreThanTenTen() {
        // given
        String name = "sopt33thchannamgung";

        // when, then
        assertThatThrownBy(() -> new Member(name))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("사용자의 이름의 길이가 적절하지 않습니다.");
    }

    @Test
    @DisplayName("멤버의 이름이 숫자를 포함하지 않는다면 예외를 발생시킨다.")
    void validateMemberNameIncludeNumber() {
        // given
        String name = "akmu";

        // when, then
        assertThatThrownBy(() -> new Member(name))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("사용자의 이름 형식이 적절하지 않습니다.");
    }

위 코드는 모두 멤버의 이름을 테스트하는 메서드이다.

여러가지 상황에 대해 테스트를 진행하려다 보니 중복된 코드도 많고 가독성도 좋지 않다.
이를 해결할 수 있는 방법이 있을까? 없으면 이 글을 쓰지도 않았다.
있다.

ParameterizedTest

@ParameterizedTest는 여러 개의 테스트를 한 테스트 코드에서 실행할 수 있도록 한다.

위 코드를 @ParameterizedTest를 이용해 작성하면 아래와 같이 작성할 수 있다.

	@ParameterizedTest
    @DisplayName("멤버의 이름이 조건에 맞지 않는다면 예외를 발생시킨다.")
    @ValueSource(strings = {"1", "sopt33thchannamgung", "akmu"})
    void validateMemberName(String name) {
        // when, then
        assertThatThrownBy(() -> new Member(name))
                .isInstanceOf(IllegalArgumentException.class);
    }

여러 테스트 케이스를 한 번에 테스트함을 통해 중복된 코드를 확 줄이고 가독성도 좋아진 모습을 확인할 수 있다.

@ParameterizedTest의 사용법을 더 익혀보자.

@ValueSource

@ValueSource는 한 가지 형식의 값으로 여러 번의 테스트를 진행할 때 사용한다.
아래의 값들에 적용 가능하다.

사용법은 위 예제와 같다.

@CsvSource

@CsvSource는 다수의 값들로 여러 번의 테스트를 진행할 때 사용한다.

아래 예제를 확인해보자.

    @Test
    @DisplayName("200%는 악동뮤지션의 앨범 [PLAY]의 타이틀곡이다.")
    void checkPLAY() {
        // given
        String song = "200%";
        String album = "PLAY";

        // when
        Akmu akmu = new Akmu(song);
        String result = akmu.getType();

        // then
        assertThat(result).isEqualTo(album);
    }

    @Test
    @DisplayName("RE-BYE는 악동뮤지션의 앨범 [사춘기 상(思春記 上)]의 타이틀곡이다.")
    void checkREBYE() {
        // given
        String song = "RE-BYE";
        String album = "사춘기 상";

        // when
        Akmu akmu = new Akmu(song);
        String result = akmu.getType();

        // then
        assertThat(result).isEqualTo(album);
    }

    @Test
    @DisplayName("오랜 날 오랜 밤은 악동뮤지션의 앨범 [사춘기 하 (思春記 下)]의 타이틀 곡이다.")
    void check오랜날오랜밤() {
        // given
        String song = "오랜 날 오랜 밤";
        String album = "사춘기 하";

        // when
        Akmu akmu = new Akmu(song);
        String result = akmu.getType();

        // then
        assertThat(result).isEqualTo(album);
    }

    @Test
    @DisplayName("어떻게 이별까지 사랑하겠어, 널 사랑하는 거지는 악동뮤지션의 앨범 [항해]의 타이틀 곡이다.")
    void check어떻게이별까지사랑하겠어널사랑하는거지() {
        // given
        String song = "어떻게 이별까지 사랑하겠어, 널 사랑하는 거지";
        String album = "항해";

        // when
        Akmu akmu = new Akmu(song);
        String result = akmu.getType();

        // then
        assertThat(result).isEqualTo(album);
    }

위 예제는 입력 값과 출력 값을 가지고 테스트를 진행하고 있다.
@CsvSource를 사용하면 위 예제를 아래와 같이 나타낼 수 있다.

    @ParameterizedTest
    @DisplayName("악동뮤지션의 노래를 입력 받고 그에 맞는 앨범을 나타낼 수 있다.")
    @CsvSource(value = {"200%, PLAY", "RE-BYE, 사춘기 상", "오랜 날 오랜 밤, 사춘기 하", "어떻게 이별까지 사랑하겠어 널 사랑하는 거지, 항해"})
    void checkAkmuSong(String song, String album) {
        // when
        Akmu akmu = new Akmu(song);
        String result = akmu.getType();

        // then
        assertThat(result).isEqualTo(album);
    }

@NullAndEmptySource

@NullSource와 @EmptySource를 합친 것으로, null 값과 empty 값을 가지고 테스트를 진행한다.

    @ParameterizedTest
    @DisplayName("null 값 또는 empty 값이 입력되면 알 수 없음으로 설정된다.")
    @NullAndEmptySource
    void checkNullAndEmpty(String song) {
        // given
        String album = "알 수 없음";

        // when
        Akmu akmu = new Akmu(song);
        String result = akmu.getType();

        // then
        assertThat(result).isEqualTo(album);
    }

위와 같이 null 값과 empty 값을 이용해 테스트를 하는 것을 확인할 수 있다.

@EnumSource

@EnumSource는 Enum 클래스의 테스트를 도와준다. 예제는 아래와 같다.

    @ParameterizedTest
    @DisplayName("악동뮤지션 음악의 앨범을 확인할 수 있다.")
    @EnumSource(value = AkmuSong.class, names = {"LoveLee", "후라이의꿈"})
    void checkAkmuSong(AkmuSong song) {
        // given
        String album = "Love Lee";

        // when, then
        assertThat(song.getAlbum()).isEqualTo(album);
    }

value에 테스트에 사용할 Enum class를 지정해준다.
만약 특정 값만 사용하고 싶은 경우 names에 사용할 값들을 넣어준다.

@MethodSource

@MethodSource는 메서드에서 반환하는 값들로 여러 번의 테스트를 진행할 때 사용한다.
@MethodSource 안에 메서드 이름을 적어준다. 이 때 메서드의 이름과 테스트 메서드의 이름을 같게 한다면 이를 생략할 수 있다.
메서드는 Arguments의 Stream을 반환해야 한다.
예제는 아래와 같다.

    @ParameterizedTest
    @DisplayName("입력된 노래와 비교하여 악동뮤지션 노래의 개수를 반환한다.")
    @MethodSource("provideAllCasesSongs")
    void countAkmuSongs(List<String> songs, int expectedResult) {
        // given
        Akmu akmu = new Akmu();

        // when
        int result = akmu.checkAkmuSong(songs);

        // then
        assertThat(result).isEqualTo(expectedResult);
    }

    private static Stream<Arguments> provideAllCasesSongs() {
        return Stream.of(
                Arguments.of(List.of("OMG", "Drama", "200%"), 1),
                Arguments.of(List.of("200%", "RE-BYE", "오랜 날 오랜 밤"), 3)
        );
    }

이처럼 @ParameterizedTest를 통해 더 질좋은 테스트 코드를 작성할 수 있다.

profile
우리가 노래하듯이, 우리가 말하듯이, 우리가 예언하듯이 살길

0개의 댓글