Skip to content
Property based testing with junit-quickcheck
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
src
.gitignore
.travis.yml
README.md
checkstyle.xml
lombok.config
pom.xml

README.md

Build Status

Read the full article: https://technology.first8.nl/generating-test-data-with-junit-quickcheck/

Generative testing demo

This project contains examples to generate data for tests, using junit-quickcheck. The application itself is a rest service, with one end point serving 3 books.

All test classes need to have a @RunWith(JUnitQuickcheck.class) annotation.

Build-in datatypes

From BookResourceTest

    /**
     * Example using build types, which junit-quickcheck can generate.
     * 
     * @param title generated by junit-quickcheck
     * @param author generated by junit-quickcheck
     * @param publisher generated by junit-quickcheck
     * @param publishedOn generated by junit-quickcheck
     */
    @Property
    public void testConversions(//
            final String title, //
            final String author, //
            final String publisher, //
            final LocalDate publishedOn) {

        // Given a book
        final Book book = new Book(title, new Author(author),
                new Publisher(publisher), publishedOn);

        // When we create a resource based on that book
        final BookResource resource = BookResource.fromBook(book);
        // And convert the resource back to a book
        final Book converted = resource.toBook();

        // Then we expected the result to have the same property values as the
        // original book
        assertThat(converted, SamePropertyValuesAs.samePropertyValuesAs(book));
    }

Here title, author, publisher and publishedOn values are generated by junit-quickcheck. This test is run 100 times with random values.

User defined datatypes

    /**
     * Example using a user defined type, of which all constructor arguments are
     * build-in types.
     * 
     * @param resource generated by junit-quickcheck
     */
    @Property
    public void testConversions(// Given a resource
            final @From(Ctor.class) BookResource resource) {

        // When we convert the resource into a book
        final Book book = resource.toBook();
        // And we convert the book back to a resource
        final BookResource converted = BookResource.fromBook(book);

        // Then we expect the result to have the same property values as
        // the original resource
        assertThat(converted,
                SamePropertyValuesAs.samePropertyValuesAs(resource));
    }

Here we let junit-quickcheck generate BookResource objects. With @From(Ctor.class) we specify that junit-quickcheck should use the constructor. This means that we can only use constructors with only build-in datatypes as parameters.

Custom generator

    /**
     * Because book has a constructor with user defined data types (Author and
     * Publisher), the Ctor.class generator will not work.
     * 
     * Therefore, we need to create a generator, to generate a book.
     */
    public static final class BookGenerator extends Generator<Book> {
        /**
         * Generator to generate strings for title, author name and publisher
         * name.
         */
        private final StringGenerator stringGenerator = new StringGenerator();
        /**
         * Generator to generate dates.
         */
        private final LocalDateGenerator localDateGenerator =
                new LocalDateGenerator();

        public BookGenerator() {
            super(Book.class);
        }

        @Override
        public Book generate(final SourceOfRandomness random,
                final GenerationStatus status) {
            final String title = stringGenerator.generate(random, status);
            final String author = stringGenerator.generate(random, status);
            final String publisher = stringGenerator.generate(random, status);
            final LocalDate publishedOn =
                    localDateGenerator.generate(random, status);
            final Book book = new Book(title, new Author(author),
                    new Publisher(publisher), publishedOn);
            return book;
        }
    }

    /**
     * Example using the BookGenerator defined above, to generate books
     * 
     * @param book generated by junit-quickcheck
     */
    @Property
    public void testConversions(// Given a book
            final @From(BookGenerator.class) Book book) {

        // When we convert the book to a resource
        final BookResource resource = BookResource.fromBook(book);
        // and we convert the resource back to a book
        final Book converted = resource.toBook();

        // Then we expect the result to have the same property values as the
        // original book
        assertThat(converted, SamePropertyValuesAs.samePropertyValuesAs(book));
    }

In order to generate values of user defined types, with constructor parameters featuring even more user defined types, we must create our own Generator. We reference this generator in @From annotation.

Mocking and collections

From BookControllerTest

    @Mock
    private BookService bookService;

    @InjectMocks
    private BookController bookController;

    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

Above, we set up mocking as usual.

    /**
     * Tests the book controller against a mock book service, using
     * junit-quickcheck to generate test data.
     * 
     * @param resources generated by junit-quickcheck
     */
    @Property()
    public void testBasedOnResources(//
            final List<@From(Ctor.class) BookResource> resources) {

        // Given an mocked bookservice, which returns generated data when
        // findAll() is called.
        when(bookService.findAll()).thenReturn(resources.stream()
                .map(BookResource::toBook).collect(Collectors.toList()));

        // When we call books() on the book controller
        final List<BookResource> results = bookController.books();

        // We expect the book controller to return book resources which match
        // the books
        Streams //
                .zip(resources.stream(), results.stream(), Pair::of)//
                .forEach(pair -> {
                    final BookResource reference = pair.getLeft();
                    final BookResource result = pair.getRight();
                    assertThat( //
                            reference, //
                            SamePropertyValuesAs.samePropertyValuesAs(result));
                });
    }

Here we let junit-quickcheck generate lists of BookResource objects. Note the @From(Ctor.class) annotation binds to the BookResource, the type parameter of List and not List itself.

You can’t perform that action at this time.