Многие сервисы в различных отраслях генерируют печатные формы счетов, отчётов и прочего в формате PDF. И конечно, перед специалистами тестирования встаёт вопрос о проверке этих документов.

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

В этой статье мы познакомим вас с инструментом, который достаточно просто может закрыть потребность в проверке содержания PDF-файлов. Это инструмент с открытым исходным кодом — pdf-test. Он по сути является обёрткой pdfbox и позволяет из коробки использовать основные мэтчеры, не задумываясь о том, чтобы писать свои или о том, как именно парсить документ. Рассмотрим далее его применение на языке Java.

Установка

Как и для любого другого инструмента, необходимо добавить зависимость в ваш файл проекта (актуальная версия на момент февраля 2024 года – 1.8.1):

pom.xml для Maven

<dependency>
	<groupId>com.codeborne</groupId>
	<artifactId>pdf-test</artifactId>
	<version>1.8.1</version>
	<scope>test</scope>
</dependency>

build.gradle для Gradle

testImplementation 'com.codeborne:pdf-test:1.8.1' 	

Можно начинать пользоваться.

Использование

Сценарий использования данного инструмента состоит всего из двух шагов:

  1. Создание экземпляра класса PDF;

  2. Проверка этого объекта средствами AssertJ или Hamcrest;

Рассмотрим всё по порядку.

Создание экземпляра класса PDF

Pdf-test позволяет создать экземпляр для дальнейшей работы несколькими способами:

  • Из файла: PDF pdf = new PDF(new File("test.pdf"));

  • Из адреса URL или URI: PDF pdf = new PDF(URL())/PDF pdf = new PDF(URI());

  • Из InputStream:  PDF pdf = new PDF(getClass().getClassLoader().getResourceAsStream("test.pdf"));

  • Из массива байтов: PDF pdf = new PDF(byteArray);

Для каждого из этих способов создать экземпляр класса PDF, существует перегрузка с указанием страниц, которые нужно парсить в объект:

	PDF pdf = new PDF(new File("test.pdf"), 1, 2);

Данный метод создаст экземпляр класса PDF, в котором будут страницы исходного документа с 1 по 2. Если вы попытаетесь создать объект из того, что файлом PDF не является, pdf-test вас об этом предупредит, выдав следующее исключение:

IllegalArgumentException – Invalid PDF file: test.pdf

Итак, теперь у нас есть объект PDF, который мы можем проверять.

Проверки PDF-файла

Что же мы можем проверить в нашем файле? Вообще, весь текст, кроме картинок и таблиц (хотя на таблицы в репозитории есть задача, за которую, возможно, кто-то возьмётся). Кроме содержимого мы можем проверить свойства файла при их наличии. Вот список свойств, которые доступны для проверки в pdf-test:

  int numberOfPages — число страниц в файле;
  String author — автор файла;
  Calendar creationDate — время создания файла;
  String creator — источник файла;
  String keywords — ключевые слова;
  String producer — программа-генератор файла;
  String subject — тема;
  String title  название файла;
  boolean encrypted — присутствие шифрования;
  boolean signed — присутствие электронной подписи;
  String signerName — имя подписанта;
  Calendar signatureTime — время подписания;

Следующие методы нужны уже для проверки содержимого PDF:

containsText(String text, String... texts) – проверка вхождения переданных строк в файл. Данная проверка регистрозависима, игнорирует множественные пробелы в переданной строке (то есть строки «Hello   world» и «Hello world» посчитает одинаковыми). Если будет передано несколько строк для проверки, то будет проверятся их вхождение в документ именно в том порядке, в каком они были переданы в метод. Если в файле несколько одинаковых подстрок, то находится только первое вхождение.

doesNotContainText(String text, String... texts) – проверка отсутствия переданных строк в тексте. Обладает теми же свойствами, что и предыдущая проверка.

containsExactText(String substring) – проверка присутствия в файле в точности переданного текста. Регистрозависима и не пропускает пробелы, в отличие от метода containsText(String text, String... texts).

doesNotContainExactText(String substring) – проверка, противоположная по смыслу предыдущей. Регистрозависима и не пропускает пробелы,  в отличие от метода doesNotContainsText(String text, String... texts).

containsTextCaseInsensitive(String substring) – регистронезависимая проверка присутствия в файле строки текста. Игнорирует пробелы, как и метод containsText(String text, String... texts).

И самое интересное:

matchesText(String regex) / matchesText(Pattern regex) – проверка того, что в файле есть подстрока, удовлетворяющая регулярному выражению. С помощью этой проверки можно проверять повторные вхождения текста, взаимное расположение текста, а также большие текстовые формы или текст, в котором есть меняющиеся данные (номера, имена, название и прочее).

Перейдём к практике

Приведём немного утрированный, но показательный пример того, как всё это может применяться на практике. Допустим, у нас есть генерируемое платёжное поручение с названием transaction.pdf:

Тогда тест по проверке нашего документа с использованием AssertJ будет выглядеть примерно так:

@Test
  public void bankTransactionTest() throws Exception {
	PDF pdf = new PDF(new File("transaction.pdf"));
	assertThat(pdf, containsText("11.11.2023"));
	assertThat(pdf, containsText("Поступ. в банк плат.", "Списано со сч. плат."));
	assertThat(pdf, matchText(".*ПЛАТЕЖНОЕ ПОРУЧЕНИЕ № \\d{3}.*"));
	assertThat(pdf, containsText("Дата", "Вид платежа", "Сумма прописью"));
	assertThat(pdf, containsText("ИНН", "КПП"));
	assertThat(pdf, containsExactText("Иван Тестов"));
	assertThat(pdf, containsText("Плательщик"));
	assertThat(pdf, matchText(".*Сч.№ \\d{20}.*"));
	assertThat(pdf, containsTextCaseInsensitive("ПАО \"БАНК \"САНКТ-ПЕТЕРБУРГ\""));
	assertThat(pdf, containsText("Отметки банка"));
	assertThat(pdf, matchText(".*БИК \\d{9}.*"));
	assertThat(pdf, matchText(".*Код операции \\d{12}.*"));

  	assertThat(pdf, doesNotContainsText("Test", "dev"));
  }

Вывод

Теперь вы знаете, как применить несложный и достаточно удобный инструмент проверки текста внутри pdf-файлов, и сможете прокачать им свои тесты. Пусть в нём не так много функциональности, но этого вполне достаточно, чтобы проверить PDF на то, что в нём есть вся нужная информация и ничего не потеряно, например, при генерации какой-нибудь важной квитанции. 

К тому же, это опенсорс-проект, и, скорее всего, со временем в нём появятся новые методы. Например, в данный момент открыта задача на добавление метода для проверки таблиц, может, именно вы её и решите.

Подробнее познакомиться с исходниками pdf-test, примерами использования и предложить свои нововведения можно, перейдя в репозиторий.

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

Спасибо за внимание!

Больше авторских материалов для разработчиков в тестировании от моих коллег читайте в соцсетях SimbirSoft – ВКонтакте и Telegram.