본문으로 건너뛰기
버전: v1.0.x

커스터마이징 옵션

Fixture Monkey는 FixtureMonkeyBuilder 를 통해 원하는 값을 가지도록 객체를 사용자 정의하거나 사용자 정의 프로퍼티 명을 사용할 수 있는 옵션도 제공합니다.

프로퍼티명 참조 방법 변경하기

defaultPropertyNameResolver, pushPropertyNameResolver, pushAssignableTypePropertyNameResolver, pushExactTypePropertyNameResolver

PropertyNameResolver 관련 옵션을 사용하면 프로퍼티명을 참조하는 방법을 사용자 정의할 수 있습니다.

defaultPropertyNameResolver 옵션은 모든 타입에 대해 프로퍼티명을 알아내는 방식을 변경하는 데 사용됩니다. 만약 특정 타입에 대해 변경을 수행하려면 pushPropertyNameResolver , pushAssignableTypePropertyNameResolver 또는 pushExactTypePropertyNameResolver 를 사용할 수 있습니다.

기본적으로 프로퍼티는 원래 이름으로 참조됩니다. 다음 예시를 통해 프로퍼티명을 사용자 정의하는 방법을 살펴봅시다:

@Data // getter, setter
public class Product {
String productName;
}

@Test
void test() {
// given
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.pushPropertyNameResolver(MatcherOperator.exactTypeMatchOperator(String.class, (property) -> "string"))
.build();
String expected = "test";

// when
String actual = fixtureMonkey.giveMeBuilder(Product.class)
.set("string", expected)
.sample()
.getProductName();

// then
then(actual).isEqualTo(expected);
}

일반적으로, 프로퍼티 명은 기존 프로퍼티 명인 "productName" 으로 해석됩니다. 그러나 pushPropertyNameResolver 를 사용하면 String 타입의 프로퍼티는 이제 "string"이라는 이름으로 참조됩니다.

특정 타입에 기본 ArbitraryBuilder 등록하기

register, registerGroup, registerExactType, registerAssignableType

때로는 클래스가 특정 제약 조건을 항상 지켜야할 수 있습니다. 사용자 정의 API를 사용해 항상 ArbitraryBuilder 를 수정해야 한다면 번거로울 수 있고 코드가 길어질 수 있습니다. 이 경우, 기본 제약 조건을 충족하는 클래스들에 대해서 기본 ArbitraryBuilder 를 설정할 수 있습니다.

register 옵션은 특정 타입에 대한 ArbitraryBuilder 를 등록하는 것을 돕습니다.

예시의 다음 코드는 Product 클래스에 대한 ArbitraryBuilder 를 등록하는 방법을 보여줍니다. 이를 등록함으로써 FixtureMonkey 에 의해 생성된 모든 Product 인스턴스는 "0"보다 크거나 같은 id 값을 가질 것입니다.

FixtureMonkey.builder()
.register(
Product.class,
fixture -> fixture.giveMeBuilder(Product.class)
.set("id", Arbitraries.longs().greaterOrEqual(0))
)
.build();

ArbitraryBuilders들을 한 번에 등록하려면 registerGroup 옵션을 사용할 수 있습니다. 이 작업은 리플렉션 또는 ArbitraryBuilderGroup 인터페이스를 사용하여 수행할 수 있습니다.

리플렉션 사용:

public class GenerateGroup {
public ArbitraryBuilder<GenerateString> generateString(FixtureMonkey fixtureMonkey) {
return fixtureMonkey.giveMeBuilder(GenerateString.class)
.set("value", Arbitraries.strings().numeric());
}

public ArbitraryBuilder<GenerateInt> generateInt(FixtureMonkey fixtureMonkey) {
return fixtureMonkey.giveMeBuilder(GenerateInt.class)
.set("value", Arbitraries.integers().between(1, 100));
}
}

FixtureMonkey.builder()
.registerGroup(GenerateGroup.class)
.build();

ArbitraryBuilderGroup 인터페이스 사용:

public class GenerateBuilderGroup implements ArbitraryBuilderGroup {
@Override
public ArbitraryBuilderCandidateList generateCandidateList() {
return ArbitraryBuilderCandidateList.create()
.add(
ArbitraryBuilderCandidateFactory.of(GenerateString.class)
.builder(
arbitraryBuilder -> arbitraryBuilder
.set("value", Arbitraries.strings().numeric())
)
)
.add(
ArbitraryBuilderCandidateFactory.of(GenerateInt.class)
.builder(
builder -> builder
.set("value", Arbitraries.integers().between(1, 100))
)
);
}
}

FixtureMonkey.builder()
.registerGroup(new GenerateBuilderGroup())
.build();

표현식 엄격 모드 사용하기

useExpressionStrictMode

표현식(특히 문자열 표현식)을 사용할 때 작성한 표현식이 일치하는 프로퍼티를 가지는지, 프로퍼티가 올바르게 선택되었는지를 파악하기 어려울 수 있습니다. useExpressionStrictMode 옵션을 사용하면 작성한 표현식이 일치하는 프로퍼티를 가지고 있지 않으면 IllegalArgumentException 예외를 던집니다.

@Test
void test() {
FixtureMonkey fixtureMonkey = FixtureMonkey.builder().useExpressionStrictMode().build();

thenThrownBy(
() -> fixtureMonkey.giveMeBuilder(String.class)
.set("nonExistentField", 0)
.sample()
).isExactlyInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("No matching results for given NodeResolvers.");
}

Java 기본 타입 제약 추가하기

javaTypeArbitraryGenerator, javaTimeTypeArbitraryGenerator

사용자 정의 JavaTypeArbitraryGenerator 인터페이스를 구현하여 Java 기본 타입(string, integer, double 등)의 기본값을 수정할 수 있습니다. 이 옵션은 JqwikPlugin 을 통해 적용할 수 있습니다.

예를 들어, Fixture Monkey로 생성된 string 타입은 기본적으로 제어 블록이 문자열에 포함되어 있는 극단적인 경우를 고려하여 생성하기 때문에 읽기 어렵습니다.

알파벳 문자로만 구성된 문자열을 생성하려면 아래 예시처럼 JavaTypeArbitraryGenerator 를 재정의하면 됩니다:

FixtureMonkey.builder()
.plugin(
new JqwikPlugin()
.javaTypeArbitraryGenerator(new JavaTypeArbitraryGenerator() {
@Override
public StringArbitrary strings() {
return Arbitraries.strings().alpha();
}
})
)
.build();

Java time 타입의 경우, javaTimeTypeArbitraryGenerator 를 사용할 수 있습니다.

어노테이션을 사용해 Java 기본 타입 제약 추가하기

javaArbitraryResolver, javaTimeArbitraryResolver

javax-validation 플러그인을 사용하여 Java 타입 프로퍼티에 제약 조건을 추가하는 것과 유사하게, 어노테이션을 사용하여 Java 타입에 제약 조건을 적용할 수 있습니다. 이는 JavaArbitraryResolver 인터페이스를 구현하면 됩니다. 이 옵션은 JqwikPlugin 을 통해 적용할 수 있습니다.

예를 들어, 프로퍼티의 길이를 최대 10자로 제한해야 한다는 의미의 MaxLengthOf10 이라는 사용자 지정 어노테이션이 있는 경우 아래와 같이 JavaArbitraryResolver 를 생성할 수 있습니다:

FixtureMonkey.builder()
.plugin(
new JqwikPlugin()
.javaArbitraryResolver(new JavaArbitraryResolver() {
@Override
public Arbitrary<String> strings(StringArbitrary stringArbitrary, ArbitraryGeneratorContext context) {
if (context.findAnnotation(MaxLengthof10.class).isPresent()) {
return stringArbitrary.ofMaxLength(10);
}
return stringArbitrary;
}
})
)
.build();

Nullability 변경하기

defaultNotNull

defaultNotNull, nullableContainer, nullableElement

인스턴스의 프로퍼티가 null이 아닌지 확인하려는 경우 아래에 언급된 옵션을 활용할 수 있습니다.

  • defaultNotNull null 프로퍼티의 생성 여부를 결정합니다. true라면 프로퍼티가 null이 될 수 없습니다.
  • nullableContainer 컨테이너 프로퍼티가 null이 될 수 있는지 여부를 결정합니다. true라면 컨테이너가 null이 될 수 있습니다.
  • nullableElement 컨테이너 프로퍼티 내의 요소가 null이 될 수 있는지 여부를 결정합니다. true라면 요소가 null이 될 수 있습니다.

기본적으로 이 세 옵션은 false로 설정되어 있습니다. 필요에 따라 true로 변경할 수 있습니다.

위험

These options only apply to properties that do not have a nullable marker, such as @Nullable in Java or ? in Kotlin.

FixtureMonkey fixtureMonkey = FixtureMonkey.builder().defaultNotNull(true).build();

FixtureMonkey fixtureMonkey = FixtureMonkey.builder().nullableContainer(true).build();

FixtureMonkey fixtureMonkey = FixtureMonkey.builder().nullableElement(true).build();

NullInjectGenerator

defaultNullInjectGenerator, pushNullInjectGenerator, pushExactTypeNullInjectGenerator, pushAssignableTypeNullInjectGenerator

특정 프로퍼티가 nullable 표시와 관계없이 null이어야 하는 경우, NullInjectGenerator 와 관련된 옵션을 사용할 수 있습니다.

defaultnullInjectGenerator 옵션을 사용하면 프로퍼티가 null이 될 확률을 설정할 수 있습니다.

기본적으로 프로퍼티가 null일 확률은 20%로 설정되어 있습니다.

항상 null이 되길 원한다면 1.0d 로 설정할 수 있습니다. DefaultNullInjectGenerator에는 NOT_NULL_INJECT(0.0d)ALWAYS_NULL_INJECT(1.0d) 와 같은 미리 정의된 값이 있습니다. 이를 가져와서 사용할 수 있습니다.

사용자 정의된 동작을 더 추가하길 원하는 경우, 자체적으로 NullInjectGenerator를 구현할 수도 있습니다.

FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.defaultNullInjectGenerator((context) -> NOT_NULL_INJECT) // NOT_NULL_INJECT를 사용하거나 확률을 0.4로 설정할 수 있습니다.
.build()

특정 타입이 null이 될 확률을 구체적으로 변경하려면 pushNullInjectGenerator 를 사용하면 됩니다.

FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.pushNullInjectGenerator(MatcherOperator.exactTypeMatchOperator(SimpleObject.class, (context) -> NOT_NULL_INJECT))
.build();

특정 클래스의 ArbitraryBuilderregister 로 등록하면 .setNotNull("*") 설정과 동일한 결과를 얻을 수 있습니다.