아래 글은 어떤 외국 PPT 자료를 참고하였는데, 어디서 받았는지 기억이 나질 않는다..
1. 테스트용 공통 인스턴스
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestClass{
private val name = "TOMAS"
private val age = 28
@Test
fun test1() {
}
@Test
fun test2() {
}
}
Junit 을 사용해서 테스트를 작성하면,
같은 클래스에 작성하더라도 @Test 가 붙은 메서드마다 새로운 객체로 테스트를 진행한다.
따라서 전역변수를 한번 선언하더라도 테스트의 수 만큼 생성되게 되는 것인데,
위와 같이 @TestInstance 의 PER_CLASS 를 사용하면 변수를 val 로 한번만 선언 후 재사용할 수 있다.
2. @Nested 에너테이션을 사용하면 계층구조의 테스트를 작성할 수 있다.
@DisplayName("자동차중")
class CarTest {
@Nested
@DisplayName("벤츠에서 만든 차는")
class BenzTest { // 외부 참조가 필요하다면 inner class
@Nested
@DisplayName("가격이")
class PriceTest {
@DisplayName("1억이 넘는다.")
@Test
fun test1() {
}
@DisplayName("1억이 넘지 않으면 에러다.")
@Test
fun test2() {
}
}
}
}
이런 식으로 구성하면 깔끔한 테스트 결과창을 볼 수 있다.
3. 확장함수를 이용한 kotlin 중복제거
assertThat(price1).isCloseTo(0.3f, Offset.offset(EPSILON))
assertThat(price2).isCloseTo(0.2f, Offset.offset(EPSILON))
assertThat(price3).isCloseTo(0.5f, Offset.offset(EPSILON))
// 위 코드를 확장함수로 중복제거
fun AbstractFloatAssert<*>.isCloseTo(expected: Float)
= this.isCloseTo(expected, Offset.offset(EPSILON))
assertThat(price1).isCloseTo(0.3f, EPSILON)
assertThat(price2).isCloseTo(0.2f, EPSILON)
assertThat(price3).isCloseTo(0.5f, EPSILON)
4. Mockk 을 이용해서 매 테스트마다 목킹을 새로 하기
@ExtendWith(MockKExtension::class)
class TestClass {
private lateinit var payService: PayService
private lateinit var carRepository: CarRepository
@BeforeEach
fun init() {
payService = mockk()
client = mockk()
}
}
// 위 보단 아래가 빠르다.
class TestClass {
private val payService: PayService
private val carRepository: CarRepository
private lateinit var carService: CarService // injectMockk 이 매번 필요하면 이것만 lateinit
@BeforeEach
fun init() {
clearMocks(payService, carRepository)
carService = CarService(payService, carRepository)
}
}
매 테스트마다 목킹을 새로 할 때 init() 에서 mocking 을 하는 것 보단
mockk 라이브러리에서 제공하는 clearMocks() 를 사용하면 더 빠르다.
덤으로 val 로 쓸 수 있다는 장점도 있다.
5. data class assertion
data class 는 알다시피 equals() 를 자동으로 적절히 오버라이딩해준다.
따라서 아래와 같은 테스트들이 가능해서 편리하다.
student 는 data class
val expected = Student(id = 2, userId = 9, name = "Cat")
assertThat(actual).isEqualTo(expected)
assertThat(actualList).containsExactly(
Student(id = 1, userId = 9, name = "Cat"),
Student(id = 2, userId = 4, name = "Dog")
)
assertThat(actual)
.isEqualToIgnoringGivenFields(expected,"id")
assertThat(actual)
.isEqualToComparingOnlyGivenFields(expected,"name")
assertThat(actualList)
.usingElementComparatorIgnoringFields("id")
.containsExactly(expected1, expected2)
assertThat(actualList)
.usingElementComparatorOnFields("name")
.containsExactly(expected1, expected2)
6. 그리고 객체의 디폴트값을 설정할 수 있음을 활용한 객체 생성 메서드를 사용하면 좋다.
fun createStudent(
id: Int = 1,
name: String = "Tomas",
grade: Int = 1,
desc: String = "Hi" // 여기 네개들은 바뀌어도 되고 안바뀌어도 될 값들.
) = Student(
id = id,
school = "Koltion Junior School", // 테스트용 객체들 중 고정될 값.
name = name,
grade = grade,
desc = desc
)
@Test
fun test1() {
val stud1 = createStudent(id = 3, name = "Brouce")
val stud2 = createStudent(id = 4, name = "Ruller", grade = 3)
// 중복된 데이터 없이 딱 바뀔 값들만 선언할 수 있다.
}
7. Data class 의 @ParameterizedTest
data class TestData(
val input: String, // 테스트로 넣을 인자
val expected: Order // 인자를 넣었을 때 예상결과
)
@ParameterizedTest
@MethodSource("getTestDataList")
fun varifyList(testData: TestData) {
assertThat(findItem(testData.input)) // when
.isEqualTo(testData.expected) // then
}
// given
private fun getTestDataList() = Stream.of(
TestData(input = "MacBook", expected = Order(13, "MacBook", 3000000)),
TestData(input = "Airpod", expected = Order(24, "Airpod", 320000)),
TestData(input = "AppleWatch", expected = Order(55, "AppleWatch", 500000)),
TestData(input = "IPad", expected = Order(16, "IPad", 1500000))
)
https://phauer.com/2019/modern-best-practices-testing-java/
https://techblog.woowahan.com/5825/
'TIL' 카테고리의 다른 글
TIL) Airflow Xcom (0) | 2022.02.25 |
---|---|
TIL) EPSILON, 몽고 업데이트시 다른 컬럼 값 참조, 몽고 덤프 (0) | 2022.02.24 |
TIL) 인덱스, DFA (0) | 2022.02.23 |
TIL) Webserver vs WAS, NGNIX vs Apache (0) | 2022.02.22 |
TIL) associate 시리즈 (0) | 2022.02.19 |