안녕하세요.
오늘은 자바에서 테스트를 할 때 많이 사용되는 AssertJ와 이를 통해 단위테스트를 진행해보고자 합니다.
AssertJ
- AssertJ는 assertion을 제공하는 자바 라이브러리로 에러 메시지와 테스트 코드의 가독성을 높여주는 라이브러리입니다.
- 쉽게 말해서 테스트의 흐름을 작성할 수 있는 라이브러리라고 보시면됩니다!
- 체이닝 문법을 통해서 직관적으로 읽힙니다.
예제를 보면서 설명을 진행하겠습니다.
@Test
void stringDoubleSplitTest() {
//given
String input = "1,2";
//when
final String[] splitedInput = input.split(",");
//then
assertThat(splitedInput).containsExactly("1", "2");
}
위에 예시를 보면 input을 split한 것을 검증하는 코드입니다.
AssertJ는 위에서 메서드 체이닝으로 직관적이라고 말씀 드렸죠?
// then 부분을 보시면 그 이유를 알 수 있습니다.
assertThat()에서 splitedInput이 ("1", "2")이랑 동일한지 검증하는 내용입니다.
JUnit5의 assertEquals(예상, 실제)와 다르게 검증에서 무엇을 말하고자 하는지 더욱 직관적으로 알 수 있습니다.
검증을 위한 다양한 문법 예시
containsExactly()와 같이 여러가지 검증을 위한 문법들이 있습니다.
다음 예시를 통해 다양한 검증을 위한 문법을 알아봅시다! (공식 문서에 가신다면, 아래 예시보다 더욱 많은 검증 문법들을 확인하실 수 있습니다!)
@Test
void assertions() {
/** Input 검증 요구사항
* 1. Null 값이면 안된다.
* 2. "Jay"가 포함된다.
* 3. "kokodak"이 포함되면 안된다.
* 4. "odo?"로 끝나야한다.
* 5. String class 타입이어야 한다.
*/
// given
String input = "Hello, World! I am Jay. Are you Odo?";
// when & then
assertThat(input)
.isNotNull()
.contains("Jay")
.doesNotContain("kokodak")
.endsWith("Odo?")
.isInstanceOf(String.class);
}
위에 코드와 같이 주석 부분 검증 요구사항이 주어진다면 우리는 주어지는 input에 대해 위와 같이 작성할 수 있습니다.
메서드 체이닝을 이용하여 보기 좋게 작성할 수 있습니다.
만약에 요구사항을 지키지 못한다면 어떤 결과가 터미널에 출력이 될까요?
.endsWith() 부분에 "Odo?" 에서 "Ure"로 바꾸었더니 사진과 같은 에러 문자가 출력됩니다.
이렇게 endWith()에서 끝나게 된다면 그 아래 체이닝인, isIntanceOf() 는 실행되지 않습니다.
"저는 isInstanceOf()도 검증하고 싶어요!" 라고 말하는 Sungha가 있습니다.
Sungha의 요구사항을 위해서 어떻게 하면 될까요?
이럴 땐 Soft assertions 기능을 사용하면 됩니다.
이를 사용하면, 모든 assertions을 실행하고 실패한 내역만 확인할 수 있게 됩니다.
예시를 보고 확인해보겠습니다.
@Test
void soft_assertions() {
/** Input 검증 요구사항
* 1. Null 값이면 안된다.
* 2. "Jay"가 포함된다.
* 3. "kokodak"이 포함되면 안된다. (여기서 틀릴 예정!)
* 4. "odo?"로 끝나야한다. (여기서 틀릴 예정!)
* 5. String class 타입이어야 한다.
*/
// given
SoftAssertions softAssertions = new SoftAssertions();
String input = "Hello, World! I am Jay. Are you Odo?";
// when & then
softAssertions.assertThat(input).isNotNull();
softAssertions.assertThat(input).contains("Jay");
softAssertions.assertThat(input).doesNotContain("Jay");
softAssertions.assertThat(input).endsWith("io?");
softAssertions.assertThat(input).isInstanceOf(String.class);
softAssertions.assertAll(); // 이걸로 모두 검증 시도!
}
위에 예시처럼 SoftAssertions 객체를 이용해서 doesNotContains()와 endsWith() 부분에 에러를 띄워보겠습니다.
에러가 나온다면 위와 같이 두 가지 틀린 케이스에 대해서 출력을 해줍니다.
이와 같이 SoftAssertions 객체에 모든 테스트를 담아두고 assertAll()로 검증을 해준다면, 아까와 같은 문제는 해결되고 Sungha의 요구사항을 만족시킬 수 있게 됩니다.
자, 이제 값을 검증하는 것은 이 정도로 마무리 하겠습니다.
그렇다면 다음에는 예외 검증을 진행해보겠습니다.
- Exception Assertions (AssertThrownBy())
바로 예시를 보겠습니다.
우르가 다음과 같은 요구사항을 전달해줬습니다. "자동차 이름이 5자가 초과된 경우에 예외처리를 해주세요."
요구사항을 지키기 위해서, 자동차의 이름을 검증하는 validateLengthOfCarName() 메서드를 아래와 같이 만들었습니다.
private static void validateLengthOfCarName(String carName) {
if (carName.length() > CAR_NAME_LENGTH_MAX) {
throw new IllegalArgumentException("자동차 이름은 5자 이하로 작성해주세요.");
}
}
자 이제 위에 코드를 기반으로 테스트를 작성해보겠습니다.
@Test
@DisplayName("validateInputCarNames() : 이름 5자 초과일 경우에 IllegalArgumentException 발생")
void test_ValidateName_IllegalArgumentException() {
//given
String input = "aaa";
String expectedMessage = "자동차 이름은 5자 이하로 작성해주세요.";
//when & then
assertThatThrownBy(() -> CarNamesValidation.validateLengthOfCarName(input))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining(expectedMessage);
}
그렇다면 위와 같이 테스트를 작성할 수 있습니다.
여기서 assertThatThrownBy에 예외를 발생시키는 코드를 작성해줍니다. (일부로 예외 사항 발생!)
그리고 아까 배운 .isInstanceOf()를 통해서 어떤 Exception이 발생하는지 확인을 해주고, 필요에 따라 hasMessageContaining()을 이용해서 에러 메시지를 확인하면 됩니다.
+ 여러 값을 검증해야하는 테스트의 경우 @ParameterizedTest + @ValueSource() 조합으로 다음과 같이 검증을 진행할 수도 있습니다. (굳이 예외처리가 아닌 경우도 가능)
@ParameterizedTest
@ValueSource(strings = {"abcdef", "123456"})
@DisplayName("validateInputCarNames() : 이름 5자 초과일 경우에 IllegalArgumentException 발생")
void test_ValidateName_IllegalArgumentException(String input) {
//given
String expectedMessage = "자동차 이름은 5자 이하로 작성해주세요.";
//when & then
assertThatThrownBy(() -> CarNamesValidation.validateLengthOfCarName(input))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining(expectedMessage);
}
실패하는 경우와 성공하는 경우 모두 위처럼 해준다면, 간편하게 테스트를 작성할 수 있습니다!
'Develop > Java' 카테고리의 다른 글
[Java] 원시값 포장에 대해 알아보자 (2) | 2023.02.23 |
---|---|
[Java] 쉽다 쉬워! 전래동화를 통해 알아보는 단위 테스트 (0) | 2023.02.16 |
[Java] 만취한 사람도 쉽게 이해할 수 있는 '일급 컬렉션'에 대해 알아보자! (0) | 2023.02.13 |
[Java] final 키워드에 대해 알아보자 (0) | 2023.02.13 |
클린코드에 대해서 알아보자 (리팩토링) (0) | 2022.11.08 |