ν μ€νΈλ₯Ό μμ±νλ€ λ³΄λ©΄ νλ‘λμ μ½λλ³΄λ€ ν μ€νΈ ν½μ€μ²λ₯Ό λ§λλ λ° λ λ§μ μκ°μ΄ λλ κ²½μ°κ° μμ΅λλ€. ν μ€νΈ μμ±μ΄ λ²κ±°λ‘κ³ μκ°μ΄ λ§μ΄ 걸릴μλ‘ ν μ€νΈ μ½λλ₯Ό μλ΅νκ² λκ³ , κ²°κ΅ κ²°ν¨μ μ·¨μ½ν μμ€ν μ ꡬνν μνμ΄ μ»€μ§λλ€.
μ£Όλ¬Έ λ‘μ§μ ν μ€νΈνκΈ° μν΄ Order ν½μ€μ²λ₯Ό λ§λ€μ΄μΌ νλλ°, Order κ°μ²΄ λ΄λΆ νλλ§ 24κ°μκ³ λ΄λΆ κ°μ²΄μ νλκΉμ§ ν©μΉλ©΄ μ μν΄μΌ ν νλκ° μμ κ°μ λ¬νμ΅λλ€. κ²λ€κ° μΌμ΄μ€λ§λ€ λ€λ₯Έ μνλ₯Ό κ°μ§ Order ν½μ€μ²λ₯Ό μΆκ°λ‘ λ§λ€μ΄μΌ ν΄μ ν μ€νΈ μ€λΉμ μλΉν μκ°μ΄ μμλμμ΅λλ€.
κ·Έλ¬λ μ€ Fixture MonkeyλΌλ PBT(Property Based Testing) λΌμ΄λΈλ¬λ¦¬λ₯Ό μκ² λμκ³ , μ΄λ₯Ό νμ©ν΄ ν
μ€νΈλ₯Ό ν¨μ¬ νΈλ¦¬νκ² μμ±ν μ μμμ΅λλ€. μ€λμ κ°μΈμ μΌλ‘ μ μ©νκ² μ¬μ©νλ ν΅μ¬ κΈ°λ₯μ κ°λ΅ν μκ°νκ³ μ ν©λλ€.
FixtureMonkeyμ μ£Όμ κΈ°λ₯
- λλ€νκ³ λ³΅μ‘ν μ μ½ μ‘°κ±΄μ κ°μ§ κ°μ²΄λ₯Ό μλ μμ±ν©λλ€
- μ€μ ν μ μ½ μ‘°κ±΄μ κ²μ¦ν μ μμ΅λλ€
- ν μ€νΈ μΌμ΄μ€λ§λ€ κ°μ²΄λ₯Ό μ μ°νκ² μ μ΄ν μ μμ΅λλ€
FixtureMonkeyλ μν°ν° νλμ μ§μ λ Bean Validation μ΄λ
Έν
μ΄μ
μ λ°λΌ μ ν¨ν μμ±κ°μ κ°μ§ κ°μ²΄λ₯Ό μμ±ν©λλ€.
μ€ν¨ ν
μ€νΈ μμ±μ²λΌ νΉμ μΌμ΄μ€μμ 쑰건μ μΆκ°νκ±°λ μ μ½μ λ²μ΄λ νλλ₯Ό μ€μ ν΄μΌ ν κ²½μ°, ArbitraryBuilderλ₯Ό μ¬μ©ν΄ ν½μ€μ²λ₯Ό μ μ΄ν μ μμ΅λλ€. ArbitraryBuilderλ λΉλ ν¨ν΄μ ν΅ν΄ κ°μ²΄μ νλκ°μ μνλ λλ‘ μ€μ νμ¬ μμ±ν μ μμ΅λλ€.
μμ
Without FixtureMonkey Test
FixtureMonkeyμ νΈλ¦¬ν¨μ μ΄ν΄λ³΄κΈ°μ μμ, λ¨Όμ κΈ°μ‘΄ λ°©μμΌλ‘ ν
μ€νΈλ₯Ό μμ±ν΄ λ³΄κ² μ΅λλ€.
λ€μμ μ£Όλ¬Έ κ³Όμ μμ μ λ ₯λ λ°°μ‘μ§ μ£Όμκ° μ ν¨νμ§ κ²μ¦νλ ν μ€νΈμ λλ€.
data class Order(
val product: List<Product>,
val purchaserName: String,
val receiver: Receiver,
val totalPrice: Long,
val coupon: List<Coupon>,
val delivery: Delivery,
)
class OrderFixture {
companion object {
fun create(
id: Long = 1L,
product: List<Product> = listOf(
Product(name = "μ΄μ½λ¦Ώ", price = 300L),
Product(name = "ν€λ³΄λ", price = 20000L),
),
purchaserName: String = "νκΈΈλ",
receiver: Receiver = Receiver(name = "νκΈΈλ", "01012341234"),
totalPrice: Long = 20300L,
coupon: List<Coupon> = listOf(Coupon()),
delivery: Delivery = Delivery("κ²½κΈ°λ", "203λ 1023νΈ", true),
): Order {
return Order(
id = id,
product = product,
purchaserName = purchaserName,
receiver = receiver,
totalPrice = totalPrice,
coupon = coupon,
delivery = delivery,
)
}
}
}
class OrderServiceTestWithOutFixtureMonkey : DescribeSpec({
val sut = OrderService()
val log = LoggerFactory.getLogger(this.javaClass)
describe("λ°°μ‘ μ£Όμ μ ν¨μ± κ²μ¬") {
it("μ ν¨μ± κ²μ¦μ ν΅κ³Όνλ€.") {
val order = OrderFixture.create()
shouldNotThrowAny {
sut.validateDeliveryAddress(order)
}
}
it("μ§λ² μ£Όμλ₯Ό μ
λ ₯λ°μμ κ²½μ°, μμΈ μ£Όμκ° μμΌλ©΄ μ λλ€") {
val order = OrderFixture.create(delivery = Delivery(baseAddress = "κ²½κΈ°λ", road = false, detailAddress = null))
val exception = shouldThrow<IllegalArgumentException> {
sut.validateDeliveryAddress(order)
}
exception.message shouldBe "μ§λ² μ£Όμμλ μμΈ μ£Όμκ° λ°λμ νμν©λλ€."
}
}})
ν
μ€νΈ μ±κ³΅κ³Ό μ€ν¨ μΌμ΄μ€λ₯Ό μν OrderFixture κ°μ²΄λ₯Ό μ μνμ¬ μ¬μ©νμ΅λλ€. Order κ°μ²΄μ μ μλ νλλΏλ§ μλλΌ μ°κ΄λ κ°μ²΄μ νλκ°λ€λ ν¨κ» μ μν΄μΌ νκΈ° λλ¬Έμ μλΉν λ²κ±°λ‘μ΄ μμ
μ
λλ€.
λ§μ½ μ°κ΄λ μν°ν°κ° λ λ§κ³ μ μν΄μΌ ν νλ μκ° ν¨μ¬ λ§μμ§λ€λ©΄, ν
μ€νΈ ν½μ€μ²λ₯Ό μ μνλ λ° ν° λΉμ©μ΄ μλͺ¨λ©λλ€.
FixtureMonkey Test
μ΄λ²μλ FixtureMonkeyλ₯Ό μ¬μ©ν΄ κ°λ¨νκ² ν½μ€μ²λ₯Ό μμ±νμ¬ ν
μ€νΈλ₯Ό μμ±ν΄ λ³΄κ² μ΅λλ€.
λ¨Όμ build.gradle.ktsμ μμ‘΄μ±μ μΆκ°ν©λλ€.
// fixture monkey testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter-kotlin:1.0.14") testImplementation("com.navercorp.fixturemonkey:fixture-monkey-jakarta-validation:0.6.3") testImplementation("com.navercorp.fixturemonkey:fixture-monkey-jackson:0.6.3")
DefaultMonkeyCreator
fun monkey() : FixtureMonkey {
return FixtureMonkey.builder()
.plugin(KotlinPlugin())
.build()
}
FixtureBuilders
fun <T> defaultFixtureBuilder(clazz: Class<T>): ArbitraryBuilder<T> {
return monkey().giveMeBuilder(clazz)
}
class OrderServiceTestWithFixtureMonkey: DescribeSpec({
val sut = OrderService()
val log = LoggerFactory.getLogger(this.javaClass)
describe("λ°°μ‘ μ£Όμ μ ν¨μ± κ²μ¬") {
it("μ ν¨μ± κ²μ¦μ ν΅κ³Όνλ€.") {
val order = defaultFixtureBuilder(Order::class.java)
.setExp(
Order::delivery,
Delivery(baseAddress = "κ²½κΈ°λ", detailAddress = null, road = true))
.sample()
shouldNotThrowAny {
sut.validateDeliveryAddress(order)
}
}
it("μ§λ² μ£Όμλ₯Ό μ
λ ₯λ°μμ κ²½μ°, μμΈ μ£Όμκ° μμΌλ©΄ μ λλ€") {
val order = defaultFixtureBuilder(Order::class.java)
.setExp(
Order::delivery,
Delivery(baseAddress = "κ²½κΈ°λ", detailAddress = null, road = false))
.sample()
val exception = shouldThrow<IllegalArgumentException> {
sut.validateDeliveryAddress(order)
}
exception.message shouldBe "μ§λ² μ£Όμμλ μμΈ μ£Όμκ° λ°λμ νμν©λλ€."
}
}})
μ΄μ²λΌ FixtureMonkeyλ₯Ό μ¬μ©νλ©΄ λλ€ν νλκ°μ κ°μ§ ν½μ€μ² κ°μ²΄λ₯Ό μμ½κ² μμ±ν μ μμ΅λλ€.
setterλ₯Ό ν΅ν΄μ κ°μ²΄λ₯Ό μ μ΄ν μ μλλ°, ν
μ€νΈνκ³ μ νλ νλλ§ λͺ
νν νννκΈ° λλ¬Έμ ν
μ€νΈμ κ΄μ¬μ¬λ₯Ό λ°λ‘ νμ
ν μ μλ€λ μ₯μ μ΄ μμ΅λλ€.