본문 바로가기
Kotlin

fixture monkey BuilderGroups

by ybs 2021. 12. 27.
반응형

이전 글에서 BuilderGroup 추가에 대해서 제대로 설명하지 않고 넘어가서 따로 정리했다.

fun fixtureMonkeyBuilder(
    objectMapper: ObjectMapper = JsonMappers.OBJECT_MAPPER
): FixtureMonkeyBuilder = KFixtureMonkeyBuilder()
    .defaultGenerator(JacksonArbitraryGenerator(objectMapper))
    .registerGroup(BuilderGroup::class.java)

 

위 코드 마지막줄에서 registerGroup 에 들어가는 BuilderGroup 클래스를 보자. 아래 코드를 보면 fixture 한개를 셋팅했다. RegisterProduct 객체를 fixture monkey 로 만드는데, 테스트를 위해 고정된 값을 따로 지정했다. fixture monkey 는 기본적으로 모든 필드를 랜덤한 값으로 채운다. 하지만 특정 기능을 테스트 하기 위해서 고정된 값이 필요할 때도 있다. 그 작업을 여기서 한다.

@Suppress("unused")
class BuilderGroup {
    fun registerProduct(fixture: FixtureMonkey): ArbitraryBuilder<RegisterProduct> =
        fixture.giveMeBuilder(RegisterProduct::class.java)
            .set("no", 1234L)
            .minSize("products", 1)
            .set("products[*].productId", "ybs")
            .specAny(
                ExpressionSpec()
                    .setNull("products[*].case1")
                    .setNotNull("products[*].case2"),
                ExpressionSpec()
                    .setNotNull("products[*].case1")
                    .setNull("products[*].case2")
            )
}

 

실제 사용하는 코드에서 fixture.giveMeBuilder(RegisterProduct::class.java) 로 RegisterProduct 객체를 만들면 위에 지정된 디폴트 셋팅으로 채워진다. RegisterProduct no 를 1234 로 고정하고 products 리스트는 최소 1개 이상이 되도록 설정했다. RegisterProduct 안의 Product 리스트도 products[*] 표현식으로 셋팅할 수 있다.

 

물론 사용하는 코드에서 추가적인 set 작업을 하게 되면 그 값으로 덮어 씌워지므로 BuilderGroup 은 어디까지나 디폴트 값을 지정하는 역할이다.

 

마지막으로 specAny 기능을 소개하겠다. RegisterProduct 비지니스 정책 상 Case1, Case2 둘 중 하나만 값이 채워진다고 가정해보자. 여러 도메인에서 공통 클래스로 같이 쓸 때 많이 발생하는 케이스 이기도 하다. fixture monkey 는 specAny 를 통해 Case1 을 null로 set 하면 Case2 를 notNull로 set 하거나 그 반대가 되도록 지원한다. Case1, Case2 중 어떤게 값이 채워질지는 랜덤하다.

@Value
@Builder
public class RegisterProduct {
	long no;

	@Builder.Default
	List<Product> products = List.of();

	@Value
	@Builder
	public static class Product {
		String productId;
		// 비지니스 정책 상 Case1 이 값이 있으면 Case2 는 항상 null이다.
		Case1 case1;
		// 비지니스 정책 상 Case2 가 값이 있으면 Case1 는 항상 null이다.		
		Case2 case2;
	}

	@Value
	@Builder
	public static class Case1 {
		String id;
	}

	@Value
	@Builder
	public static class Case2 {
		String id;
	}
}

 

아래는 5개 RegisterProduct 객체를 출력한 결과다. 고정시킨 값과 아닌 값이 의도한 대로 채워졌음을 확인할 수 있고 RegisterProduct 리스트도 최소 1개 이상인것, 그리고 Case1 Case2 둘 중 하나만 존재함을 확인할 수 있다.

RegisterProduct(no=1234, products=[RegisterProduct.Product(productId=ybs, case1=RegisterProduct.Case1(id=zK=as=Z1*2i)D4~OQa=%U@gEmYb?VIge1zvP`sq[%K+7_R&Ct60lB7z3pH{>~{}`g+b<QX,K2#X@C%6To+I$^cpyG/^>EjY.FF)	R	'YC@?G%" |yO), case2=null), RegisterProduct.Product(productId=ybs, case1=RegisterProduct.Case1(id=), case2=null), RegisterProduct.Product(productId=ybs, case1=RegisterProduct.Case1(id=T~f'*|O$v1iZBKR), case2=null)])
RegisterProduct(no=1234, products=[RegisterProduct.Product(productId=ybs, case1=RegisterProduct.Case1(id={H@+OSDSg0*up), case2=null)])
RegisterProduct(no=1234, products=[RegisterProduct.Product(productId=ybs, case1=RegisterProduct.Case1(id=6S2Sau[sn\	76]), case2=null), RegisterProduct.Product(productId=ybs, case1=RegisterProduct.Case1(id=), case2=null), RegisterProduct.Product(productId=ybs, case1=RegisterProduct.Case1(id=_cOfm), case2=null)])
RegisterProduct(no=1234, products=[RegisterProduct.Product(productId=ybs, case1=null, case2=RegisterProduct.Case2(id=K'p(Y2Eo<$C|bM)), RegisterProduct.Product(productId=ybs, case1=null, case2=RegisterProduct.Case2(id=J^B&))])
RegisterProduct(no=1234, products=[RegisterProduct.Product(productId=ybs, case1=RegisterProduct.Case1(id=k'Y(C|tV&i), case2=null), RegisterProduct.Product(productId=ybs, case1=RegisterProduct.Case1(id=}TRUiX	h8BFeRiSaz), case2=null), RegisterProduct.Product(productId=ybs, case1=RegisterProduct.Case1(id=f+), case2=null)])
반응형