Open tsuharesu opened 6 years ago
Hi @tsuharesu thanks for the question. I believe you're asking if you can customise a particular argument in a constructor - if so then unfortunately this isn't possible.
I'd absolutely love to be able to do this but it requires knowing the name of the constructor argument through reflection, which Java 8 is better at, but at the time of writing this lib I couldn't see a reliable way of doing this. Maybe Kotlin has a way of doing this?
A couple workarounds I can think of are,
1.
// customise the instance and manually create it using JFixture
// for each default arg and do your own thing for the other args.
// This will quickly get unwieldy though
fixture.customise().lazyInstance(Order.class, () -> new Order(fixture.create(String.Long), "custom string"));`
fixture.create(Order.class);
2.
// customise the type of the argument you're overriding.
// Obviously this will apply for all instances of that type though
fixture.customise().lazyInstance(String.class, () -> "my order id");
fixture.create(Order.class);
If you know how this could be done I'd be very happy to take a PR as it's a feature that's been requested a few times.
Thanks
Additionally, except with Kotlin data classes, there is no guarantee a contructor parameter named "size" corresponds to the "size" field.
You could use the copy constructor:
fixture.customise().lazyInstance(Order.class, () -> new JFixture().create(Order.class).copy(size = 123));
fixture.create(Order.class);
However you will have to use a different instance of JFixture to create the initial object. The intercept
customisation does not return the new value, otherwise would be perfect for this (this is a change we should make - unless there is already a way to do this?).
We solved this by setting the field via reflection. Kotlin data class properties are just simple final
fields. So we generate the fixture object and then setField(fixtObj, "size", 123)
; this can be combined with intercept
as well.
@richkeenan Kotlin tries to make the constructor parameter names accessible:
https://stackoverflow.com/a/36093832/253468
Accessible around: FooBar::class.constructors.first().parameters.first().name
public abstract val name: String? defined in kotlin.reflect.KParameter Name of this parameter as it was declared in the source code, or null if the parameter has no name or its name is not available at runtime. Examples of nameless parameters include this instance for member functions, extension receiver for extension functions or properties, parameters of Java methods compiled without the debug information, and others.
Edit: you can also access this from Java!
JvmClassMappingKt.getKotlinClass(FooBar.class).getConstructors().iterator().next().getParameters().get(0).getName()
@TWiStErRob That's very cool, exactly what we'd need to get it working. KFixture anyone? 😉
This means you can detect if Kotlin is on classpath and try to use this class
(e.g. compileOnly dependency with Class.forName
check before usage)
no need for a separate library or increasing dependency count of JFixture.
@richkeenan @TWiStErRob Thanks for your help, really!
I think Ex. 1 (using lazyInstance
) can help for now. Would be the same as using KodeIn to create instances of a class.
I like the idea of KFixture 😂
According to the wiki:
Is there a way to make this work in the constructor? I have a Kotlin
data class
with a property defined asval
. This property is in the constructor but has no setter.