InnerSpec

InnerSpec은 적용하려는 커스터마이징에 대한 타입 독립적인 명세입니다. ArbitraryBuilder 내의 setInner() 메서드를 사용하면 InnerSpec 인스턴스에 정의된 명세를 빌더에 적용할 수 있습니다.

InnerSpec 에는 커스터마이징 세부 정보가 저장되며 여러 ArbitraryBuilder에서 재사용할 수 있습니다. ArbitraryBuilder에서 픽스처 몽키 표현식을 사용하는 방식과 달리 InnerSpec은 중첩된 구조를 사용해 프로퍼티에 접근이 가능합니다.

InnerSpec 의 또 다른 장점은 일반적인 표현식과 달리 맵 프로퍼티를 커스터마이징할 수 있다는 점입니다.

ArbitraryBuilder 에 InnerSpec 적용하기

빌더에 미리 정의된 InnerSpec 을 적용하려면 다음과 같이 setInner() 메서드를 사용하세요.

InnerSpec innerSpec = new InnerSpec().property("id", 1000);

fixtureMonkey.giveMeBuilder(Product.class)
    .setInner(innerSpec);
val innerSpec = InnerSpec().property("id", 1000)

fixtureMonkey.giveMeBuilder<Product>()
    .setInner(innerSpec)

프로퍼티 커스터마이징하기

property()

ArbitraryBuilder 의 set() 메서드와 유사하게, 프로퍼티 이름과 원하는 값을 지정하여 프로퍼티를 커스터마이징할 수 있습니다.

InnerSpec innerSpec = new InnerSpec()
    .property("id", 1000);
val innerSpec = InnerSpec()
    .property("id", 1000)

size(), minSize(), maxSize()

size(), minSize(), 그리고 maxSize() 는 프로퍼티의 크기를 지정하는 데 사용할 수 있습니다.

앞서 언급했듯이, InnerSpec 은 중첩된 방식으로 명세을 정의합니다. property() 를 사용하여 컨테이너 프로퍼티를 먼저 선택한 다음, 내부에 정의된 innerSpec 컨슈머를 사용하여 크기를 설정할 수 있습니다.

InnerSpec innerSpec = new InnerSpec()
    .property("options", options -> options.size(5)); // size:5

InnerSpec innerSpec = new InnerSpec()
    .property("options", options -> options.size(3, 5)); // minSize:3, maxSize:5

InnerSpec innerSpec = new InnerSpec()
    .property("options", options -> options.minSize(3)); // minSize:3

InnerSpec innerSpec = new InnerSpec()
    .property("options", options -> options.maxSize(5)); // maxSize:5
val innerSpec = InnerSpec()
    .property("options") { it.size(5) } // size:5

val innerSpec = InnerSpec()
    .property("options") { it.size(3, 5) } // minSize:3, maxSize:5

val innerSpec = InnerSpec()
    .property("options") { it.minSize(3) } // minSize:3

val innerSpec = InnerSpec()
    .property("options") { it.maxSize(5) } // maxSize:5

postCondition()

postCondition() 은 프로퍼티가 특정 조건을 만족해야 하는 경우 사용할 수 있습니다.

InnerSpec innerSpec = new InnerSpec()
    .property("id", id -> id.postCondition(Long.class, it -> it > 0));
val innerSpec = InnerSpec()
    .property("id") { it.postCondition(Long::class.java) { it > 0 }}

inner()

또한 inner() 를 사용하여 미리 정의된 InnerSpec 을 사용하여 프로퍼티를 커스터마이징할 수 있습니다.

InnerSpec innerSpec = new InnerSpec()
    .property("id", 1000L);

fixtureMonkey.giveMeBuilder(Product.class)
    .setInner(
        new InnerSpec()
            .property("nestedObject", nestedObject -> nestedObject.inner(innerSpec))
    );
val innerSpec = InnerSpec()
    .property("id", 1000L)

fixtureMonkey.giveMeBuilder<Product>()
    .setInner(
        InnerSpec()
            .property("nestedObject") { it.inner(innerSpec) }
    )

리스트 커스터마이징하기

listElement()

목록 내의 개별 요소는 listElement()를 사용하여 선택할 수 있습니다. 이는 픽스처 몽키 표현식을 사용하여 “[n]“으로 요소를 참조하는 것과 동일합니다.

InnerSpec innerSpec = new InnerSpec()
    .property("options", options -> options.listElement(0, "red"));
val innerSpec = InnerSpec()
    .property("options") { it.listElement(0, "red") }

allListElement()

만약 목록의 모든 요소를 동시에 설정하려면 allListElement()를 사용할 수 있습니다. 이는 픽스처 몽키 표현식을 사용하여 “[*]“로 요소를 참조하는 것과 동일합니다.

InnerSpec innerSpec = new InnerSpec()
    .property("options", options -> options.allListElement("red"));
val innerSpec = InnerSpec()
    .property("options") { it.allListElement("red") }

맵 커스터마이징하기

InnerSpec은 맵 프로퍼티 엔트리를 커스터마이징하기 위해 특별한 메소드를 제공합니다.

key(), value(), entry()

key(), value(), entry() 메소드를 사용하여 맵 프로퍼티 엔트리를 커스터마이징할 수 있습니다. key()를 사용하면 맵 엔트리의 키에 지정된 값을 할당하고, 엔트리의 값은 무작위로 설정됩니다. 마찬가지로, value()를 사용하면 맵 엔트리의 값에 지정된 값을 할당하고, 키는 무작위로 설정됩니다. 키와 값을 동시에 지정하려면 entry()를 사용할 수 있습니다.

InnerSpec innerSpec = new InnerSpec()
    .property("merchantInfo", merchantInfo -> merchantInfo.key(1000));

InnerSpec innerSpec = new InnerSpec()
    .property("merchantInfo", merchantInfo -> merchantInfo.value("ABC Store"));

InnerSpec innerSpec = new InnerSpec()
    .property("merchantInfo", merchantInfo -> merchantInfo.entry(1000, "ABC Store"));
val innerSpec = InnerSpec()
    .property("merchantInfo") { it.key(1000) }

val innerSpec = InnerSpec()
    .property("merchantInfo") { it.value("ABC Store") }

val innerSpec = InnerSpec()
    .property("merchantInfo") { it.entry(1000, "ABC Store") }

keys(), values(), entries()

맵 내의 여러 개의 엔트리를 설정할 때 keys(), values(), entries()를 사용하여 여러 값을 전달할 수 있습니다.

InnerSpec innerSpec = new InnerSpec()
    .property("merchantInfo", merchantInfo -> merchantInfo.keys(1000, 1001, 1002));

InnerSpec innerSpec = new InnerSpec()
    .property("merchantInfo", merchantInfo -> merchantInfo.values("ABC Store", "123 Convenience", "XYZ Mart"));

InnerSpec innerSpec = new InnerSpec()
    .property("merchantInfo", merchantInfo -> merchantInfo.entries(1000, "ABC Store", 1001, "123 Convenience", 1002, "XYZ Mart"));
val innerSpec = InnerSpec()
    .property("merchantInfo") { it.keys(1000, 1001, 1002) }

val innerSpec = InnerSpec()
    .property("merchantInfo") { it.values("ABC Store", "123 Convenience", "XYZ Mart") }

val innerSpec = InnerSpec()
    .property("merchantInfo") { it.entries(1000, "ABC Store", 1001, "123 Convenience", 1002, "XYZ Mart") }

allKey(), allValue(), allEntry()

allListElement()와 유사하게, allKey(), allValue(), allEntry()를 사용하여 맵 내의 모든 엔트리를 지정된 값으로 설정할 수 있습니다.

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"));
val innerSpec = InnerSpec()
    .property("merchantInfo") { it.allKey(1000) }

val innerSpec = InnerSpec()
    .property("merchantInfo") { it.allValue("ABC Store") }

val innerSpec = InnerSpec()
    .property("merchantInfo") { it.allEntry(1000, "ABC Store") }

keyLazy(), valueLazy(), entryLazy()

ArbitraryBuilder의 setLazy() 메소드와 유사하게, Supplier를 전달하여 값을 할당할 수 있습니다. Supplier는 InnerSpec이 적용된 ArbitraryBuilder가 샘플링될 때마다 실행됩니다.

InnerSpec innerSpec = new InnerSpec()
    .property("merchantInfo", merchantInfo -> merchantInfo.keyLazy(this::generateMerchantKey));

InnerSpec innerSpec = new InnerSpec()
    .property("merchantInfo", merchantInfo -> merchantInfo.valueLazy(this::generateMerchantValue));

InnerSpec innerSpec = new InnerSpec()
    .property("merchantInfo", merchantInfo -> merchantInfo.entryLazy(this::generateMerchantKey, this::generateMerchantValue));
val innerSpec = InnerSpec()
    .property("merchantInfo") { it.keyLazy(this::generateMerchantKey) }

val innerSpec = InnerSpec()
    .property("merchantInfo") { it.valueLazy(this::generateMerchantValue) }

val innerSpec = InnerSpec()
    .property("merchantInfo") { it.entryLazy(this::generateMerchantKey, this::generateMerchantValue) }

allKeyLazy(), allValueLazy(), allEntryLazy()

allKey() 메소드와 마찬가지로, allKeyLazy()를 사용하여 맵 내의 모든 엔트리에 keyLazy()를 적용할 수 있습니다. allValueLazy()allEntryLazy()도 유사하게 작동합니다.

InnerSpec innerSpec = new InnerSpec()
    .property("merchantInfo", merchantInfo -> merchantInfo.allKeyLazy(this::generateMerchantKey));

InnerSpec innerSpec = new InnerSpec()
    .property("merchantInfo", merchantInfo -> merchantInfo.allValueLazy(this::generateMerchantValue));

InnerSpec innerSpec = new InnerSpec()
    .property("merchantInfo", merchantInfo -> merchantInfo.allEntryLazy(this::generateMerchantKey, this::generateMerchantValue));
val innerSpec = InnerSpec()
    .property("merchantInfo") { it.allKeyLazy(this::generateMerchantKey) }

val innerSpec = InnerSpec()
    .property("merchantInfo") { it.allValueLazy(this::generateMerchantValue) }

val innerSpec = InnerSpec()
    .property("merchantInfo") { it.allEntryLazy(this::generateMerchantKey, this::generateMerchantValue) }

중첩된 맵 커스터마이징하기

메서드를 조합하여 InnerSpec 내에서 맵의 키, 값 또는 둘 다를 효과적으로 커스터마이징할 수 있습니다.

다음과 같이 중첩된 맵 구조의 시나리오를 고려해보겠습니다.

public class Example {
    Map<Map<String, String>, String> mapByString;
    Map<String, Map<String, String>> stringByMap;
}

맵 타입의 키 설정

맵 타입의 키를 설정하려면 key()를 사용하여 맵 키에 접근한 다음, 해당 키를 추가로 커스터마이징할 수 있습니다.

InnerSpec().property("mapByString", m -> m.key(k -> k.entry("key", "value")));
InnerSpec().property("mapByString") { m -> m.key { k -> k.entry("key", "value") } }

만약 엔트리 자체를 설정해야 하는 경우, entry()로 엔트리에 접근하고 InnerSpec을 사용하여 키를 추가로 커스터마이징한 다음, 특정 값을 설정합니다.

InnerSpec().property("mapByString", m -> m.entry(k -> k.entry("innerKey", "innerValue")), "value")
InnerSpec().property("mapByString") { m -> m.entry({ k -> k.entry("innerKey", "innerValue") }, "value") }

맵 타입의 값 설정

map 타입의 값이 있는 맵의 경우, value()를 사용하여 맵 값을 접근한 다음, 해당 값을 추가로 커스터마이징할 수 있습니다.

InnerSpec().property("stringByMap", m -> m.value(v -> v.entry("key", "value")))
InnerSpec().property("stringByMap") { m -> m.value { v -> v.entry("key", "value") } }

만약 엔트리 자체를 설정해야 하는 경우, entry()로 엔트리에 접근하고 InnerSpec을 사용하여 키를 추가로 커스터마이징한 다음, 특정 값을 설정합니다.

InnerSpec().property("stringByMap", m -> m.entry("key", v -> v.entry("innerKey", "innerValue")))
InnerSpec().property("stringByMap") { m -> m.entry("key") {v -> v.entry("innerKey", "innerValue")} }