Study/test
[Kotlin] 테스트 코드 쉽게 작성하기 ( KotlinFixture, MockK, LiveTemplates )
에디개발자
2021. 12. 27. 07:00
반응형
개인적으로 테스트 코드는 중요하다고 생각합니다. 하지만 다양한 이유로 테스트 코드는 많이 Skip 됩니다.
- 시간적 여유가 없을 때
- 테스트 코드를 위한 노가다 작업이 많을 때
- 그 외 등등...
이 방법을 백퍼센트 보완하진 못하지만 좀 더 용이하게 작성하기 위해 제가 사용하는 방법을 공유합니다.
목차
- MockK
- KotlinFixture
- Live Templates
1. MockK
Java 진영에는 Mockito가 있다면 Kotlin 진영에서는 Mockk가 있습니다.
Mockk는 Java에서 Mock처리하는 것과 동일하게 Kotlin스럽게 Mock을 처리할 수 있도록 도와줍니다.
환경구성
build.gradle.kts
testImplementation 'com.ninja-squad:springmockk:{version}'
// 1)
sourceSets {
test.java.srcDirs += 'src/test/kotlin'
}
1) Java Project일 경우 kotlin 폴더를 생성하여 하위에 테스트 코드를 작성하려할 때 이 설정을 넣어주면 자동으로 kotlin 폴더가 테스트 폴더로 지정됨
Project Structure > Modules에서 직접 test folder 지정할 경우 모든 개발자가 이 설정을 해줘야하며 intellij를 재부팅해도 재설정해줘야하는 불편함이 있음
Mockito vs MockK
Mockito 소스와 Mockk 소스를 비교해보겠습니다.
Mockito
@ExtendWith(MockitoExtension.class)
public class TestJava {
@Mock
private TestObject testObject;
private TestService testService;
@BeforeEach
public void setUp() {
testService = new TestService(testObject);
}
@Test
public void runTest() {
// given
String result = "Run!!!";
given(testObject.run()).willReturn(result);
given(testObject.temp())
// when
String actual = testService.runTestObject();
// then
assertThat(actual).isEqualTo(result);
verify(testObject, times(1)).temp();
}
}
MockK
@ExtendWith(MockKExtension::class)
class TestKotlin {
@MockK
private lateinit var testObject: TestObject
private lateinit var testService: TestService
@BeforeEach
fun setUp() {
testService = TestService(testObject)
}
@Test
fun `test`() {
// given
val result = "Run!!!"
every { testObject.run() } returns result
every { testObject.temp() } just runs
// when
val actual = testService.runTestObject()
// then
assertThat(actual).isEqualTo(result)
verify(exactly = 1) { testObject.temp() }
}
}
주요 비교 Point!
// java
given(testObject.run()).willReturn(result);
given(testObject.temp())
verify(testObject, times(1)).temp();
// kotlin
every { testObject.run() } returns result
every { testObject.temp() } just runs
verify(exactly = 1) { testObject.temp()}
Kotlin 진영은 Java 진영에서 가독성을 높게 보완하였습니다.
테스트코드 또한 마찬가지로 MockK를 사용한다면 높은 가독성을 가지는 테스트 코드를 작성할 수 있습니다.
2) KotlinFixture
Test Data의 생성을 쉽게 도와주는 라이브러리입니다.
환경구성
build.gradle.kts
testImplementation 'com.appmattus.fixture:fixture:{version}'
활용 방법
간단하게 선언하여 사용가능
// kotlin
val fixture = kotlinFixture()
every { sampleRepository.findById(any()) } returns
fixture()
val fixture = kotlinFixture {}
// kotlin에서 java 코드 test
val fixture = Fixture()
every { sampleRepositroy.findById(any()) } returns
fixture()
데이터 클래스의 원하는 필드를 설정하여 사용가능
data class Market(
val name: String,
val address: Address,
val customers: List<String>
)
data class Address(
val address1: String,
val address2: String,
val postNumber: String,
)
// 사용
val fixture = kotlinFixture {
nullabilityStrategy(NeverNullStrategy)
optionalStrategy(NeverOptionalStrategy)
}
val market: Market = fixture()
// result
// Market(name=13e5ddd1-c88c-491d-a4ef-e745f1f6c38d, address=Address(address1=640b4dab-6a88-4bb8-9c60-50a81e12bd37, address2=0e58eb58-e785-4754-8cd6-35b32d4d5889, postNumber=a106cd6a-91db-4937-bd44-9aebc1eccd78), customers=[1df56ad6-0ef2-462e-884b-191a3450a6e2, 0cfb0a90-6919-4365-90c6-4c0475ce9c7e, b80b18da-d2c0-4d13-9ee9-6880a7731b72, a3802105-ca82-4034-913d-e1d7828d8ce2, a78f2ff3-d1e6-4738-afff-b482c9373d18])
// 특정 필드 지정
val market2: Market = fixture { property(Market::name} { "마케엣!" }
// result
// Market(name=마케엣!, address=Address(address1=3adff601-3489-45ac-9074-689fb67fe2bd, address2=59d94ca2-0d48-4724-a61b-269e30e10222, postNumber=5c10ebd4-12a8-4b02-8f3c-ac2377c9737f), customers=[64588699-bd6c-4c06-8aa5-2e566618e228, 945f008c-38b2-4b78-830d-dda7389dc0ff, fb76706f-1154-43ac-b7a6-39964cc47592, 2be0479c-eb84-4351-82e2-f6c2f151c63c, 89ee55cc-9972-4d60-880a-c508e7d3ffbc])
주의사항!
data class의 속성 접근자가 private인 경우 위 방법은 사용할 수 없습니다. 아래와 같이 속성값을 하드코딩해야합니다.
data class Market(
private val name: String,
private val address: Address,
)
val fixture = kotlinFixture {
nullabilityStrategy(NeverNullStrategy)
optionalStrategy(NeverOptionalStrategy)
}
val market2: Market2 = fixture {
property<Market2, String>("name") {"Market!!"}
}
println(market2)
다양한 방법으로 활용
두 가지 모두 보완하도록 작성
- kotlinFixture를 이용할 수 있는 메서드 제공
- private 일 경우 하드코딩 없이 객체 생성 메서드 제공 ( private인 경우가 많진 않겠지만.. )
// fixture config
private val fixture = kotlinFixture {
nullabilityStrategy(NeverNullStrategy)
optionalStrategy(NeverOptionalStrategy)
}
// object fixture
class MarketFixture {
companion object {
// kotlinFixture 기능 활용 - (1)
fun generate(c: ConfigurationBuilder.() -> Unit) = fixture<Market> { c }
// 일반 메서드 제공 - (2)
fun generate(
name: String = fixture(),
address: Address = fixture(),
customers: List<String> = fixture()
) = Market(
name = name,
address = address,
customers = customers
)
}
}
fun test() {
// (1)
val a = MarketFixture.generate {
property(Market::name) {"Market!!!"}
}
// (2)
val b = MarketFixture.generate(name = "Market!!!!")
}
3. Live Templates
Intellij의 LiveTemplates 의 기능을 이용하여 반복적인 작업을 처리합니다.
반복적인 코드 적용
BeforeEach
// live templates
@BeforeEach
fun setUp() {
$END$
}
// code
// tsetup
@BeforeEach
fun setUp() {
}
Nested Class
// live templates
@Nested
@DisplayName("$CLASS_DESCRIPTION$")
inner class $CLASS_NAME$ {
$END$
}
// code
// tnested
@Nested
@DisplayName("")
inner class {
}
Test Method
// live templates
@Test
fun `$METHOD_NAME$`() {
}
// code
// tdd
@Test
fun ``() {
}
assert
// live templates
// assertall
assertAll(
{ assertThat($A$).isEqualTo($B$) }
)
// asserteq
assertThat($A$).isEqualTo($B$)
// assertsize
assertThat($A$).hasSize($B$)
// code
// asserteq
assertThat().isEqualTo()
// assertall
assertAll(
{ assertThat().isEqualTo() }
)
// assertsize
assertThat().hasSize()
반응형