12월 26, 2023

JUnit @ParameterizedTest를 활용하여 Test code 간략하게 작성하기

 JUnit의 @ParameterizedTest를 사용하여 TestCode를 간략하게 작성할 수 있는 방법에 대해 알아보겠다. 

1. Dependency 추가


먼저 Gradle에 다음과 같은 Dependency를 추가해준다. 

testImplementation 'org.junit.jupiter:junit-jupiter-params:5.4.2'


2. @ParameterizedTest 사용법


2.1 @ParameterizedTest 사용이유

Test 코드를 사용하다 보면 test 인자로 들어오는 값이 무엇이냐에 따라 Test 결과값이 바뀐다. 특히 예를 들어 몇 자리에서 몇자리까지 validation으로 들어오는 경우에는 그 경계값에 해당되는 숫자를 test 코드에 넣고 일일히 테스트해야 하기 때문에 소스의 중복이 많아진다. 


테스트 코드의 중복을 줄이고 모든 경계값에 대한 테스트를 진행할 때 @ParameterizedTest를 사용하면 아주 유용하다. 


@ParameterizedTest는 다양한 annotation과 함께 사용할 수 있다. 


2.2 @ValueSource annotation 사용

@ValueSource는 literal 값의 단일 배열을 지정할 수 있고, 테스트 시마다 단일 인자를 제공할 경우 사용할 수 있다. 


short, byte, int, long, float, double, char, boolean 뿐 아니라 java.lang.String, java.lang.Class에 대해서도 지원이 가능하다. 


@ValueSource 뒤에 (strings = { } ) 이런 식으로 원하는 타입을 적은 뒤 그 뒤에 literal을 넣어주면 된다. string 인자일 경우 strings = { } , int 인자일 경우 ints = {} 이런식으로 적을 수 있다.  

@DisplayName("비밀번호가 7자 미만 또는 13자 초과하는 경우 IllegalArgumentException 예외를 발생시킨다.")
@ParameterizedTest
@ValueSource(strings = {"aabbcc", "abcdefdeeffeeg"})
void validatePasswordTest(String password) {
assertThatCode(()->PasswordValidator.validate(password))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("비밀번호는 최소 7자 이상 13자 이하로 설정되어야 한다.");
}


여기서는 password가 단일인자 string의 형태로 들어왔으므로 strings = { } 의 형태로 @ValueSource를 설정해줄 수 있는 것이다. 


만약 @ParameterizedTest를 하지 않았다면 6글자의 password, 14자리의 password를 가지는 Test method를 각각 하나씩 적었어야 할 것이다. 


동일 기능을 수행하고 있는 Test Code의 여러 test case를 @ParameterizedTest와 @ValueSource를 사용하여 간략하게 작성할 수 있다. 


2.3 @MethodSource

위의 @ValueSource의 경우 parameter를 하나만 넘겨주기 때문에 인자가 여러 개인 경우는 테스트가 어렵다. 


@MethodSource annotation을 사용하면 우리가 사용하고자 하는 method를 새롭게 만들고 그 메서드의 복합인자를 가져와 test에 사용할 수 있어서 다각도로 테스트가 가능하다. 

import java.util.stream.Stream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
@DisplayName("사칙 연산 (+, -, *, /) 을 수행한다.")
@ParameterizedTest
@MethodSource("formulaCalculation")
void calculateTest(int operand1, String operator, int operand2, int result) {
int formulaResult = Calculator.calculate(operand1, operator, operand2);
assertThat(formulaResult).isEqualTo(result);
}

private static Stream<Arguments> formulaCalculation(){
return Stream.of(
arguments(3, "+", 4, 7),
arguments(5, "-", 2, 3),
arguments(2, "*", 2, 4),
arguments(8, "/", 2, 4)
);
}


이런 식으로 적어주면 formulaCalulation() 이라고 우리가 따로 정의한 static method의 인자를 넣어줄 수 있다. 


이런 식으로 사용하면 인자가 여러개 일 때도 손쉽게 테스트 할 수 있다. 


12월 23, 2023

[JUnit5] 테스트코드 단축기, @DisplayName 테스트 이름 표시하는 방법

 JUnit은 Java의 단위 테스트 Framework이다. 

하나의 jar 파일로 관리되고 있어서 java에서 사용하기에도 매우 편리하다. 


Intellij에서 JUnit을 사용해서 Test Method를 작성하기 위해서는 

Windows 기반 : alt + insert 를 누르고

Mac 기반: command + N을 누르면 

아래와 같이 Generate 아래 Test Method가 뜬다. 


위 Test Method를 클릭하면 


@Test라는 annotation을 단 Test method가 생성이 된다. 


여기서 해당 단위 테스트의 이름을 표시하기 위해서는 

@DisplayName이라는 annotation을 추가로 활용할 수 있다. 

@DisplayName은 한글, 이모지, 공백 등 테스트케이스 이름을 제약 없이 자유롭게 설정할 수 있다. 즉 내가 해당 테스트 메서드를 작성하면서 이 테스트의 의도가 있었을 텐데 그 의도를 @DisplayName에 적어주면서 코드의 가독성을 높일 수 있는 것이다. 


@DisplayName("ID에 영문, 특수문자, 대문자가 모두 포함되면 ID validation이 통과한다")
@Test

이런 식으로 작성하고 method를 작성하면서 그 테스트 케이스의 의도, 또는 부연설명을 자연스럽게 적을 수 있다. 


@DisplayName("ID에 영문, 특수문자, 대문자가 없으면 ID validation이 통과하지 않는다")
@Test

반면 위와 같이 작성하여 요구사항을 충족하지 않을 경우의 테스트 케이스도 작성할 수 있다. 

이렇게 하고 Test를 Run하면 이렇게 @DisplayName에 작성한 설명이 뜨면서 

어떤 TestCase가 통과했는지 손쉽게 확인할 수 있다. 


12월 23, 2023

TDD (Test Driven Development), BDD (Behavior Driven Development) 란?

 프로그래밍 개발 프로세스 중 TDD, BDD 라는 말을 들어보았을 것이다. 

간단히 말해서 TDD는 Test Driven Development의 약자, BDD는 Behavior Driven Development의 약자이다.  

오늘은 각각의 정의와 특징에 대하여 알아보도록 하겠다. 


1. TDD (Test Driven Development)

Full name이 의미하는 것처럼 TDD는 테스트 주도 개발이다. 코드를 작성하기 전 테스트를 먼저 작성해야 한다. TDD는 아주 짧은 개발 서클의 반복에 의존한다고 볼 수 있다. 

TDD에서 개발자는 요구되는 기능에 대한 테스트 케이스를 작성하고 해당 테스트 케이스를 통과하는 짧으면서도 가독성 좋은 코드를 작성하는 것이다. 즉 테스트를 먼저 작성하고 그 테스트를 통과하기 위한 코드를 이후에 작성하는 것이다. 코드를 작성한 뒤에 필요에 따라 리팩토링(Refactoring) 하는 과정을 거칠 수 있다. 

* Test Code 작성 => Production Code 작성 => 리팩토링


[TDD 장점]

위 프로세스처럼 코드를 작성하면 깔끔하게 코드를 작성할 수 있고 리팩토링을 통해서 코드의 퀄리티를 높일 수 있다. 


[TDD 단점] 

하지만 단점도 존재한다. 

일단 테스트코드를 작성하면서 절대적인 코드량이 늘어나고 테스트케이스에 대한 고민이 있을 수 있다. 어디서부터 어디까지 테스트를 해야 할지, 어디부터 테스트를 해야 할지에 대한 고민이 필요하다. 일정이 빡빡한 프로젝트에서는 적용하기 어려울 수 있는 개발 프로세스이다. 


2. BDD (Behavior Driven Development)

BDD는 TDD와 완전 다른 개념은 아니고 TDD를 base로 하여 파생된 프로그래밍 개발 프로세스이다. TDD에서는 테스트 통과를 위한 코드를 작성했다면, BDD에서는 그 개념에서 더 나아가 테스트 케이스 자체가 코드의 요구사항이 되도록 하는 방식이라고 생각하면 된다.

TDD에서 각 테스트 케이스에 대한 유닛테스트를 진행했다면, BDD에서는 통합 테스트와 시나리오 테스트까지 확장해서 진행한다. 


즉 BDD를 사용하면 TDD에서 논의된 단점을 커버할 수 있다. 

어디서부터 테스트를 진행할지, 어떤 기능은 테스트하고 어떤 것은 하지 않을지 등의 고민을 해결할 수 있다.