728x90
problem
누구나 한번쯤 테스트 코드를 짜다보면 이런 생각을 해봤을 것이다.
" 누가 테스트 데이터 좀 자동으로 만들어주면 안되나... "
그래서 찾았다....
solution
네이버페이에서 개발한 오픈소스 Fixture Monkey
https://github.com/naver/fixture-monkey
Build.gradle
//JAVA
testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter:1.0.15")
//Kotlin
testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter-kotlin:1.0.15")
Fixture Monkey
- 보일러플레이트를 줄이고 테스트 로직에 집중 할 수 있다.
- 테스트하고자 하는 관심사를 쉽게 확인할 수 있다.
- 테스트 개체 간의 복잡한 의존성을 매번 작성하지 않고 쉽게 관리가 가능하다.
- 랜덤한 값이 바인딩 되기 때문에 테스트의 신뢰성을 높여줄 수 있다.
생성 동작 지정
- BeanArbitraryIntrospector ( 기본생성자와 setter 생성)
- ConstructorPropertiesArbitraryIntrospector (레코즈타입 클래스 생성)
- FieldReflectionArbitraryIntrospector (리플렉션 생성)
- BeanArbitraryIntrospector (빌더 생성 -> 클래스에 빌더가 있어야 함)
- ArbitraryIntrospector (다중 생성 -> 위 생성타입 여러개를 사용하여 생성)
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
//여기에 넣으면 됌
.objectIntrospector(BeanArbitraryIntrospector.INSTANCE)
.build();
유효성 검사
Bean Validation ( 유효성 검사 ) 도 가능 하다.
fixture-monkey-jakarta-validation 의존성에 추가해주어야 한다.
하지만 위의 starter를 추가해 주었다면 자동으로 추가 된다.
@Test
void test() {
// given
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE)
.plugin(new JakartaValidationPlugin())
.build();
// when
Product actual = fixtureMonkey.giveMeOne(Product.class);
// then
then(actual).isNotNull();
then(actual.getId()).isGreaterThan(0);
then(actual.getProductName()).isNotBlank();
then(actual.getPrice()).isLessThanOrEqualTo(100000);
then(actual.getOptions().size()).isGreaterThanOrEqualTo(3);
then(actual.getOptions()).allSatisfy(it -> then(it).isNotEmpty());
then(actual.getCreatedAt()).isNotNull().isLessThanOrEqualTo(Instant.now());
}
테스트를 통해 @Valid 적용 된 개체인지 확인 할 수 있다.
내장함수
픽스처몽키에는 다양한 내장함수들이 있다.
또 내장함수에 함수를 넣어 전달 할 수 있어 더 세밀하고 고도화된 개체를 생성 할 수 있다.
.set() / 필드에 해당 값 주입
@Test
void test() {
// given
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE)
.build();
long id = 1000;
// when
Product actual = fixtureMonkey.giveMeBuilder(Product.class)
.set("id", id)
.sample();
// then
then(actual.getId()).isEqualTo(1000);
}
특정 필드가 임의의 값을 가지기 원한다면 .set() 함수를 쓰자
.size() , .minSize() .maxSize() / 필드의 갯수 지정
fixtureMonkey.giveMeBuilder(Product.class)
.size("options", 5); // size:5
fixtureMonkey.giveMeBuilder(Product.class)
.size("options", 3, 5); // minSize:3, maxSize:5
.setNull() , .setNotNull / Nullable
fixtureMonkey.giveMeBuilder<Product>()
.setNullExp(Product::id)
fixtureMonkey.giveMeBuilder<Product>()
.setNotNullExp(Product::id)
함수
InnerSpec innerSpec = new InnerSpec()
.property("merchantInfo", it -> it.entry(1000, "ABC Store"));
Map 내의 엔트리 값 지정
InnerSpec innerSpec = new InnerSpec()
.property("merchantInfo", merchantInfo -> merchantInfo.allKey(1000));
InnerSpec innerSpec = new InnerSpec()
.property("merchantInfo", merchantInfo -> merchantInfo.allValue("ABC Store"));
InnerSpec innerSpec = new InnerSpec()
.property("merchantInfo", merchantInfo -> merchantInfo.allEntry(1000, "ABC Store"));
필드 특정 조건 수행 / Post Condition
InnerSpec innerSpec = new InnerSpec()
.property("id", id -> id.postCondition(Long.class, it -> it > 0));
postCondition 사용시 많은 자원이 사용되므로 .set()을 사용하는것을 권장
중복 내장 함수
InnerSpec innerSpec = new InnerSpec()
.property("id", 1000L);
fixtureMonkey.giveMeBuilder(Product.class)
.setInner(
new InnerSpec()
.property("nestedObject", nestedObject -> nestedObject.inner(innerSpec))
);
리펙토링 전
리펙토링 후
참고문헌
https://naver.github.io/fixture-monkey/v1-0-0/docs/generating-objects/fixture-monkey/
728x90
'개-발 > Java + Spring + Kotlin' 카테고리의 다른 글
[Spring] Transaction 트랜잭션의 이해 (0) | 2024.04.24 |
---|---|
[Spring] Jasypt 중요 정보를 암호화 하자 (0) | 2024.04.12 |
[Spring Batch] 스프링 배치 개념 (0) | 2024.03.27 |
[Kotlin] build.gradle 환경변수 적용 (0) | 2024.03.18 |
[Spring] Java + Kotlin 멀티 모듈 프로젝트 만들기 (설정편) (0) | 2024.03.17 |