JSON рандомизатор

Привет! Подоспела очередная порция годноты. В этот раз расскажу о проблеме рандомизации JSON и способе ее решения. Сразу спойлер: описанный способ подходит не только для JSON, поэтому можно дочитывать заметку до конца.

images?q=tbn:ANd9GcR9yXDTICCdyVcv  Xdule6W utrnacgseImIMaasrXqo4sqKfD

Давайте для начала определимся с проблемой, которую я буду решать. Представим проект, у которого есть фронтенд, который общается с бекендом посредством REST API. На таком проекте REST API мы используем для прекондишинов:

@Test
public void testCanEditUser(){
    String userEmail = UserApi.createUserForTest()

    User newUserData = User()
        .withName("Ivan")
        .withEmail("test@gmail.com")
        .withPhone("+456789453")

    open(EditUserPage.class)
        .findUser(userEmail)
        .toEditMode()
        .setData(newUserData)
        .save()
}

На первый взгляд, все достаточно удобно и просто. Суть проблемы кроется именно в прекондишине UserApi.createUserForTest(). В моем случае для создания юзера на эндпоинт нужен запрос с таким body:

{
  "emails": [
    {
      "emailTypeId": 82,
      "email": "test_person@email.com",
      "comment": "",
      "isDefault": true
    }
  ],
  "addresses": [
    {
      "isShippingDefault": true,
      "isBillingDefault": true,
      "addressTypeId": 80,
      "countryId": 3,
      "cityId": 2,
      "isGeneralInfo": true
    }
  ],
  "confidenceId": 120,
  "income": 0,
  "languageCode": "en",
  "lastName": "Ivanov",
  "name": "Test",
  "netWorth": 0,
  "nickName": "Ivan",
  "sexId": 130,
  "liquidAssets": 0,
  "employeeId": 1
}

Достаточно сложная структура с кучей полей. Наверное, на многих проектах можно встретить такую ситуацию. Для того, чтобы сделать тест, описанный выше, перезапускаемым, нам нужно некоторые значения полей, например, email, делать уникальными, иначе мы получим ошибку валидации.

Можно рандомизировать поле имейл:

UserDto userDto = JsonUtils.getUser("user.json",UserDto.class);
userDto.setEmails(Arrays.asList(EmailDto().withEmail(RandomUtils.nextEmail())));

UserApi.addNewUser(userDto);

Такой подход работает, если нам нужно рандомизировать одно поле. Если мы хотим делать все рандомно, то тут - дрова.

Упершись в эту проблему, я начал искать решение. Мне хотелось сделать так:

{
  "emails": [
    {
      "emailTypeId": 82,
      "email": "{{ email }}",
      "comment": "",
      "isDefault": true
    }
  ]
}

Просто ставить плейсхолдер, а при диссериализции получать JAVA-объект с уже рандомизованным email. Конечно, можно было это легко сделать: .replaceAll("{{ email }}",RandomUtils.nextEmail()).

Приколько, но недостаточно гибко. А если я захочу рандомизиовать поле phone? Мне что теперь идти и дописывать код?

Гуглеж натолкнул меня на один интересный сайт https://www.json-generator.com/. Отличная идея и реализация, но у него нету API, а подобное я нашел только для NodeJS. Мне же нужно для Java.

Присмотревшись к своей хотелке, я подумал: блин, так можно же взять template engine и дело в шляпе.

Покумекав еще пару дней, я нашел отличный инструмент jTwig. Очень простой и легковесный template engine для Java.

Пример работы с ним выглядит так:

JtwigTemplate template = JtwigTemplate.classpathTemplate("templates/user.json");
JtwigModel model = JtwigModel.newModel()
            .with("faker", new Faker());

String json = template.render(model);

UserDto userdto = getEntity(json, User.class);

Во второй строке я применил библиотеку java-faker.

Темплейты теперь можно писать так:

{
  "name": "{{ faker.name().firstName() }}",
  "lastName": "{{ faker.name().lastName() }}",
  "addresses": [
    "{{ faker.address().streetAddress() }}",
    "{{ faker.address().streetAddress() }}"
  ]
}

Да-да, jTwig умеет вызывать Java методы - мегафича.

Теперь, имея такую функциональность, те поля, которые нужно, мы параметризуем и после диссериализации получаем готовые Java-объекты.

P/S Описанный способ отлично работает на моем проекте. К сожалению, jTwig не умеет вызывать java-методы с параметрами:

{
    "id": "{{ randomIntBetween(7, 10) }}"
}

В таких случаях мы велосипедируем, но я знаю, что эту проблему можно решить, используя вместо jTwig какой-то другой template engine. К примеру, Freemarker, Thymeleaf или Pebble.

На этом у меня все. Оставайтесь на связи, подписывайтесь на мою группу в Facebook.