Read the full article: https://technology.first8.nl/generating-test-data-with-junit-quickcheck/
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.
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.
/**
* 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.
/**
* 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.
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.