Home Java Creating realistic test data for Java applications

Creating realistic test data for Java applications

by admin

The closer your test or demo data is to the real world, the better you can test your application for UX, improve and catch extreme cases during development.In this article, I will show you how to use the Vaadindata generator example to create demo data for a simple SQL database.The article shows you how to create a complete application using Spring Boot , JPA , Project Lombok , Vaadin and MariaDB

You can also watch a video version of this article :

Project setup

In this article I use a Vaadin Flow project created using an online tool called Vaadin Start However, you can use the Vaadin data generator example in any Java project with or without Vaadin.

Adding necessary dependencies

To use the Vaadin data generator example, add the following dependency to the file pom.xml :

<dependency><groupId> com.vaadin</groupId><artifactId> exampledata</artifactId><version> 4.0.0</version></dependency>

If you are reading this article from scratch, also add Spring Data, MariaDB JDBC and Project Lombok dependencies:

<dependency><groupId> org.springframework.boot</groupId><artifactId> spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId> org.mariadb.jdbc</groupId><artifactId> mariadb-java-client</artifactId><scope> runtime</scope></dependency><dependency><groupId> org.projectlombok</groupId><artifactId> lombok</artifactId><optional> true</optional></dependency>

Implementation of a simple backend

Let’s say we want to implement a view to display a list of books. We need to set up a connection to the database, create a Java class that represents the book (as a JPA object), create a class (or interface) to access the database using JPA, and implement a service class to encapsulate the details of the database technology.

Configuring the database connection

With Spring Boot, you can configure the database connection in the application.properties by adding the following :

spring.datasource.url=jdbc:mariadb://localhost:3306/book_demospring.datasource.username=rootspring.datasource.password=passwordspring.jpa.hibernate.ddl-auto=create

The last line is only needed if you want to delete and recreate the database schema each time you run the application. There is and other options, which may be more convenient during development, depending on what stage you’re at in your project.

Remember to set your database connection parameters so that they point to your database instance.

Implementing a JPA entity

Implementing a JPA entity is easier with Project Lombok. Here is an example of a possible implementation :

package com.example.application.backend;import lombok.Data;import lombok.EqualsAndHashCode;import javax.persistence.*;import java.time.LocalDate;@Data@EqualsAndHashCode(onlyExplicitlyIncluded = true)@Entitypublic class Book{@EqualsAndHashCode.Include@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;@Lobprivate String imageData;private String title;private String author;private LocalDate publishDate;private Integer pages;}

This class is persistence-ready, which means that JPA will be able to map instances of this class to a MariaDB database table (or any other database that the JDBC driver provides). It is important to note here that we want the column id is automatically generated for us if we pass the value of null With Lombok annotation @Data Getters and setters are added, and @EqualsAndHashCode I’m sure you’ve guessed what it does. The important thing is that we instructed Lombok to use only id property for the methods equals(Object) and hashCode() So, two books are the same if they have the same values id , whether or not the other properties have different values. Implementing these two methods is necessary for JPA to function properly.

Implementation of the cClass repository

We need a way to access the database. We could use the JDBC API to connect to the MariaDB database, run SQL queries, and set manually returned values in instances of the Book However, JPA’s mission is to provide this functionality, which is augmented by Spring Data. Access to the database is so simple that we only need to declare an interface :

package com.example.application.backend;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Repository;@Repositorypublic interface BookRepositoryextends JpaRepository<Book, Integer> {}

There is no need to implement this interface at all. Spring Data will provide objects that implement the interface if necessary. If you examine the methods available in the interface, you will find many useful for creating, reading, updating, and deleting instances of Book You can also add methods to the interface without their implementation, and Spring Data will use the naming convention to create the implementation for you.

Service class implementation

interface BookRepository does not hide the fact that we use JPA as a persistence mechanism. To improve code support, we can introduce a newclass that uses the repository and provides the methods needed by the user interface. We can also add any additional business logic needed for the application to this class :

package com.example.application.backend;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class BookService{private final BookRepositoryrepository;public BookService(BookRepository repository) {this.repository = repository;}public List<Book> findAll() {return repository.findAll();}}

The constructor of this Service class gets an object of the type BookRepository Since the class also has the annotation @Service , Spring will create a new repository instance and pass it to a constructor when you, in turn, have a constructor in another class (such as another service or user interface when implemented in Java. ), which gets the object BookService object. Spring creates all instances of the class for you using a design pattern called Inversion of Control, so you never use the keyword new Java to create these class instances and give Spring the ability to pass objects through constructors using a design pattern called Dependency Injection. There are many online resources where you can find out more about it

Using the Vaadin Data Generator Example

A convenient time to create demo data is when running an application. To run a Java method at application startup, we can create a bean component like Spring CommandLineRunner in any configuration class, for example, we can add to the class Application the following method :

@Beanpublic CommandLineRunner createDemoDataIfNeeded(BookRepository repository) {return args -> {... logic here ...};}

Spring will implement the desired object BookRepository Before executing the method.

Generator tuning

The Vaadin data generator pattern is accessed using the ExampleDataGenerator Here is the code we can add to the lambda expression from the previous code snippet :

if (repository.count() == 0) {var generator = new ExampleDataGenerator<> (Book.class, LocalDateTime.now());generator.setData(Book::setImageData, DataType.BOOK_IMAGE_URL);generator.setData(Book::setTitle, DataType.BOOK_TITLE);generator.setData(Book::setAuthor, DataType.FULL_NAME);generator.setData(Book::setPublishDate, DataType.DATE_LAST_10_YEARS);generator.setData(Book::setPages, new ChanceIntegerType("integer", "{min:20, max: 1000}"));List<Book> books = generator.create(100, new Random().nextInt());}

It checks if there are no books in the database, because we don’t want to corrupt the data if it already exists.

The generator is configured using the method setData(BiConsumer, DataType) , which takes a reference to the setter method in the class Book and a specific data type. There are many data types available. Be sure to study the values in the class DataType to get an idea of the data types. You will find, for example, data types for creating book names, people names, dates, times, cities, countries, phone numbers, addresses, food names, words, sentences, numbers, logical values, etc.

Creating and saving example data

Call the create(int, int) to create instances of the class Book :

List<Book> books = generator.create(100, new Random().nextInt());

The first parameter is the number of created instances (100 books in the previous example) and the second is the initial number used by the internal random number generator. The method returns a list of objects that we can save with the repository instance :

repository.saveAll(books);

Measuring the time to create and save the data

It is useful to display a message in the log when an application generates data, a process that may take time depending on the type and amount of data being generated. It is also useful to show a message when the data generation process is complete, perhaps showing the time it took. Here is the full implementation of the method createDemoDataIfNeeded(BookRepository) which does exactly that :

@SpringBootApplication@Theme(value = "demo")@PWA(name = "Demo", shortName = "Demo", offlineResources = {"images/logo.png"})@NpmPackage(value = "line-awesome", version = "1.3.0")@Log4j2public class Application extends SpringBootServletInitializer implements AppShellConfigurator {public static void main(String[] args) {SpringApplication.run(Application.class, args);}@Beanpublic CommandLineRunner createDemoDataIfNeeded(BookRepository repository) {return args -> {if (repository.count() == 0) {log.info("Generating demo data...");var generator = new ExampleDataGenerator<> (Book.class, LocalDateTime.now());generator.setData(Book::setImageData, DataType.BOOK_IMAGE_URL);generator.setData(Book::setTitle, DataType.BOOK_TITLE);generator.setData(Book::setAuthor, DataType.FULL_NAME);generator.setData(Book::setPublishDate, DataType.DATE_LAST_10_YEARS);generator.setData(Book::setPages, new ChanceIntegerType("integer", "{min: 20, max: 1000}"));var stopWatch = new StopWatch();stopWatch.start();List<Book> books = generator.create(100, new Random().nextInt());repository.saveAll(books);stopWatch.stop();log.info("Demo data generated in " + stopWatch.getTime() + "ms.");}};}}

It uses Apache Commons ( StopWatch ) for synchronization and Lombok for logging ( @Log4j2 ).

Implementation of web representation in Java

You can check if the data is actually in the database by connecting to a MariaDB instance and running the following query :

select * from book;

However, to do this in a more interesting way, we can add a web representation using Vaadin and examine the data in the browser :

package com.example.application.ui;import com.example.application.backend.Book;import com.example.application.backend.BookService;import com.vaadin.flow.component.dialog.Dialog;import com.vaadin.flow.component.grid.Grid;import com.vaadin.flow.component.html.Image;import com.vaadin.flow.component.orderedlayout.VerticalLayout;import com.vaadin.flow.router.Route;@Route("")public class BooksView extends VerticalLayout {public BooksView(BookService service) {var grid = new Grid<Book> ();grid.setSizeFull();grid.addComponentColumn(this::getThumbnail);grid.addColumn(Book::getTitle).setHeader("Title");grid.addColumn(Book::getAuthor).setHeader("Author");grid.addColumn(Book::getPublishDate).setHeader("Publish date");grid.addColumn(Book::getPages).setHeader("Pages");grid.setItems(service.findAll());add(grid);setSizeFull();}private Image getThumbnail(Bookbook) {var image = new Image(book.getImageData(), book.getTitle() + " cover");image.setHeight("70px");image.addClickListener(event -> showCover(book));return image;}private void showCover(Book book) {var image = new Image(book.getImageData(), "Cover");image.setSizeFull();var dialog = new Dialog(image);dialog.setHeight("90%");dialog.open();}}

This class uses the Vaadin API to add a view mapped to the root context using annotation @Route("") Constructor creates a user interface component Grid and configures the columns that connect each of them to a property in the Book , using appropriate getter methods. There is a special column that displays a thumbnail which, when clicked, opens a dialog box in which the book cover is displayed as an enlarged image.

To start the application, run the command :

mvn spring-boot:run

Alternatively, you can simply run the Application standard entry point method main(String[]) Once the application is compiled and running (a process which may take longer if you are creating it for the first time), you can access it in the browser at http://localhost:8080 Here is a screenshot of it :

Creating realistic test data for Java applications

You may also like