From 7cd6b3317401fba57fe08fdbde6c054c81cca387 Mon Sep 17 00:00:00 2001 From: Johannes Link Date: Fri, 7 Jun 2024 11:55:03 +0200 Subject: [PATCH] Changes user guide to reflect real behaviour of provider method type matching --- .../customized-parameter-generation.md | 55 +++++++++++++------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/documentation/src/docs/include/customized-parameter-generation.md b/documentation/src/docs/include/customized-parameter-generation.md index 4970f3270..968b49ac0 100644 --- a/documentation/src/docs/include/customized-parameter-generation.md +++ b/documentation/src/docs/include/customized-parameter-generation.md @@ -41,14 +41,32 @@ where `TParam` is the static type of the parameter to be provided. If the return type cannot be matched, jqwik will throw a `CannotFindArbitraryException`. **Caveat:** -_Since this kind of type matching follows Java's rules for assignability, -some type mismatches, especially in the presence of wildcards and constrained type variables, -can be confusing._ -Arbitrary provision usually starts with a +Up to version `1.7.4`, it was possible that the provider method's return type +was broader than the property method's parameter type. +Since version `1.8.0` this is no longer the case. +That's why the following code _will now fail at runtime_ with a `CannotFindArbitraryException`: + +```java +@Property +void favouritePrimes(@ForAll("favouritePrimes") int aFavourite) { +} + +@Provide +Arbitrary favouritePrimes() { + return Arbitraries.of(3, 5, 7, 13, 17, 23, 41, 101); +} +``` + +#### How to write a provider method + +Arbitrary provision often starts with a [static method call to `Arbitraries`](#static-arbitraries-methods), maybe followed -by one or more [filtering](#filtering), [mapping](#mapping) or -[combining](#combining-arbitraries) actions. +by one or more [filtering](#filtering), [mapping](#mapping), +[flat mapping](#flat-mapping) or [combining](#combining-arbitraries) actions. + +In general, all methods that return and modify arbitrary instances can be used. + #### Provider Methods with Parameters @@ -80,29 +98,30 @@ Arbitrary favouritePrimes() { } ``` -From time to time, though, you need it as a `BigInteger` instead of an `int`. -You can kill both types with a single method: +From time to time, though, you may want to use details from the type usage to change +how the arbitrary is created. +In the following example, an annotation is used to decide whether to generate large primes: ```java @Property -void favouritePrimesAsInts(@ForAll("favouritePrimes") int aFavourite) { ... } +void largePrimes(@ForAll("favouritePrimes") @LargePrimes int aPrime) { + System.out.println("prime = " + aPrime); +} @Property -void favouritePrimesAsBigInts(@ForAll("favouritePrimes") BigInteger aFavourite) { ... } +void smallPrimes(@ForAll("favouritePrimes") int aPrime) { + System.out.println("prime = " + aPrime); +} @Provide -Arbitrary favouritePrimes(TypeUsage targetType) { - Arbitrary ints = Arbitraries.of(3, 5, 7, 13, 17, 23, 41); - if (targetType.getRawType().equals(BigInteger.class)) { - return ints.map(BigInteger::valueOf); +Arbitrary favouritePrimes(TypeUsage targetType) { + if (targetType.findAnnotation(LargePrimes.class).isPresent()) { + return Arbitraries.integers().greaterOrEqual(2).filter(this::isPrime); } - return ints; + return Arbitraries.of(3, 5, 7, 13, 17, 23, 41); } ``` -Mind that Java's type system now forces you to use a wildcard in the return type. - - ### Arbitrary Suppliers Similar to [provider methods](#arbitrary-provider-methods) you can specify an