Best practice for delegating configurations?
941design opened this issue · 3 comments
Hi,
How to properly delegate a generator configuration?
Assume MyObject
has two attributes of the same type whose generators need to be configured. Let's say foo
and bar
are both integers, and I want to configure them with different ranges. I created a configuration annotation with fields fooMin
, fooMax
, barMin
, and barMax
, and a generator MyObjectGen
that can be configured with MyObjectConf
. In configure
I want to delegate to sub-generators. What is the proper way to do this?
My solution works but does not feel right, because I also hat to create an implementation of @InRange
in order to configure the inner generators. I know I could configure the inner generators with annotations, but that does not seem to be practical either (when I have a lot of test methods each requiring a unique configuration).
On the other hand, I like the usage of my implementation, because how the parameter is configured matches that of the built-in generated parameters.
What am I missing?
Thanks a lot!
@RunWith(JUnitQuickcheck.class)
public class MyObjectPropertiesTest {
@Target({PARAMETER, FIELD, ANNOTATION_TYPE, TYPE_USE})
@Retention(RUNTIME)
@From(MyObjectGen.class)
@GeneratorConfiguration
@interface MyObjectConf {
int fooMin() default 0;
int fooMax() default 10;
int barMin() default 0;
int barMax() default 10;
}
public static class MyObject {
private final int foo;
private final int bar;
public MyObject(int foo, int bar) {
this.foo = foo;
this.bar = bar;
}
}
public static class MyObjectGen extends Generator<MyObject> {
private final IntegerGenerator fooGen = new IntegerGenerator();
private final IntegerGenerator barGen = new IntegerGenerator();
public MyObjectGen() {
super(MyObject.class);
}
public void configure(MyObjectConf config) {
fooGen.configure(new InRangeBase() {
@Override
public String min() {
return String.valueOf(config.fooMin());
}
@Override
public String max() {
return String.valueOf(config.fooMax());
}
});
barGen.configure(new InRangeBase() {
@Override
public String min() {
return String.valueOf(config.barMin());
}
@Override
public String max() {
return String.valueOf(config.barMax());
}
});
}
@Override
public MyObject generate(SourceOfRandomness random, GenerationStatus status) {
return new MyObject(fooGen.generate(random, status),
barGen.generate(random, status));
}
}
@Property
public void lessThanZero(@MyObjectConf(fooMin = -10, fooMax = -1) MyObject obj) {
assertThat(obj.foo, lessThan(0));
}
}
@941design Thanks for this -- sorry I'm just getting around to reading it. Will advise.
@941design I think using the random
passed to generate()
directly might be your best bet:
public static class MyObjectGen extends Generator<MyObject> {
private int fooMin, fooMax, barMin, barMax;
public MyObjectGen() {
super(MyObject.class);
}
public void configure(MyObjectConf config) {
this.fooMin = config.fooMin();
this.fooMax = config.fooMax();
this.barMin = config.barMin();
this.barMax = config.barMax();
}
@Override
public MyObject generate(
SourceOfRandomness random,
GenerationStatus status) {
return new MyObject(
random.nextInt(fooMin, fooMax),
random.nextInt(barMin, barMax));
}
}
@941design Let me know how this works for you -- if satisfactory, I'll close the issue. Thanks!