InnerSpec
An InnerSpec is a type-independent specification for the customizations you wish to apply.
Using the setInner() method within ArbitraryBuilder, you can apply customizations defined within an InnerSpec instance into your builder.
InnerSpec holds customization details and can be reused on ArbitraryBuilders.
Unlike using expressions used in ArbitraryBuilder, InnerSpec enables a more nested and structured approach.
An added advantage of InnerSpec is its ability to customize map properties, unlike normal expressions.
Kotlin EXP is not supported for InnerSpec, as it is designed to be type-independent. Instead, you need to specify the property by its name.
Applying InnerSpec to the ArbitraryBuilder
To apply your pre-defined InnerSpec to the builder, use the setInner() method as shown below:
- Java
- Kotlin
InnerSpec innerSpec = new InnerSpec().property("id", 1000);
fixtureMonkey.giveMeBuilder(Product.class)
.setInner(innerSpec);
val innerSpec = InnerSpec().property("id", 1000)
fixtureMonkey.giveMeBuilder<Product>()
.setInner(innerSpec)
Customizing properties
property()
Similar to the set() method in ArbitraryBuilder, you can customize a property by specifying its name and providing the desired value.
Fixture Monkey expressions such as refering elements ([]) or nested fields(.) are not allowed as the property name. Only the property name itself is allowed.
- Java
- Kotlin
InnerSpec innerSpec = new InnerSpec()
.property("id", 1000);
val innerSpec = InnerSpec()
.property("id", 1000)
size(), minSize(), maxSize()
size(), minSize(), and maxSize() can be used to specify the size of the property.
As previously mentioned, InnerSpec defines customizations in a nested manner.
You can first select the container property using property() and then proceed to define an innerSpec consumer to set the size.
- Java
- Kotlin
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() can be used when you require your property to match a specific condition.
Using setPostCondition can incur higher costs for narrow conditions. In such cases, it's recommended to use set instead.
- Java
- Kotlin
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()
You can also customize a property using another pre-defined InnerSpec with the help of inner().
- Java
- Kotlin
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) }
)
Customizing list properties
listElement()
Individual elements within lists can be selected using listElement().
This is equivalent to referencing elements with "[n]" using expressions.
- Java
- Kotlin
InnerSpec innerSpec = new InnerSpec()
.property("options", options -> options.listElement(0, "red"));
val innerSpec = InnerSpec()
.property("options") { it.listElement(0, "red") }
allListElement()
If you wish to set all elements of the list simultaneously, you can use allListElement().
This is equivalent to referencing elements with "[*]" using expressions.
- Java
- Kotlin
InnerSpec innerSpec = new InnerSpec()
.property("options", options -> options.allListElement("red"));
val innerSpec = InnerSpec()
.property("options") { it.allListElement("red") }
Customizing map properties
InnerSpec provides special methods for customizing map property entries.
Similar to lists, setting a map entry without specifying the size first might lead to no change. Prior to setting a value, ensure that the map property has the intended size.
key(), value(), entry()
You can customize map property entries using key(), value(), and entry() methods.
Using key() assigns a specified value to the key of a map entry, while the entry's value remains randomized.
Similarly, value() assigns a specified value to the map entry's value, while the key becomes randomized.
If you want to specify both the key and value at once, you can use entry().
- Java
- Kotlin
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()
When setting multiple entries within a map, you can use keys(), values(), and entries() to pass multiple values.
- Java
- Kotlin
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()
Similar to allListElement(), it is possible to set every entry within the map to the specified value with allKey(), allValue(), and allEntry().
- Java
- Kotlin
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()
Similar to the setLazy() method in ArbitraryBuilder, you can pass a Supplier to assign the value.
The Supplier will run every time the ArbitraryBuilder with the InnerSpec applied is sampled.
- Java
- Kotlin
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()
Just as with the allKey() method, you can use allKeyLazy() to apply keyLazy() to every entry within the map.
Both allValueLazy() and allEntryLazy() function similarly.
- Java
- Kotlin
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) }
Customizing nested Maps
By combining methods within InnerSpec, you can effectively customize maps with map-type keys, map-type values, or both.
Consider the scenario of a nested map structure like the following:
public class Example {
Map<Map<String, String>, String> mapByString;
Map<String, Map<String, String>> stringByMap;
}
Setting map-type key
To set a map with a map-type key, you can access the map key using key(), and then further customize it.
- general expression
- Kotlin Exp
InnerSpec().property("mapByString", m -> m.key(k -> k.entry("key", "value")));
InnerSpec().property("mapByString") { m -> m.key { k -> k.entry("key", "value") } }
If you need to set the entry itself, access the entry with entry() and further customize the key using InnerSpec, then set the specific value.
- general expression
- Kotlin Exp
InnerSpec().property("mapByString", m -> m.entry(k -> k.entry("innerKey", "innerValue")), "value")
InnerSpec().property("mapByString") { m -> m.entry({ k -> k.entry("innerKey", "innerValue") }, "value") }
Setting map-type value
For a map with a map-type value, access the map value using value(), and then further customize it.
- general expression
- Kotlin Exp
InnerSpec().property("stringByMap", m -> m.value(v -> v.entry("key", "value")))
InnerSpec().property("stringByMap") { m -> m.value { v -> v.entry("key", "value") } }
If you need to set the entry itself, access the entry with entry() and further customize the value using InnerSpec, then set the specific key.
- general expression
- Kotlin Exp
InnerSpec().property("stringByMap", m -> m.entry("key", v -> v.entry("innerKey", "innerValue")))
InnerSpec().property("stringByMap") { m -> m.entry("key") {v -> v.entry("innerKey", "innerValue")} }