Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arbitrary proiders registered under a Domain don't seem to support @ForAll properly #407

Closed
adam-waldenberg opened this issue Nov 10, 2022 · 3 comments

Comments

@adam-waldenberg
Copy link

adam-waldenberg commented Nov 10, 2022

Testing Problem

The following code:

public class CustomDomain extends DomainContextBase {
	@Provide
	public Arbitrary<MyType> provideMyType(@ForAll int number) {
		return Arbitraries.of(new MyType());
	}
}

public class MyTest {
	@Property
	@Domain(CustomDomain.class)
	public void shouldMatch(@ForAll MyType myType) {
		/* Some testring logic */
	}
}

This throws an exception complaining that an arbitrary for parameter type int cannot be found - something I am assuming should work. At least that is what I would expect.

Current Workaround

While not ideal - current workaround is to use inheritance (rather than a Domain annotation) and share the provider that way.

@jlink
Copy link
Collaborator

jlink commented Nov 10, 2022

The feature works almost as you expect. Let me explain:
Domains are conceived to give you perfect control about how objects of a certain domain (think accounting, sales, ...) are being created. That means, that by default they do not inherit the global context, ie. all the default and globally registered arbitrary providers.

To make your example work, you'll have to tell jqwik to use the global context as well. You can do that in one of two ways.
Either declare it at the specific domain:

@Domain(DomainContext.Global.class)
public class CustomDomain extends DomainContextBase {
	@Provide
	public Arbitrary<MyType> provideMyType(@ForAll int number) {
		return Arbitraries.of(new MyType());
	}
}

Or at the place where you use the domain:

@Domain(DomainContext.Global.class) // alternatively on the test container class
public void shouldMatch(@ForAll MyType myType) {
	/* Some testing logic */
}

That said, domains are not considered to be the best way for reusing provider methods.
Consider arbitrary suppliers instead.

And one more thing on using @ForAll parameter in provider methods.
Under the hood, they will always fall back to flat mapping over the arbitrary generating the injected parameter.
You pay for flat-mapping with rather non-optimal shrinking. If you really need it depends on how you use the parameter.
In your example, flat-mapping may be warranted, if the injected number is being used for configuring the resulting arbitrary, e.g.

@Provide
public Arbitrary<List<MyType>> provideMyTypes(@ForAll int number) {
	return Arbitraries.create(() -> new MyType()).list().ofSize(number);
}

If you just want to use number in a composed value, I'd recommend plain mapping, e.g.:

@Provide
public Arbitrary<MyType> provideMyType() {
	return Arbitraries.integers().map (number -> new MyType(number));
}

@jlink jlink closed this as completed Nov 10, 2022
@adam-waldenberg
Copy link
Author

Got it! Sneaky. I see it's in one of the examples on the User guide - but it probably needs to be documented a little better.

@jlink
Copy link
Collaborator

jlink commented Nov 10, 2022

but it probably needs to be documented a little better.

I agree. Will put it on my todo list for the next release.

adam-waldenberg added a commit to unigrid-project/hedgehog that referenced this issue Nov 11, 2022
Due to some issues in JQwik, we currently have to wrap the provider in a
@provide method and call the model. Meaning, everywhere we use the model,
we have to define a @provide method. Not ideal - but works for now. For
more information on the issue, see:

jqwik-team/jqwik#407
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants