JUnit 5 기본적인 사용법

김나쁜 Kimbad·2024년 7월 25일
0

TDD

목록 보기
1/2

JUnit

JUnit 4? JUnit 5?

  • JUnit 4
    - JUnit4가 성숙기를 거쳐 훨씬 안정적
    - 현재 Java 개발자들의 대부분이 익숙함(오래 사용됨)
    - 다양한 IDE에서 지원
    - 멀티스레드 지원 불안정
    - 확정성이 떨어지고 테스트 코드 구성이 어려움
  • JUnit 5
    - JUnit4의 문제점을 대부분 보완
    - 테스트 인스턴스 라이프사이클 지원
    - 동적 테스트 지원
    - 테스트 파라미터화 기능
    - 모듈화 가능

JUnit 5

JUnit4는 단일 jar로 구성되어 있었지만, JUnit5는 JUnit Platfrom, JUnit Jupiter, JUnit Vintage 모듈 세가지로 구성된다. JUnit 5는 Java 8 이상의 버전을 요구한다.

  • JUnit Platform
    - JVM에서 동작하는 테스트 프레임워크로, 테스트를 발견하고 계획 생성 및 결과를 보고하는 TestEngine 인터페이스를 정의함
  • JUnit Jupiter
    - JUnit5 TestEngine의 구현체로 JUnit5 기반의 테스트를 실행시키기 위한 Test Engine을 Platform에 제공한다.
  • JUnit Vintage
    - Test Engine에서 Junit3 및 JUnit4 기반 테스트를 실행하기 위한 기능을 제공한다.

기본적인 테스트 케이스 형식 예시

import static org.junit.jupiter.api.Assertions.assertEquals;
import example.util.Calculator;
import org.junit.jupiter.api.Test;

class MyFirstJUnitJupiterTests {
	private final Calculator calculator = new Calculator();
	  
	@Test
	void addition() {
		assertEquals(2, calculator.add(1, 1));
	}
}

JUnit 5 기본적인 어노테이션

Junit5에서 제공하는 org.junit.jupiter.api 패키지 내의 어노테이션을 설명합니다.

  • @Test
    • 해당 어노테이션을 달아둔 메서드가 테스트 메서드임을 나타냄
  • @BeforeEach
    • 각각의 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory 전에 실행된다.
  • @AfterEach
    • 각각의 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory 후에 실행된다.
  • @BeforeAll
    • 모든 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory 전에 실행된다.
  • @AfterAll
    • 모든 @Test, @RepeatedTest, @ParameterizedTest, @TestFactory 후에 실행된다.
  • @ExtendWith
    • 확장을 선언적으로 등록하는데 사용된다. Extendtion 뒤에 인자로 확장할 Extension을 추가하여 사용한다.
    • Spring을 사용할 경우 @ExtendWith(SpringExtension.class)와 같이 사용한다.
  • @Disabled
    • 테스트 클래스 또는 테스트 메서드를 비활성화 하는데 사용된다.
  • @ParameterizedTest
    • 메서드가 매개변수가 있는 테스트임을 나타낸다.
  • @RepeatedTest
    • 메서드가 반복 테스트를 위한 테스트 템플릿임을 나타낸다.
  • @TestFactory
    • 메서드가 동적 테스트를 위한 테스트 팩토리임을 나타낸다.
  • @TestMethodOrder
    • 테스트 메서드 실행 순서를 구성하는데 사용된다.
  • @DisplayName
    • 테스트 클래스 또는 테스트 메서드에 대한 사용자 지정 표시 이름을 정해줄 때 사용된다.
  • @DisplayNameGeneration
    - @DisplayName 처럼 별도의 이름을 주는 것이 아닌 코딩한 클래스, 메소드 이름을 이용해 변형시키는 어노테이션이다.
  • @IndicativeSentencesGeneration
    - IndicativeSentences의 구분자를 커스텀해서 사용할 수 있게 해준다.
@IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class) 
public class IndicativeSentencesGenerationAnnotation { 

	@Test void test_display_name_generation() { 
		// "클래스명 -> test display name generation" 으로 표시됨
	} 
	
}

📝
JUnit 테스트는 기본적으로 TestInfo 타입의 인자도 받을 수 있습니다.
TestInfo 클래스는 아래와 같은 메서드를 가집니다.

getDisplayName() : String, @DisplayName값과 동일
getTags() : Set\, @Tag 배열 값
getTestClass() : Optional\<Class\<?>>, 패키지 + 테스트 클래스명
getTestMethod() : Optional\, 패키지 + 테스트 클래스 명 + 테스트 메서드

Junit 4 -> JUnit 5 일부 어노테이션 변경점

Junit5와 Junit4 는 일부 어노테이션에서도 차이가 있다.

이전에 Junit 4 기반으로 작성된 파일들은 위의 내용과 같이 junit-vintage-engine이 있다면 자동으로 Junit Platform 런처에서 Junit 3, 4기반 테스트 코드를 선택한다. 이미 만들어진 Junit 4 파일을 5버전으로 수정할 필요는 없다.

  • Assertion 의 위치가 Junit5에서는 org.junit.jupiter.api.Assertions으로 변경되었고 AssertJ, Hamcrest, Trust에서 제공하는 org.junit.Assert는 그대로 사용할 수 있다.
  • @Before@After 어노테이션이 사라지고, 각각 @BeforeEach@AfterEach로 변경
  • @BeforeClass@AfterClass 어노테이션이 사라지고, @BeforeAll@AfterAll로 변경
  • @Ignore이 사라지고, @Disabled로 변경
    • 단, 이 경우 @EnableJUnit4MigrationSupport 어노테이션을 붙여준다.
  • @Category가 사라지고, @Tag로 변경
  • @RunWith이 사라지고 @ExtendWith으로 변경
  • @Rule@ClassRule이 사라지고, @ExtendWith@RegisterExtention으로 대체되었다.

JUnit 5 설정하기 (Gradle)

  1. JUnit5 의존성 추가하기
dependencies {
	testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.7.0")
	testImplementation('org.junit.jupiter:junit-jupiter:5.7.0')
	testRuntimeOnly('org.junit.vintage:junit-vintage-engine:5.3.1')

Test에 순서를 추가하는 어노테이션

  • @TestMethodOrder(OrderAnnotation.class)
  • `@Order(n)

@Tag를 사용해 태깅하기

@Slf4j  
public class TaggingTest {  
  
    @Test  
    @Tag("홀수")  
    void 테스트01() {  
        log.info("01번 테스트");  
    }  
  
    @Test  
    @Tag("짝수")  
    void 테스트02() {  
        log.info("02번 테스트");  
    }  
  
    @Test  
    @Tag("홀수")  
    void 테스트03() {  
        log.info("03번 테스트");  
    }  
  
    @Test  
    @Tag("짝수")  
    void 테스트04() {  
        log.info("04번 테스트");  
    }  
}

위와 같이 홀수번으로 지정된 테스트만 실행된다.

@DisplayNameGeneration을 기본값으로 설정하기

테스트 클래스를 생성할 때마다 @DisplayNameGeneration을 명시해주지 않고 이를 편하게 기본값으로 설정할 수 있다.

  1. test 아래 resources 폴더를 만든 후 junit-platform.properties 파일 생성

2. junit.jupiter.displayname.generator.default에 원하는 DisplayNameGenerator 추가
junit.jupiter.displayname.generator.default = org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores

  1. @DisplayNameGenerator가 없어도 명시된 방법이 Default로 실행된다.

ℹ️
@DisplayNameGeneration의 적용순서는 다음과 같습니다.
1. @DisplayName
2. @DisplayNameGeneration
3. Properties에 명시한 Default값
4. 1,2,3 중 아무 값도 없을 경우 DisplayNameGenerator.Standard.class

반복 테스트

@RepeatedTest, @ParameterizedTest 처럼 어노테이션명이 Test로 끝나면 별도로 @Test 어노테이션이 없어도 테스트가 가능하다.

@RepeatedTest

📝
value : int, 반복 횟수로 반드시 0보다 커야한다.
name: 반복할때 나타나는 테스트명, 지정하지 않을 시
기본값 : "repetition " + 현재 반복 횟수 + " of " + 총 반복 횟수의 형태로 나타난다.

@RepeatedTest(value = 1, name = "{displayName} {currentRepetition}/{totalRepetitions}")와 같은 형태로 작성할 수도 있다.

RepetationInfo 타입의 인자를 받을 수 있다.

📝
RepetationInfo Class의 구성요소

getCurrentRepetition() : int, 현재 반복횟수
getTotalRepetation(): int, 총 반복횟수
DISPLAY_NAME_PLACEHOLDER: String, @DisplayName
SHORT_DISPLAY_NAME: String, 반복 시 나타나는 테스트명
기본 값 - "repetition" + 현재 반복 횟수 + of + "총 반복 횟수"
LONG_DISPLAY_NAME: String, DISPLAY_NAME_PLACEHOLDER + " :: " + SHORT_DISPLAY_NAME
TOTAL_REPETITIONS_PLACEHOLDER: String, 현재 반복 횟수
CURRENT_REPETITION_PLACEHOLDER: String, 총 반복 횟수

⚠️
RepetitionInfo를 테스트 메서드 인자로 받을 시 반드시 @RepeatedTest 어노테이션이 선언 되어야 합니다.

@ParameterizedTest

특정 파라미터를 넣어서 테스트를 반복적으로 실행할 수 있게 해주는 어노테이션이다.

⚠️
@ParameterizedTest는 단독으로 사용되지 않고 어떤 파라미터를 사용하는지에 관한 어노테이션을 필수적으로 선언해줘야 한다.

@ValueSource

다양한 타입의 파라미터를 배열로 받아서 사용할 수 있게 해주는 어노테이션이다.

short[], byte[], int[], long[], float[], double[], char[], boolean[], String[], Class<?>[] 와 같이 다양한 타입을 사용할 수 있다.

해당 배열의 길이만큼 반복하며, 파라미터를 2개 이상 넣을 시 에러가 발생한다.

    @ParameterizedTest  
    @ValueSource(ints = {1, 2, 3, 4, 5, 6, 7, 8, 9})  
    void 파라미터_테스트(int n) {  
        log.info("파라미터 테스트 : {}회 반복", n);  
    }  
  
//    @ParameterizedTest  
//    @ValueSource(ints = {1, 2, 3, 4, 5}, strings = {"하나", "둘", "셋", "야!"}) 
//    void 파라미터_테스트(int n, String str) {  
//        // @ValueSource는 파라미터 2개를 받을 수 없음  
//    }

@NullSource, @EmptySource, @NullAndEmptySource

파라미터로 Null값을 주거나 빈 값을 줄 때 사용한다.

@ParameterizedTest  
@NullSource  
void 널임(String str) {  
    // 원시타입은 사용할 수 없다.  
    assertNull(str);  
}  
  
@ParameterizedTest  
@EmptySource  
void 비어있음(Map map) {  
    // 비어있는 Map 객체 {}를 반환하므로 Null이 아니다.  
    // 원시타입 배열, 객체 배열, Collection 객체 등에 사용 가능하다.  
    assertNull(map);  
}  
  
@ParameterizedTest  
@NullSource  
@EmptySource  
@ValueSource(strings = { "a", "b", "c", "d" })  
void 널이고비어있음(String text) {  
	// 순서대로 NULL, "", "a", "b", "c", "d"
    assertTrue(text == null || text.trim().isEmpty());  
}

@EnumSource

Enum에 정의된 상수들을 테스트하기 위한 어노테이션이다.

@ParameterizedTest  
// @EnumSource(value = CountryEnum.class)  
@EnumSource // value를 지정하지 않을 시 메서드 인자로 추론한다.  
void 이넘_테스트(CountryEnum countryEnum) {  
    log.info("요거! : {}", countryEnum);  
}  
  
@ParameterizedTest  
@EnumSource(value = CountryEnum.class, mode = INCLUDE, names = {"CANADA", "TURKEY", "FRANCE"})  
void 이넘_모드(CountryEnum countryEnum) {  
    log.info("names를 포함한 ENUM : {}", countryEnum);  
}

mode에서 지원하는 비교 방식은 INCLUDE, EXCLUDE, MATCH_ANY, MATCH_ALL이 있으며 names 속성에서 문자열 혹은 정규식을 지정할 수 있다.

@MethodSource

Factory 메서드에서 리턴해주는 값을 가지고 반복하는 어노테이션이다.

⚠️
Factory 메서드 조건

  1. 테스트 클래스에 @TestInstance(Lifecycle.PER_CLASS)가 선언 되어있는 경우를 제외하고는 반드시 static 클래스여야 한다.
  2. 인자가 없어야 한다.
  3. Stream\ 형태로 리턴해야한다.
@ParameterizedTest  
@MethodSource(value = "methodTestProvider")  
void 메서드_테스트(String str, int num, List<String> list) {  
    log.info("문자열 : {}, 정수 : {}, 리스트 : {}", str, num, list);  
}  
  
static Stream<Arguments> methodTestProvider() {  
    return Stream.of(  
            Arguments.arguments("가", 1, Arrays.asList("A", "B")),  
            Arguments.arguments("나", 2, Arrays.asList("C", "D"))  
    );  
}

@CsvSource

CSV (Comma Seperated Value) 형식의 데이터로 반복하는 어노테이션이다.

📝
@CsvSource의 속성 값

value : String[], CSV형식의 데이터
delimiter: char, delimiter를 char형으로 변경, delimiterString과 같이 사용 불가
delimiterString: String, delimiter를 String형으로 변경
emptyValue: String, CSV 데이터 중 빈 값인 경우 대체되는 값
nullValues: String[], CSV 데이터 중 null 값으로 대체할 값

@ParameterizedTest  
@CsvSource(value = {  
        "가, A",  
        "나, B",  
        "다, C",  
        "라, ''",  
        "ImNull, Metoo"  
},  
emptyValue = "Empty",  
nullValues = {"ImNull", "Metoo"})  
void CSV_테스트(String hangul, String alpha) {  
    log.info("한글 : {}, 알파벳 : {}", hangul, alpha);  
    // assertNull(hangul, alpha); // nullValue로 지정된 값만 Null로 변경됨  
}

테스트 인스턴스

ℹ️
테스트 인스턴스란?

JUnit은 설정된 테스트 단위로 테스트 인스턴스를 생성한다.
JUnit 5에서 테스트 인스턴스의 기본 단위는 메서드 단위로 지정되어
각 메서드의 실행 결과가 다른 테스트에 영향을 주지 못하므로 단위 테스트에 적합하지만
테스트 클래스가 가진 각 테스트들을 하나의 단위로 묶어야 할 시 테스트 인스턴스 단위를
클래스 단위로 변경해줄 수 있다.

다음과 같은 예시의 경우

public int i=0;

@Test
void test_1() {
	assertTrue(++i == 1);
}	

@Test
void test_2() {
	assertTrue(++i == 1);
}	

결과가 모두 성공으로 나온다(두 Case 모두 결과가 1.)
기본 테스트 인스턴스의 단위가 메서드 단위이기 때문.

@TestInstance(value = PER_CLASS) 
public class TestInstanceAnnotation { 
	public int i=0; 
	
	@Test void test_1() { 
		assertTrue(++i == 1); 
	} 
	
	@Test void test_2() { 
		assertTrue(++i == 1); 
	} 
}

위와 같이 테스트 인스턴스의 단위를 클래스로 변경할 경우
test_2에서 실패하게 된다. (전역변수 i를 클래스간 공유하게 된다.)

모든 테스트 인스턴스의 기본값을 @TestInstance(value = PER_CLASS)로 지정하고 싶은 경우

# resources/junit-platform.properties
junit.jupiter.testinstance.lifecycle.default = per_class

테스트의 순서

JUnit 5는 테스트 순서가 명시적으로 정해지지 않은 경우 기본 알고리즘에 의해 정해지지만
아닌 경우도 있다. (순서가 보장되지 않는다.)

[!cation]
@TestMethodOrder 주석 중

  • If {@code @TestMethodOrder} is not explicitly declared on a test class,
    inherited from a parent class, or declared on a test interface implemented by
    a test class, test methods will be ordered using a default algorithm that is
    deterministic but intentionally nonobvious.

만약 @TestInstance를 통해 클래스 단위로 테스트를 실행하여 각 테스트 간 실행 순서가 명확하게 보장되어야 한다면 @TestMethodOrder를 사용해야 한다.

@TestMethodOrder 어노테이션의 value 속성에 정렬 타입을 지정할 수가 있다.
MethodName, DisplayName, OrderAnnotation, Random 으로 선택 가능하다.
혹은 Class<? extends MethodOrderer> 형태의 커스텀 클래스를 직접 구현하여 사용할 수도 있다.

  • MethodName : 메서드 명칭 순으로 실행한다.
  • DisplayName: @DisplayName의 명칭순으로 실행한다.
  • OrderAnnotation: @Order(n)으로 지정한 순서대로 실행한다.
  • Random: 랜덤으로 실행한다.

시스템 환경 별 테스트

아래 모두 disabledReason이라는 속성을 포함하며 @Disabled 어노테이션이 선언되어있는 경우에만 사용하게 된다.

@EnabledOnOs, @DisabledOnOs

OS 환경 별 테스트를 할 수 있게 해주는 어노테이션이다.

  • value: OS[], LINUX, MAC, SOLARIS, WINDOWS, OTHER

@EnabledOnJre, @DisabledOnJre

JRE 환경 별 테스트를 할 수 있게 해주는 어노테이션이다.

  • value: JRE[], 테스트 JRE (JAVA_8, JAVA_9, JAVA10, .... OTHER)

@EnabledOnJreRange

JRE 버전 범위로 테스트를 할 수 있게 해주는 어노테이션이다.

  • min: JRE, 테스트를 할 최소 버전의 JRE
  • max: JRE, 테스트를 할 최대 버전의 JRE

@EnabledIfSystemProperty, @DisabledIfSystemProperty

JVM의 System Property 별 테스트를 할 수 있게 해주는 어노테이션이다.
복수형으로 사용 가능하다.

  • named: String, JVM의 System Property명
  • matches: String, 찾고자 하는 이름(정규식)

@EnabledIfEnvironmentVariable, @DisabledIfEnvironmentVariable

환경 변수 별로 테스트 할 수 있게 해주는 어노테이션이다.
복수형으로 사용 가능하다.

  • named: String, 환경변수명
  • matches: String, 찾고자 하는 이름(정규식)

@EnabledIf, @DisabledIf

개발자가 원하는 조건으로 테스트를 진행할 수 있게 해주는 어노테이션이다.

  • value: 조건 로직이 있는 메서드명
@EnabledIf("customCondition") 
@Test void testEnabledIf() {
	
}

@Test 
@DisabledIf("customCondition") 
void testDisabledIf() { 
	
}

boolean customCondition() { 
	return true;
}

JUnit 4 -> JUnit 5

JUnit 4JUnit 5
org.junitorg.junit.jupiter.api
@Before / @After@BeforeEach / @AfterEach
@BeforeClass / @AfterClass@BeforeAll / @AfterAll
@Ignore@Disabled
@Category@Tag
@Runwith@ExtendWith
@Rule / @ClassRule@ExtendWith / @RegisterExtension

마이그레이션 순서

JUni4를 5로 올리면서 기존 작성된 테스트 코드에 있는 어노테이션을 굳이 바꿀 필요는 없다.
이유는 JUnit 5의 구성 요소 중 JUnit 3, 4 를 지원해주는 JUnit Vintage가 있다.

만약 기존 소스 중 아래 명시된 JUnit 4의 기능을 사용하고 있다면 junit-jupiter-migrationsupport 모듈 의존성을 추가후 @EnableRuleMigrationSupport를 클래스에 붙여주어야 한다.

아래 기능들은 JUnit Vintage에서 제공해주지 않아 별도의 모듈이 필요하다.

  1. @Rule의 subclass 3가지
org.junit.rules.ExternalResource (including org.junit.rules.TemporaryFolder)

org.junit.rules.Verifier (including org.junit.rules.ErrorCollector)

org.junit.rules.ExpectedException
  1. @Ignore

Given-When-Then 패턴

테스트를 더 가독성 있게 구조화하고 유지보수하기 쉽도록 구조화 하는 방법론을 의미한다.
이 패턴을 따르게 되면 테스트 케이스가 더 구체적이고 이해하기 쉬워지며 각각의 단계가 분리되어 테스트의 각 부분이 어떤 역할을 담당하는지 명확해진다.

섹션설명
Given설정, 테스트의 초기 상태 또는 사전 조건을 설정한다. 입력 데이터나 테스트가 실행될 문맥을 지정한다.
When동작, 테스트되는 동작 또는 이벤트를 설명한다. 테스트되는 특정 메서드나 동작을 나타낸다.
Then검증, "When" 섹션에서 설명한 동작으로 인해 기대되는 결과 또는 동작을 정의한다.

[예시]
Given-when-then 패턴을 이용한 예시

Given 섹션에서는 '초기 상태'를 설정하고 When 섹션에서는 '특정 동작을 수행'하며, Then 섹션에서는 '예상 결과를 검증'하는 어서션(assertion)을 사용하여 검증한다.

class 테스트패턴 {

	@Test
	void 테스트_사이즈() {
		// Given
		List<String> list = new ArrayList<>();

		//When
		list.add("가");
		list.add("나");
		list.add("다");

		//Then
		assertEquals(3, list.size()); 
		assertTrue(list.contains("가")); 
		assertFalse(list.isEmpty());
	}
}

테스트 명명 규칙

'메서드명테스트대상상태예상동작' 형식

메서드명_테스트대상의상태_예상동작 형식으로 JUnit 메서드 명을 지정하는 방식이다.
메서드 이름이 코드 리팩터링의 일환으로 변경이 되는 경우 테스트 이름도 변경되어야 하므로 단점이 있다.

  • isAdult_AgeLessThan18_False : 만약 나이가 18보다 작으면 성인이 아님 (False)을 반환하는 경우
  • withdrawMoney_InvalidAccount_ExceptionThrown : 잘못된 계정으로 출금 시 예외가 발생하는 경우
  • admitStudent_MissingMandatoryFields_FailToAdmit: 필수 입력 필드가 누락되었을 때 학생을 입학시키지 못하는 경우

“test” 접두어 형식

test 접두어를 가진 JUnit 메서드 명으로 지정하는 방식이다.
"test" 접두어가 중복된다는 주장도 있지만, 일부 개발자들은 이 기법을 사용하는 것을 선호한다. 또한 SonarLint에 code smells를 피할 수 있다는 이유로 권장되기도 한다.

  • testIsNotAnAdultIfAgeLessThan18: 만약 나이가 18보다 작으면 성인이 아님을 테스트
  • testFailToWithdrawMoneyIfAccountIsInvalid: 잘못된 계정으로 출금 시 실패하는 것을 테스트
  • testStudentIsNotAdmittedIfMandatoryFieldsAreMissing: 필수 입력 필드가 누락되었을 때 학생이 입학되지 않는 것을 테스트

'Should예상동작테스트대상상태' 형식

Should_예상동작_When_테스트대상상태으로 JUnit 메서드 명을 지정하는 방식이다.

  • Should_ThrowException_When_AgeLessThan18: 만약 나이가 18보다 작으면 예외를 Throw
  • Should_FailToWithdrawMoney_ForInvalidAccount: 잘못된 계정일 경우 출금에 실패
  • Should_FailToAdmit_IfMandatoryFieldsAreMissing: 필수 입력 필드가 누락되었을 때 입학에 실패

'When테스트대상상태_Expect예상동작' 형식

When_테스트대상상태_Expect_예상동작으로 메서드 명을 지정하는 방식이다.

  • When_AgeLessThan18_Expect_isAdultAsFalse: 만약 나이가 18보다 작으면 성인이 아님 (False)
  • When_InvalidAccount_Expect_WithdrawMoneyToFail: 잘못된 계정으로 출금 시 실패할 것을 예상
  • When_MandatoryFieldsAreMissing_Expect_StudentAdmissionToFail: 필수 입력 필드가 누락되었을 때 학생 입학이 실패할 것을 예상

'Given-When-Then' 형식

- Behavior-Driven Development (BDD)의 일부로 개발된 명명 규칙이다. 테스트를 사전 조건 (Given), 테스트 대상 상태 (When), 예상 동작 (Then)으로 세 가지 부분으로 나누어 작성한다.

이 접근 방식은 Behavior-Driven Development (BDD)의 일부로 개발된 명명 규칙에 기반을 두고 있다. 개별 테스트를 세 부분으로 나눠 사전 조건, 테스트 대상 상태 및 예상 동작을 위의 형식에 맞춰 작성한다.

  • Given_UserIsAuthenticated_When_InvalidAccountNumberIsUsedToWithdrawMoney_Then_TransactionsWillFail: 사용자가 인증된 상태에서 잘못된 계정 번호를 사용하여 금액을 인출하면 거래가 실패할 것입니다.

출처

노바의 개발 도서관:티스토리
노력남자

profile
Bad Language

0개의 댓글

관련 채용 정보