작성자 : 장채은
실습 환경
- Java 11
- Spring Boot 2.7.7
🍰 JUnit 5 모듈 구성
- JUint 플랫폼
- 테스팅 프레임 워크를 구동하기 위한 런처와 테스트 엔진을 위한 API wprhd
- JUnit Jupiter
- JUnit 5를 위한 테스트 API와 실행 엔진 구성
- JUnit Vintage
- JUnit 3, 4로 작성된 테스트를 5 플랫폼에서 실행하기 위한 모듈 제공
Jupiter로 테스트를 작성하고 실행하려면 build.gradle 파일 내 dependencies에 의존을 추가해주어야 한다.
다음은 TDD 스터디 프로젝트의 build.gradle file이다. Jupiter 관련 의존성이 추가된 것을 확인할 수 있다.
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.7'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
group = 'testdriven'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// junit
testImplementation('org.junit.jupiter:junit-jupiter:5.5.0')
}
tasks.named('test') {
useJUnitPlatform()
}
🍰 @Test Annotation & Test Method
- @Test Annotation
- Test 메소드 위에 @Test Annotation 붙임
- not private
- assertEqals() Method
- 실제 값과 테스트 값이 같은지 비교
@Test
void plus(){
int result = Calculator.plus(1, 2);
assertEquals(3, result); // 실제 값과 동일 한지 비교
assertEquals(5, Calculator.plus(4, 1));
}
🍰 주요 단언 메소드
Method | explanation |
assertEquals(expected, actual) | 실제 값과 기대하는 값이 같은지 검사 |
assertNotEquals(unexpected, actual) | 실제 값과 특정 값과 같지 않은지 검사 |
assertSame(Object expected, Object actual) | 두 객체가 동일한 객체인지 검사 |
assertNotSame(Object unexpected, Object actual) | 두 객체가 동일하지 않은 객체인지 검사 |
assertTrue(boolean condition) | 값이 true 인지 검사 |
assertFalse(boolean condition) | 값이 false 인지 검사 |
assertNull(Object actual) | 값이 null 인지 검사 |
assertNotNull(Object actual) | 값이 null 이 아닌지 검사 |
fail() | 테스트 실패 처리 |
assertThrows(Class<T> expectedType, Executable executable) |
executable을 실행한 결과로 지정한 타입의 익셉션이 발생하는지 검사 |
assertDoesNotThrow(Executable executable) | executable을 실행한 결과로 지정한 타입의 익셉션이 발생하지 않는지 검사 |
Assert 메서드는 실패하면 다음 코드를 실행하지 않고 바로 익셉션 발생
assertAll() : 실패한 Test 확인 메소드
public class TestMethod {
@Test
void assertEqualsMethod(){
LocalDate localDate = LocalDate.now();
LocalDate localDate1 = LocalDate.now();
assertEquals(localDate, localDate1);
}
@Test
void assertEqualsMethod2(){
assertEquals(3, 5/2); // 에러 발생시, 검증 코드 실패로
assertEquals(4, 2*2); // 이 코드 실행 되지 않음
}
@Test
void failMethod(){
try{
AuthService authService = new AuthService();
authService.authenticate(null, null); // null 이 발생했는데, 익셉션이 발생하지 않으면 테스트 실패 (null == null)
fail();
}catch (IllegalArgumentException e){
}
}
@Test
void assertThrowsMethod(){
IllegalArgumentException e = assertThrows(IllegalStateException.class,
()->{
AuthService authService = new AuthService();
authService.authenticate(null, null);
});
assertTrue(e.getMessage().contains("id"));
}
}
- assertAll()
모든 검증을 실행하고 그 중에 실패한 것이 있는지 확인하고 싶을 때 사용하는 메소드이다.
@Test
void assertAllMethod(){
assertAll(
() -> assertEquals(3, 5/2), // 실패
() -> assertEquals(4, 2*2),
() -> assertEquals(6, 11/2) // 실패
);
}
/**
*에러 코드
*/
org.gradle.internal.exceptions.DefaultMultiCauseException: Multiple Failures (2 failures)
org.opentest4j.AssertionFailedError: expected: <3> but was: <2>
org.opentest4j.AssertionFailedError: expected: <6> but was: <5>
at app//org.junit.jupiter.api.AssertAll.assertAll(AssertAll.java:80)
at app//org.junit.jupiter.api.AssertAll.assertAll(AssertAll.java:44)
at app//org.junit.jupiter.api.AssertAll.assertAll(AssertAll.java:38)
---

🍰 테스트 라이프 사이클
🍪@BeforeEach Annotation And @AfterEach Annotation
JUnit 실행 순서
1. 테스트 메서드르 포함한 객체 생성
2. (존재하면) @BeforeEach Annotation 이 붙은 메소드 실행
3. @Test 어노테이션이 붙은 메소드 실행
4. (존재하면) @AfterEach 어노테이션이 붙은 메소드 실행
@BeforeEach
테스트를 실행하는데 필요한 준비 작업 할 때 사용
not private
예) 테스트에서 사용할 임시 파일을 생성, 테스트 메소드 사용할 객체 생성
@AfterEach
테스트를 실행한 후에 정리할 것이 있을 때 사용
not private
예) 태스트에서 사용한 임시 파일을 삭제
public class LifecycleTest {
public LifecycleTest(){
System.out.println("new LifecycleTest");
}
// 각각 테스트 하나 진행 시작할 때 호출되는 메소드
@BeforeEach
void setUp(){
System.out.println("setUp");
}
@Test
void a(){
System.out.println("A");
}
@Test
void b(){
System.out.println("B");
}
// 각각 테스트 하나 진행 끝날 때 호출되는 메소드
@AfterEach
void tearDown(){
System.out.println("tearDown");
}
}
🍪@BeforeAll Annotation And @AfterAll Annotation
한 클래스의 모든 테스트 메서드가 실행되기 전에 특정 작업을 수행해야하는 경우
@BeforeAll
정적 메소드에 사용
클래스의 모든 테스트 메소드를 실행하기 전에 한 번 살행
@AfterAll
정적 메소드에 사용
클래스의 모든 테스트 메소드를 실행한 뒤 생성
🍰 테스트 메서드 간 실행 순서 의존과 필드 공유하지 않기
public class BadTest {
private FileOperator op = new FileOperator();
private static File file;
@Test
void fileCreationTest(){
File createdFile = op.createFile(); // 파일 생성
assertTrue(createdFile.length()>0);
this.file = createdFile;
}
@Test
void readFileTest(){
long data = op.readData(file); // fileCreationTest에서 생성한 파일 사용
assertTrue(data>0);
}
}
다음 위의 코드는 하나의 File을 두 Test 코드가 공유하고 있다.
위에서부터 fileCreationTest -> readFileTest 순으로 코드가 진행되면 테스트에 성공하지만, 테스트 메소드가 특정 순서로 실행된다는 가정하에 테스트 메소드를 작성하면 안된다.
[순서는 버전에 따라 달라질 수 있기 때문이다.]
따라서 각 테스트 메소드는 서로 독립적으로 동작해야 한다. 한 메소드의 결과에 따라 다른 메소드의 실행 결과가 달라지면 안된다.
🍰 @DisplayName, @Disabled
@DisplayName
이름만으로 테스트 내용을 설명하기 부족할 때 사용
공백, 특수 문자 사용 등
테스트 결과를 메소드 이름이 아닌 DisplayName으로 표시
@Disabled
테스트 메소드를 실행 대상에서 제외할 때 사용
아직 완성되지 않았거나 잠시동안 테스트를 실행하지 말아야 할 때
@DisplayName(("@DisplayName 테스트"))
public class DisplayNameTest {
@DisplayName("값이 같은지 비교")
@Test
void assertEqualsMethod(){
}
@DisplayName("익셉션 발생 여부 테스트") // 테스트 메모
@Test
void assertThrowsTest(){
}
@Disabled // 특정 테스트 실행하고 싶지 않을 때
@Test
void failMethod(){
}
}
🍰 모든 테스트 실행하기
배포 전, 모든 테스트를 실행하여 깨지는 테스트가 없는지 확인해야 한다.
이 때, 실패하는 테스트가 존재하면 잘못되었다는 것으로 코드 푸시나 배포를 잠시 멈추고 문제 원인을 찾아 제거한다.
# maven test
mvn test
# gradle test
gradle test
https://github.com/GDSC-MYONGJI/22-23-TDD-Study
GitHub - GDSC-MYONGJI/22-23-TDD-Study: [22-23 GDSC MJU 1기] TDD 스터디를 위한 저장소
[22-23 GDSC MJU 1기] TDD 스터디를 위한 저장소. Contribute to GDSC-MYONGJI/22-23-TDD-Study development by creating an account on GitHub.
github.com
참고용 도서
http://www.yes24.com/Product/Goods/89145195
테스트 주도 개발 시작하기 - YES24
TDD(Test-Driven Development)는 테스트부터 시작한다. 구현을 먼저 하고 나중에 테스트하는 것이 아니라 먼저 테스트를 하고 그다음에 구현한다. 구현 코드가 없는데 어떻게 테스트할 수 있을까? 여기
www.yes24.com
'Group Study (22-23) > TDD' 카테고리의 다른 글
[TDD] 6. 대역 (1) | 2023.02.24 |
---|---|
[TDD] 5. 테스트 코드의 구성 (1) | 2023.02.02 |
[TDD] 3. TDD ∙ 기능 명세 ∙ 설계 (3) | 2023.01.21 |
[TDD] 2. 테스트 코드 작성 순서 (1) | 2023.01.17 |
[TDD] 1. TDD 시작 (4) | 2023.01.06 |