프로젝트 개요
관련 소스 파일
이 페이지의 내용은 다음 소스 파일을 기반으로 생성되었습니다:
- README.md
- docs/docs/introduction/overview.mdx
- fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java
- fixture-monkey/src/main/java/com/navercorp/fixturemonkey/Constants.java
- fixture-monkey/src/main/java/com/navercorp/fixturemonkey/ArbitraryBuilder.java
- fixture-monkey/src/main/java/com/navercorp/fixturemonkey/ArbitraryBuilders.java
- fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java
- fixture-monkey/src/main/java/com/navercorp/fixturemonkey/JavaTypeArbitraryBuilder.java
- fixture-monkey-benchmarks/fixture-monkey-benchmark/src/jmh/java/com/navercorp/fixturemonkey/SeedBenchmark.java
- fixture-monkey-benchmarks/fixture-monkey-benchmark/src/jmh/java/com/navercorp/fixturemonkey/AdapterBenchmark.java
Fixture Monkey는 Java와 Kotlin 환경에서 테스트 객체를 자동으로 생성하고 제어할 수 있도록 설계된 오픈소스 라이브러리다. "Write once, Test anywhere"라는 슬로건 아래, 복잡한 테스트 픽스처 생성 과정을 단순화하고 테스트 코드의 유지보수성을 높이는 것을 핵심 목표로 한다 (README.md:1-28).
이 라이브러리의 가장 독특한 특징은 경로 기반 표현식을 통해 중첩된 필드에 자유롭게 접근하고 설정할 수 있다는 점이다. 개발자는 복잡한 객체 그래프를 수동으로 구성할 필요 없이, 단일 표현식으로 깊이 중첩된 속성까지 제어할 수 있다 (docs/docs/introduction/overview.mdx:1-29).
Naver에서 개발된 Fixture Monkey는 실제 운영 환경에서 검증된 신뢰성을 갖추고 있다. 한국의 대표적인 모바일 결제 서비스에서 10,000개 이상의 테스트를 지원하며 복잡한 비즈니스 요구사항을 처리하는 데 성공적으로 활용되고 있다 (README.md:191-220).
기술 스택 및 요구사항
| 구분 | 항목 | 버전/설명 |
|---|---|---|
| 언어 | Java (JDK) | 1.8 이상 |
| Kotlin | 1.8 이상 (Kotlin 지원 시) | |
| 프레임워크 | Jqwik | 1.7.3 |
| Kotest Property | 5.9.1 (Kotlin 지원 시) | |
| 라이선스 | Apache 2.0 | 오픈소스 |
| 빌드 도구 | Gradle / Maven | 둘 다 지원 |
| IDE 지원 | IntelliJ IDEA | FixtureMonkey Helper 플러그인 |
프로젝트 디렉토리 구조
fixture-monkey/
├── fixture-monkey/ # 핵심 라이브러리 모듈
│ └── src/main/java/com/navercorp/fixturemonkey/
│ ├── FixtureMonkey.java # 주요 진입점 클래스
│ ├── FixtureMonkeyBuilder.java # 빌더 패턴 구현
│ ├── ArbitraryBuilder.java # 객체 생성 인터페이스
│ ├── ArbitraryBuilders.java # 유틸리티 클래스
│ └── Constants.java # 상수 정의
├── docs/ # 문서화
│ └── docs/introduction/
│ └── overview.mdx # 개요 문서
├── fixture-monkey-benchmarks/ # 성능 벤치마크
│ └── fixture-monkey-benchmark/
│ └── src/jmh/java/com/navercorp/fixturemonkey/
├── README.md # 프로젝트 소개
└── LICENSE # Apache 2.0 라이선스
핵심 기능 및 특징
원라인 테스트 객체 생성
전통적인 테스트 객체 생성 방식에서는 수많은 setter 호출과 보일러플레이트 코드가 필요했다. Fixture Monkey는 이를 단일 라인으로 대체한다.
java1// Before: 수동 객체 생성 2Product product = new Product(); 3product.setId(1L); 4product.setName("Test Product"); 5// ... 많은 setter 호출 6 7// After: Fixture Monkey 사용 8Product product = fixtureMonkey.giveMeOne(Product.class);
직관적인 경로 기반 설정
경로 표현식을 통해 중첩된 필드에 직접 접근할 수 있다. items[*].product.name과 같은 표현식으로 리스트 내 모든 항목의 중첩 속성을 한 번에 설정할 수 있다.
java1ArbitraryBuilder<Order> orderBuilder = fixtureMonkey.giveMeBuilder(Order.class) 2 .set("items[*].product.name", "Special Product");
재사용 가능한 테스트 스펙
한 번 정의된 ArbitraryBuilder는 여러 테스트에서 재사용할 수 있다. 이를 통해 테스트 코드 중복을 제거하고 일관된 테스트 데이터를 유지할 수 있다.
java1ArbitraryBuilder<Product> productBuilder = fixtureMonkey.giveMeBuilder(Product.class) 2 .set("category", "Book") 3 .set("price", 1000); 4 5Product product1 = productBuilder.sample(); 6Product product2 = productBuilder.size("reviews", 3).sample();
범용 객체 생성
상속, 순환 참조, 복잡한 구조를 포함한 모든 종류의 객체 구조를 처리할 수 있다. 단순한 POJO부터 복잡한 객체 그래프까지 자동으로 생성한다.
java1Foo foo = FixtureMonkey.create().giveMeOne(Foo.class); // 순환 참조 포함 2Bar bar = FixtureMonkey.create().giveMeOne(Bar.class); // 상속 포함
동적 테스트 데이터
각 sample() 호출은 고유한 테스트 데이터를 생성한다. 정적인 테스트 데이터로는 발견하기 어려운 엣지 케이스를 찾을 수 있다.
java1Product sample1 = fixtureMonkey.giveMeBuilder(Product.class).sample(); 2Product sample2 = fixtureMonkey.giveMeBuilder(Product.class).sample(); 3assertThat(sample1).isNotEqualTo(sample2);
ArbitraryBuilder 인터페이스
ArbitraryBuilder는 Fixture Monkey의 핵심 인터페이스로, 테스트 객체 생성을 위한 다양한 메서드를 제공한다. set 메서드는 경로 표현식과 값을 받아 해당 속성을 설정한다.
java1ArbitraryBuilder<T> set(String expression, @Nullable Object value); 2ArbitraryBuilder<T> set(String expression, @Nullable Object value, int limit); 3ArbitraryBuilder<T> set(PropertySelector propertySelector, @Nullable Object value);
(fixture-monkey/src/main/java/com/navercorp/fixturemonkey/ArbitraryBuilder.java:53-92)
아키텍처 및 주요 컴포넌트
시스템 아키텍처
正在加载图表渲染器...
아키텍처 설명:
- 사용자 코드 레이어: 테스트 코드에서
FixtureMonkey.create()또는FixtureMonkey.builder()를 통해 인스턴스를 생성한다. - API 레이어:
giveMeBuilder(),sample()등의 메서드를 통해 객체 생성을 요청한다. - 핵심 엔진:
FixtureMonkey,ArbitraryBuilder등이 실제 객체 생성 로직을 처리한다. - 설정 및 상수:
FixtureMonkeyOptions과Constants가 기본 동작을 정의한다. - 플러그인 생태계: Kotlin 지원, IntelliJ 플러그인 등 확장 기능을 제공한다.
(fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java:62-133, fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java:75-134)
FixtureMonkey 핵심 클래스
FixtureMonkey는 라이브러리의 주요 진입점 클래스로, 다음과 같은 핵심 컴포넌트를 포함한다:
- fixtureMonkeyOptions: 객체 생성 동작을 제어하는 옵션들
- manipulatorOptimizer: 조작자 최적화기
- monkeyContext: 컨텍스트 정보 관리
- monkeyManipulatorFactory: 조작자 팩토리
- monkeyExpressionFactory: 표현식 팩토리
(fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java:62-133)
FixtureMonkeyBuilder 빌더 패턴
FixtureMonkeyBuilder는 빌더 패턴을 구현하여 FixtureMonkey 인스턴스를 유연하게 구성할 수 있게 한다. 주요 설정 가능 항목:
- PropertyGenerator: 속성 생성기 등록
- ObjectPropertyGenerator: 객체 속성 생성기 설정
- ManipulatorOptimizer: 조작자 최적화기 지정
- MonkeyExpressionFactory: 표현식 팩토리 구성
java1FixtureMonkey sut = FixtureMonkey.builder() 2 .objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE) 3 .build();
(fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java:75-134)
Constants 상수 정의
Constants 클래스는 라이브러리 전반에서 사용되는 기본값들을 정의한다:
| 상수명 | 값 | 설명 |
|---|---|---|
| DEFAULT_ELEMENT_MIN_SIZE | 0 | 컬렉션 최소 크기 |
| DEFAULT_ELEMENT_MAX_SIZE | 3 | 컬렉션 최대 크기 |
| NO_OR_ALL_INDEX_INTEGER_VALUE | Integer.MAX_VALUE | 인덱스 없음/전체 표시 |
| MAX_MANIPULATION_COUNT | Integer.MAX_VALUE | 최대 조작 횟수 |
| ALL_INDEX_STRING | "*" | 모든 인덱스 표현 |
| HEAD_NAME | "$" | 헤드 이름 |
(fixture-monkey/src/main/java/com/navercorp/fixturemonkey/Constants.java:20-28)
객체 생성 호출 흐름
正在加载图表渲染器...
호출 흐름 설명:
- 빌더 생성:
FixtureMonkey.builder()를 통해 빌더 인스턴스를 생성한다. - 옵션 설정:
objectIntrospector()등의 메서드로 동작을 커스터마이징한다. - 인스턴스 생성:
build()를 호출해FixtureMonkey인스턴스를 생성한다. - ArbitraryBuilder 획득:
giveMeBuilder()로 특정 타입의 빌더를 얻는다. - 조작 설정:
set()등의 메서드로 속성 값을 지정한다. - 객체 생성:
sample()을 호출해 최종 객체를 생성한다.
(fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkey.java:62-133, fixture-monkey/src/main/java/com/navercorp/fixturemonkey/FixtureMonkeyBuilder.java:75-134)
사용 예시 및 시작하기
의존성 추가
Gradle (Java):
groovy1testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter:1.1.18")
Gradle (Kotlin):
groovy1testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter-kotlin:1.1.18")
Maven (Java):
xml1<dependency> 2 <groupId>com.navercorp.fixturemonkey</groupId> 3 <artifactId>fixture-monkey-starter</artifactId> 4 <version>1.1.18</version> 5 <scope>test</scope> 6</dependency>
기본 사용법
Java 예시:
java1// 가장 간단한 사용법 2FixtureMonkey fixtureMonkey = FixtureMonkey.create(); 3Product product = fixtureMonkey.giveMeOne(Product.class); 4 5// 빌더 패턴을 통한 커스터마이징 6@Test 7void sampleOrder() { 8 FixtureMonkey sut = FixtureMonkey.builder() 9 .objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE) 10 .build(); 11 12 Order actual = sut.giveMeBuilder(Order.class) 13 .set(javaGetter(Order::getOrderNo), "1") 14 .set(javaGetter(Order::getProductName), "Line Sally") 15 .minSize(javaGetter(Order::getItems), 1) 16 .sample(); 17 18 then(actual.getOrderNo()).isEqualTo("1"); 19 then(actual.getProductName()).isEqualTo("Line Sally"); 20 then(actual.getItems()).hasSizeGreaterThanOrEqualTo(1); 21}
Kotlin 예시:
kotlin1@Test 2fun sampleOrder() { 3 val sut = FixtureMonkey.builder() 4 .plugin(KotlinPlugin()) 5 .build() 6 7 val actual = sut.giveMeBuilder<Order>() 8 .setExp(Order::orderNo, "1") 9 .setExp(Order::productName, "Line Sally") 10 .minSizeExp(Order::items, 1) 11 .sample() 12 13 then(actual.orderNo).isEqualTo("1") 14 then(actual.productName).isEqualTo("Line Sally") 15 then(actual.items).hasSizeGreaterThanOrEqualTo(1) 16}
실제 테스트 시나리오
java1@Test 2void testOrderProcessing() { 3 // Given 4 Order order = fixtureMonkey.giveMeBuilder(Order.class) 5 .set("items[*].quantity", 2) 6 .set("items[*].product.price", 1000) 7 .sample(); 8 9 // When 10 OrderResult result = new OrderProcessor().process(order); 11 12 // Then 13 assertThat(result.getTotalAmount()).isEqualTo(4000); // 2 items * 2 quantity * 1000 price 14 assertThat(result.getStatus()).isEqualTo(OrderStatus.COMPLETED); 15}
ArbitraryBuilders 유틸리티
ArbitraryBuilders 클래스는 여러 ArbitraryBuilder를 조합하는 유틸리티 메서드를 제공한다. zip 메서드를 통해 여러 빌더의 결과를 결합할 수 있다.
java1// 두 ArbitraryBuilder 조합 2public static <T, U, R> ArbitraryBuilder<R> zip( 3 ArbitraryBuilder<T> a1, 4 ArbitraryBuilder<U> a2, 5 BiFunction<T, U, R> combinator 6) 7 8// 리스트로 조합 9public static <R> ArbitraryBuilder<R> zip( 10 List<ArbitraryBuilder<?>> list, 11 Function<List<?>, R> combinator 12)
(fixture-monkey/src/main/java/com/navercorp/fixturemonkey/ArbitraryBuilders.java:28-70)
적용 시나리오
단위 테스트 객체 생성
복잡한 도메인 객체를 테스트하기 위해 수동으로 setter를 호출하는 대신, Fixture Monkey를 사용하여 자동으로 생성할 수 있다. 특히 생성자나 빌더 패턴이 복잡한 경우에 유용하다.
프로퍼티 기반 테스트
Jqwik과 통합하여 프로퍼티 기반 테스트를 수행할 수 있다. 각 테스트 실행마다 다른 데이터가 생성되어 엣지 케이스를 발견하기 쉽다.
통합 테스트 데이터 준비
데이터베이스나 외부 API와의 통합 테스트에서 필요한 테스트 데이터를 쉽게 생성할 수 있다. 경로 기반 설정을 통해 연관된 객체들의 속성도 일관되게 설정할 수 있다.
테스트 코드 리팩토링
기존의 중복된 테스트 픽스처 생성 코드를 ArbitraryBuilder로 추출하여 재사용할 수 있다. 테스트 코드의 가독성과 유지보수성이 향상된다.
보고서 읽기 가이드
正在加载图表渲染器...
추천 읽기 순서:
- 프로젝트 개요 (현재 문서): Fixture Monkey의 전반적인 소개와 핵심 개념 파악
- 핵심 기능 심화: 경로 표현식, 조작자, 제약 조건 등 상세 기능 학습
- 아키텍처 상세: 내부 구현 원리와 확장 포인트 이해
- API 레퍼런스: 각 클래스와 메서드의 상세 스펙 확인
- 실전 예제: 다양한 사용 시나리오와 모범 사례 학습
- 플러그인 확장: Kotlin, Jackson, Jakarta Bean Validation 등 플러그인 활용
- 성능 최적화: 대규모 테스트 환경에서의 성능 튜닝
프로젝트 핵심 능력 지표
| 지표 | 수치/내용 |
|---|---|
| 지원 언어 | 2개 (Java, Kotlin) |
| 최소 JDK 버전 | 1.8 |
| 핵심 모듈 수 | 4개 이상 (core, benchmarks, plugins 등) |
| API 진입점 | 2개 (FixtureMonkey.create, FixtureMonkey.builder) |
| 핵심 인터페이스 | ArbitraryBuilder (set, sample, size 등) |
| 기본 컬렉션 크기 범위 | 0-3 (설정 가능) |
| 운영 검증 | 10,000개 이상의 테스트 지원 (Naver 결제 서비스) |
| 플러그인 생태계 | Kotlin, IntelliJ Helper 등 |
| 문서화 언어 | 2개 (English, Korean) |
(README.md:1-220, fixture-monkey/src/main/java/com/navercorp/fixturemonkey/Constants.java:20-28)
