반응형
Intro
Node.js 백엔드 개발자가 굳이 JUnit을 참고를 할 필요가 없는데 굳이 참고를 한 이유가 궁금할 것이다. 이유는 단순하다.
Node.js를 사용해서 TDD를 공부하려고 하는데 관련된 레퍼런스가 아무래도 자바공화국 답게 Java/Spring으로 다룬 테스트 코드가 더 많았기 때문이다(물론 Node.js와 관련된 레퍼런스도 찾을 수 있었지만 보통 Jest를 사용하는 아주 간단한 예제 정도 밖에 없었음). 책이나 유튜브, 인강을 보면서 공부를 하려고 해도 온 세상이 Java라서 어쩔 수 없이 Java, JUnit, Spring으로 만들어진 테스트 코드를 참고로 하면서 공부를 할 수 밖에 없었다. 다행히 학부 때 Java/SpringBoot를 다뤄본적이 있어 그렇게 어렵지는 않게 공부를 할 수 있었다.
@ParameterizedTest
- 테스트 모듈에 전달할 값을 어노테이션의 배열로 지정한다
- 테스트 모듈은 배열 길이 만큼 실행되고, 배열의 요소가 하나씩 인자 값으로 전달 된다
- TYPES는 인자 값으로 사용할 타입의 복수명사(ints, strings, bytes, …)로 선언해야 한다
JUnit
ValueSource
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class ParameterizedTestExample {
@ParameterizedTest
@CsvSource({
"1, 2, 3",
"2, 3, 5",
"3, 5, 8"
})
void add(int first, int second, int expectedResult) {
assertEquals(expectedResult, first + second);
}
}
CsvValue
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class ParameterizedTestExample {
@ParameterizedTest
@CsvSource({
"1, 2",
"2, 4",
"3, 6"
})
void testMultiplication(int input, int expected) {
assertEquals(expected, input * 2);
}
}
Jest
ValueSource
describe("", () => {
it.each([1, 2, 3, 4])('', (numbers) => {
expect(numbers).toBeGreaterThan(0);
});
});
CsvValue
describe('testMultiplication', () => {
test.each([
[1, 2],
[2, 4],
[3, 6],
])('given %i as input, returns %i', (input, expected) => {
expect(input * 2).toBe(expected);
});
});
@RepeatedTest
- 지정된 횟수(value)만큼 반복적으로 실행되도록 설정하는 어노테이션
- 10번 반복해서 테스트 해야 하는 경우, For 구문을 사용해서 테스트 코드를 작성할 수도 있지만, 코드의 가독성이 떨어진다
- RepeatedTest 어노테이션을 사용하면 For 구문 없이 반복 실행할 수 있다
JUnit
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.RepeatedTest;
class RepeatedTestExample {
@RepeatedTest(5)
void testRepeatedly() {
int randomValue = (int) (Math.random() * 10);
assertTrue(randomValue < 10);
}
}
Jest
describe('testRepeatedly', () => {
for (let i = 0; i < 5; i++) {
test(`repetition ${i + 1}`, () => {
const randomValue = Math.floor(Math.random() * 10);
expect(randomValue).toBeLessThan(10);
});
}
});
@TestFactory
동적으로 테스트 모듈을 생성하는 팩토리 메서드를 정의하는 어노테이션
- 동적으로 다양한 테스트 케이스를 검증해야 하는 경우에 사용한다
- 인수테스트처럼 사용자 시나리오 테스트를 할 때 사용한다
JUnit
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import java.util.Arrays;
import java.util.Collection;
import static org.junit.jupiter.api.Assertions.assertTrue;
class TestFactoryExample {
@TestFactory
Collection<DynamicTest> dynamicTests() {
return Arrays.asList(
DynamicTest.dynamicTest("Test 1", () -> assertTrue(Math.random() < 0.5)),
DynamicTest.dynamicTest("Test 2", () -> assertTrue(Math.random() < 0.5)),
DynamicTest.dynamicTest("Test 3", () -> assertTrue(Math.random() < 0.5))
);
}
}
Jest
describe('dynamicTests', () => {
const tests = [
{ name: 'Test 1', fn: () => expect(Math.random()).toBeLessThan(0.5) },
{ name: 'Test 2', fn: () => expect(Math.random()).toBeLessThan(0.5) },
{ name: 'Test 3', fn: () => expect(Math.random()).toBeLessThan(0.5) }
];
tests.forEach(({ name, fn }) => {
test(name, fn);
});
});
@ExtendWith
- 테스트 클래스에서 공통적으로 사용할 확장 기능을 설정하는 어노테이션
- Mock 객체를 쉽게 사용할 수 있도록 기능을 제공하는 프레임워크의 기능을 JUnit에서도 사용할 수 있또록 확장해주는 역할
- JUnit의 테스트 코드에서 Spring ApplicationContext 기능을 JUnit에서도 사용할 수 있도록 확장해주는 역할
JUnit
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class ExtendWithExample {
@Mock
private MyService myService;
@InjectMocks
private MyComponent myComponent;
@Test
void testService() {
when(myService.doSomething()).thenReturn("Mocked Result");
String result = myComponent.useService();
assertEquals("Mocked Result", result);
}
}
Jest
- Jest는 Jest 자체에서 mocking 기능을 활용할 수 있다
- jest.fn()을 이용해서 service 객체를 mocking하면서, mockReturnValue()를 활용하여 반환 값을 설정할 수 있다
const myService = {
doSomething: jest.fn(),
};
const myComponent = {
useService: function() {
return myService.doSomething();
},
};
describe('ExtendWithExample', () => {
test('testService', () => {
myService.doSomething.mockReturnValue('Mocked Result');
const result = myComponent.useService();
expect(result).toBe('Mocked Result');
});
});
실습
describe("JUnit annotation을 Jest로 변환", () => {
// JUnit: ParameterizedTest
it.each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
])(
"ParameterizedTest(%i, %i, %i)",
(firstElement, secondElement, expected) => {
expect(firstElement + secondElement).toBe(expected);
}
);
// JUnit: DisplayName
it("DisplayName", () => {
expect(true).toBe(true);
});
// JUnit: Disabled
it.skip("Disabled", () => {
expect(true).toBe(true);
});
// JUnit: Timeout
it("Timeout", () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 1000);
});
}, 2000);
// JUnit: Nested
describe("Nested", () => {
it("Nested", () => {
expect(true).toBe(true);
});
});
// JUnit: RepeatedTest
for (let i = 0; i < 3; i++) {
it("RepeatedTest", () => {
expect(true).toBe(true);
});
}
// JUnit: TestFactory
const testFactory = (firstElement, secondElement, expected) => {
it(`TestFactory(${firstElement}, ${secondElement}, ${expected})`, () => {
expect(firstElement + secondElement).toBe(expected);
});
};
testFactory(1, 1, 2);
// JUnit: RepeatedTest + TestFactory
for (let i = 0; i < 3; i++) {
testFactory(1, 1, 2);
}
const tests = [
{ name: "Test 1", fn: () => expect(Math.random()).toBeLessThan(1) },
{ name: "Test 2", fn: () => expect(Math.random()).toBeLessThan(1) },
{ name: "Test 3", fn: () => expect(Math.random()).toBeLessThan(1) },
];
tests.forEach(({ name, fn }) => {
it(name, fn);
});
});
PASS ./annotation.test.js
JUnit annotation을 Jest로 변환
✓ ParameterizedTest(1, 1, 2) (3 ms)
✓ ParameterizedTest(1, 2, 3)
✓ ParameterizedTest(2, 1, 3)
✓ DisplayName (4 ms)
✓ Timeout (1003 ms)
✓ RepeatedTest
✓ RepeatedTest
✓ RepeatedTest (1 ms)
✓ TestFactory(1, 1, 2)
✓ TestFactory(1, 1, 2)
✓ TestFactory(1, 1, 2)
✓ TestFactory(1, 1, 2)
✓ Test 1
✓ Test 2
✓ Test 3
○ skipped Disabled
Nested
✓ Nested
Test Suites: 1 passed, 1 total
Tests: 1 skipped, 16 passed, 17 total
Snapshots: 0 total
Time: 1.297 s, estimated 2 s
Ran all test suites.
Watch Usage: Press w to show more.
반응형
'TDD' 카테고리의 다른 글
| Jest 많이 썼던 내용들 정리 (1) (5) | 2024.10.24 |
|---|---|
| 간단한 게시판 만들면서 TDD 맛보기 (POST 요청) (1) | 2024.09.01 |
| 단위 테스트: 기본 개념 (0) | 2024.08.31 |
| 예제; 암호 검사기 (0) | 2024.08.31 |
| 테스트의 개념과 중요성 (0) | 2024.08.31 |