Giter Club home page Giter Club logo

Comments (9)

jlink avatar jlink commented on July 20, 2024

I did a short dive into junit-quickcheck's complex types examples. I hope that there's a solution with fewer API methods.

Would something like

public class Arbitraries ...
    public static <T> Arbitrary<T> forClass(Class<T> targetClass, Constructor constructor)

fit the scenarios you're thinking of? Parameter constructor can be optional if there's only one constructor available. The straightforward implementation I have in mind would only work if all ctor parameters can be provided out of the box or by registered arbitrary providers.

I've also been thinking about why the need for "automagic" complex generation hasn't shown up in my properties so far. I think that using plain generators - e.g. just @ForAll String aString - is rarely what I want. In my Java programs - unlike in Haskell code - most base types do need additional constraints to make sense within a domain. That's why I often build up arbitraries for domain types from the ground up and combine them together to arbitraries for more complex types. In those cases Arbitraries.forClass() as sketched above will almost never work.

from jqwik.

sir4ur0n avatar sir4ur0n commented on July 20, 2024

In my experience, we often have rich POJOs made out of smaller POJOs. Those rich POJOs definitely need some additional constraints on some fields (e.g. a price cannot be negative), however many other fields accept all their type space (POJO, enums, Strings, UUID, collections...).
Writing by hand manual generators for each sub-POJO is already a PITA, having to craft everything manually for each single field makes it unnecessarily cumbersome.

Also, when there is a generic way to build an arbitrary POJO, I'm pretty sure there would be value in a pre-processor + annotation mechanism so that we don't even have to write all the noise around generators. But that's another story 😄

I'm curious, why did you say unlike in Haskell code? We actually have a part of our codebase in Haskell and we want/expect/hope to write the same kind of property tests in both languages. Of course Haskell typing is much more powerful. I don't see why Haskell base types need less constraints (though I DO see how Haskell types make it easier to express our model 😄 ).

Edit: Forgot to answer: your proposal looks nice as long as there's an overload (most of our POJOs have a single constructor, let's avoid noise!). What is this Constructor type though? Is it java.lang.reflect.Constructor? I'm not a big consumer of reflection APIs: is there an easy way to get a Constructor?

from jqwik.

jlink avatar jlink commented on July 20, 2024

A propos Haskell: Algebraic types allow stricter domain modelling than Java's type system does. Thereby the percentage of total functions (a function that is valid for all possible inputs) can be much higher if you use this capability to restrict types to allow only what the domain does. In that case you don't need additional constraints for value generation in properties since the type already has that information. And that's even more true for dependent types as in Idris.

java.lang.reflect.Constructor can be retrieved by reflection - e.g. MyClass.class.getConstructors(). A constructor has all the information jqwik needs to generate parameters for it and run it. So, given

public static class Person {
	public Person(String name, int age, List<String> hobbies) {
		...
	}
}

Something like that should be doable:

Arbitrary<Person> people = Arbitraries.forType(Person.class);

Worthwhile in your context?

from jqwik.

sir4ur0n avatar sir4ur0n commented on July 20, 2024

I'm actually skeptical, because imagine you have 2 constructors: an empty one (mandatory for some stuff) and one with all fields.
Obviously you don't want an arbitrary that doesn't fill any field, so you need every time to get a reference to the full constructor:

Foo.class.getConstructor(String.class, int.class, int.class)

Which, again, seems unnecessarily verbose.

I just took a look at the fieldsOf() reflection mechanism of junit-quickcheck and it actually doesn't look that complicated (I may have missed some complex parts of course ^^'):

  1. grab all class and parent fields
  2. remove synthetic and final fields
  3. change visibility
  4. iterate over all remaining fields to invoke the generate method and set the field.

This ensures all fields are reached, with minimalist writing/maintenance cost.

Arbitrary<Person> people = Arbitraries.forTypeWithFields(Person.class);

from jqwik.

jlink avatar jlink commented on July 20, 2024

The fieldsOf approach won't consider any constraints applied in the constructor.

What I could do: Use all non-private constructors that have parameters and choose randomly between them.

from jqwik.

jlink avatar jlink commented on July 20, 2024

Started to work on it. Hold your breath...

from jqwik.

jlink avatar jlink commented on July 20, 2024

The current snapshot has a first shot at the feature. Starting point is

class Arbitraries {
    public static <T> TypeArbitrary<T> forType(Class<T> targetType)` {}
}

It's currently working on constructors and factory methods. Using fields would be another thing to implement.

See https://github.com/jlink/jqwik/blob/master/api/src/main/java/net/jqwik/api/arbitraries/TypeArbitrary.java for the options you have.

See https://github.com/jlink/jqwik/tree/master/documentation/src/test/java/net/jqwik/docs/types for an example. It's especially interesting when combined with the other new feature:
Domains

All feedback welcome and appreciated.

from jqwik.

jlink avatar jlink commented on July 20, 2024

I like the current solution. Below is the relevant part from the user guide. Please comment!

Generation from a Type's Interface

Some domain classes are mostly data holders. They come with constructors
or factory methods to create them and you might want to create different
instances by "just" filling the constructors' parameters with values
that are themselves generated. Using the building blocks you've seen until
now requires the use of Arbitrary.map() or even Combinators.combine(...).as(...)
to invoke the relevant constructor(s) and/or factories yourself.
There's a simpler way, though...

Consider a simple Person class:

public class Person {

	private String name;
	private final int age;

	public Person(String name, int age) {
		if (name == null || name.trim().isEmpty())
			throw new IllegalArgumentException();
		if (age < 0 || age > 130)
			throw new IllegalArgumentException();

		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return String.format("%s (%d)", name, age);
	}
}

A first step to use arbitrarily generated Person objects without having
to write a lot of jqwik-specific boiler plat code could look like that:

@Property
void aPersonsIsAlwaysValid(@ForAll @UseType Person aPerson) {
    Assertions.assertThat(aPerson.name).isNotBlank();
    Assertions.assertThat(aPerson.age).isBetween(0, 130);
}

Notice the annotation @UseType which tells jqwik to use the type
information of Person to generate it. By default the framework will
use all public constructors and all public, static factory methods in
the class in order to generate instances. Whenever there's an exception during
generation they will be ignored; that way you'll only get valid instances.

There are quite a few ways usage and configuration options. Have a look
at the complete example:
and check the following api entry points:

from jqwik.

jlink avatar jlink commented on July 20, 2024

Covered for now. Feel free to reopen or create a new issue for enhancements.

from jqwik.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.