Рубрики
Без рубрики

Реалистичная генерация тестовых данных для приложений Java

Чем ближе ваши тестовые или демонстрационные данные к реальному миру, тем лучше вы сможете протестировать приложение… Помеченный java, vaadin, данными, mariadb.

Чем ближе ваши тестовые или демонстрационные данные к реальному миру, тем лучше вы сможете протестировать приложение на предмет UX, улучшить и выявить проблемные ситуации во время разработки. В этой статье я покажу вам, как использовать генератор примеров данных Vaadin для создания демонстрационных данных для простой базы данных SQL. В статье показано, как создать полноценное приложение, объединяющее весеннюю загрузку , СПД , Проект Ломбок , Ваадин и МариаДБ .

Вы также можете посмотреть видеоверсию этой статьи:

Настройка проекта

В этой статье я использую проект Vaadin Flow, созданный с помощью онлайн-инструмента под названием Vaadin Start . Однако вы можете использовать генератор примеров данных Vaadin в любом проекте Java с Vaadin или без него.

Добавление необходимых зависимостей

Чтобы использовать генератор данных примера Vaadin, добавьте следующую зависимость в pom.xml файл:


    com.vaadin
    exampledata
    4.0.0

Если вы читаете эту статью с нуля, добавьте данные Spring, MariaDB Драйвер JDBC и зависимости проекта Ломбок, а также:


    org.springframework.boot
    spring-boot-starter-data-jpa


    org.mariadb.jdbc
    mariadb-java-client
    runtime


    org.projectlombok
    lombok
    true

Реализация простого Бэкэнда

Допустим, мы хотим реализовать представление для отображения списка книг. Нам нужно настроить подключение к базе данных, создать класс Java, представляющий книгу (как сущность JPA), создать класс (или интерфейс) для доступа к базе данных с использованием JPA и реализовать класс обслуживания для инкапсуляции деталей технологии базы данных.

Настройка подключения к базе данных

С помощью Spring Boot вы можете настроить подключение к базе данных в файле application.properties , добавив следующее:

spring.datasource.url=jdbc:mariadb://localhost:3306/book_demo
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=create

Последняя строка необходима только в том случае, если вы хотите удалять и заново создавать схему базы данных при каждом запуске приложения. Существуют другие варианты , которые могут быть более удобными во время разработки, в зависимости от стадии, на которой вы находитесь в своем проекте.

Не забудьте установить параметры подключения к базе данных так, чтобы они указывали на ваш экземпляр базы данных.

Внедрение организации JPA

Внедрение организации JPA становится проще с проектом Ломбок. Вот возможная реализация:

package com.example.application.backend;

import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.*;
import java.time.LocalDate;

@Data
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Entity
public class Book {

    @EqualsAndHashCode.Include
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Lob
    private String imageData;

    private String title;

    private String author;

    private LocalDate publishDate;

    private Integer pages;

}

Этот класс поддерживает сохранение, что означает, что JPA сможет сопоставлять экземпляры этого класса с таблицей базы данных MariaDB (или любой другой базой данных, предоставляющей драйвер JDBC). Здесь важно отметить, что мы хотим, чтобы столбец id автоматически создавался для нас, если мы передаем значение null . Аннотация Ломбока @Data добавляет геттеры и сеттеры, а также @EQUALS и хэш-код … Я уверен, вы можете догадаться, что он делает. Важно то, что мы указываем Ломбоку использовать только свойство id для equals(объекта) и Хэш-код() методы. Таким образом, две книги одинаковы, если они имеют одинаковые значения id независимо от того, имеют ли другие свойства разные значения или нет. Внедрение этих двух методов необходимо для правильного функционирования JPA.

Реализация класса репозитория

Нам нужен способ получить доступ к базе данных. Мы могли бы использовать API JDBC для подключения к базе данных MariaDB, выполнения SQL-запросов и ручной установки возвращаемых значений в экземплярах Book . Однако миссия JPA состоит в том, чтобы обеспечить эту функциональность, и она дополняется данными Spring. Доступ к базе данных настолько прост, что нам нужно всего лишь объявить интерфейс:

package com.example.application.backend;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface BookRepository extends JpaRepository {
}

Нет никакой необходимости реализовывать этот интерфейс вообще. Spring Data предоставит объекты, которые реализуют интерфейс, когда это необходимо. Если вы изучите методы, доступные в интерфейсе, вы найдете много полезных для создания, чтения, обновления, удаления Книги экземпляров. Вы также можете добавлять методы в интерфейс без их реализации, и Spring Data будет использовать соглашение об именах для создания реализации для вас.

Реализация класса обслуживания

Интерфейс Хранилища книг не скрывает того факта, что мы используем JPA в качестве механизма сохранения. Чтобы улучшить удобство сопровождения кода, мы можем ввести новый класс, который использует репозиторий и предоставляет методы, необходимые пользовательскому интерфейсу. Этот класс также может добавлять любую дополнительную бизнес-логику, требуемую приложением:

package com.example.application.backend;

import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BookService {

    private final BookRepository repository;

    public BookService(BookRepository repository) {
        this.repository = repository;
    }

    public List findAll() {
        return repository.findAll();
    }

}

Конструктор этого класса обслуживания принимает объект типа Хранилище книг . Так как класс также помечен @Service , Spring создаст новый экземпляр репозитория и передаст его конструктору, когда у вас, в свою очередь, будет конструктор в другом классе (например, другой сервис или пользовательский интерфейс, когда он реализован на Java), который принимает объект BookService . Spring создает все экземпляры для вас, используя шаблон, называемый Инверсия управления таким образом, вы никогда не используете новый Ключевое слово Java для создания этих экземпляров и предоставления Spring возможности передавать объекты через конструкторы с использованием шаблона, называемого Внедрение зависимостей . Существует множество онлайн-ресурсов, чтобы узнать больше об этом .

Использование Генератора данных Примера Vaadin

Хорошим моментом для создания демонстрационных данных является запуск приложения. Чтобы запустить метод Java при запуске приложения, мы можем создать компонент Spring типа CommandLineRunner в любом классе конфигурации, например, мы можем добавить следующий метод в класс Application :

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

Spring введет необходимый объект Хранилище книг перед выполнением метода.

Настройка генератора

Доступ к генератору примеров данных Vaadin осуществляется через класс Генератор примеров данных . Вот код, который мы можем добавить в лямбда-выражение предыдущего фрагмента кода:

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 books = generator.create(100, new Random().nextInt());
}

Это проверяет, что в базе данных нет книг, так как мы не хотим портить данные, если они уже существуют.

Генератор настраивается с помощью метода setData(BiConsumer, тип данных) , который принимает ссылку метода на установщик в классе Book и определенный тип данных. Существует множество доступных типов данных. Обязательно проверьте значения в классе Тип данных , чтобы получить представление. Вы найдете, например, типы данных для создания названий книг, имен людей, дат, времени, городов, стран, номеров телефонов, адресов, названий продуктов питания, слов, предложений, чисел, логических значений и других.

Создание и сохранение примера данных

Вызовите метод create(int, int) для создания экземпляров Книги :

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

Первый параметр – это количество создаваемых экземпляров (100 книг в предыдущем примере), а второй – начальное значение, используемое внутренним генератором случайных чисел. Метод возвращает список объектов, которые мы можем сохранить с помощью экземпляра репозитория:

repository.saveAll(books);

Генерация измерительных данных и экономия времени

Полезно отображать сообщение в журнале, когда приложение генерирует данные, процесс, который может занять некоторое время в зависимости от типа и объема создаваемых данных. Также полезно показывать сообщение о завершении процесса генерации данных, возможно, с указанием времени, которое на это ушло. Вот полная реализация метода создать демонстрационные данные, если это необходимо (Хранилище книг) , который делает именно это:

@SpringBootApplication
@Theme(value = "demo")
@PWA(name = "Demo", shortName = "Demo", offlineResources = {"images/logo.png"})
@NpmPackage(value = "line-awesome", version = "1.3.0")
@Log4j2
public class Application extends SpringBootServletInitializer implements AppShellConfigurator {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public 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 books = generator.create(100, new Random().nextInt());
                repository.saveAll(books);
                stopWatch.stop();
                log.info("Demo data generated in " + stopWatch.getTime() + "ms.");
            }
        };
    }

}

Это использует Apache Commons ( секундомер ) для синхронизации и Ломбок ( @Log4j2 ) для ведения журнала.

Реализация веб-представления на Java

Вы можете проверить, действительно ли данные находятся в базе данных, подключившись к экземпляру MariaDB и выполнив следующий запрос:

select * from book;

Однако, чтобы сделать его более интересным, мы можем добавить веб-представление с помощью Vaadin и изучить данные в браузере:

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();
        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(Book book) {
        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();
    }
}

Этот класс использует API Vaadin для добавления представления, сопоставленного с корневым контекстом через @Route("") . Конструктор создает Сетку Компонент пользовательского интерфейса и настраивает столбцы, соединяющие каждый из них со свойством в классе Book , используя соответствующие геттеры. Там есть специальный столбец, в котором отображается уменьшенное изображение, которое при нажатии открывает диалоговое окно для отображения обложки книги в виде увеличенного изображения.

Чтобы запустить приложение, запустите:

mvn spring-boot:run

В качестве альтернативы вы можете просто запустить стандартную точку входа main(Строка[]) метод в классе Приложения . Как только приложение скомпилировано и запущено (процесс, который может занять больше времени, если вы создаете его в первый раз), вы можете получить к нему доступ в браузере по адресу http://localhost:8080 . Вот скриншот:

Оригинал: “https://dev.to/alejandro_du/realistic-test-data-generation-for-java-apps-9kn”