Обработка ошибок Java + Junit5 + AspectJ + Slf4j + Playwright
Введение
Аспе́ктно-ориенти́рованное программи́рование (АОП) — парадигма программирования, основанная на идее разделения функциональности для улучшения разбиения программы на модули.
Методология АОП была предложена группой инженеров исследовательского центра Xerox PARC под руководством Грегора Кичалеса (Gregor Kiczales). Ими же было разработано аспектно-ориентированное расширение для языка Java, получившее название AspectJ — (2001 год).
Цель : Сделать отчеты в Allure доступными для понимая и быстрой оценки состояния продукта
Код будет написан намеренно максимально просто, чтобы было понятно как использовать аспекты . Вы можете и должны их применять, чтобы сохранить чистоту кода.
Создаем проект 'Gradle'
plugins {
id 'java'
id 'io.qameta.allure' version '2.11.2'
}
group 'ru.play'
version '1.0-SNAPSHOT'
configurations {
aspectConfig}
repositories {
mavenCentral()}
allure {
version = allureVersion
useJUnit5 {
version = allureVersion
}
report {
version.set(allureVersion) }
adapter {
aspectjWeaver.set(true)
frameworks {
junit5 {
adapterVersion.set(allureVersion)
}
}
}
}
dependencies {
testImplementation(
"org.junit.jupiter:junit-jupiter:$jupiterVersion",
"com.microsoft.playwright:playwright:$playwriteVersion",
"org.aspectj:aspectjrt:${aspectjweaverVersion}",
"com.fasterxml.jackson.core:jackson-databind:$jacksonVersion", ) testCompileOnly "org.projectlombok:lombok:${lombokVersion}"
implementation "org.slf4j:slf4j-api:${slf4jVersion}"
implementation "org.aspectj:aspectjweaver:${aspectjweaverVersion}"
aspectConfig "org.aspectj:aspectjweaver:${aspectjweaverVersion}"
}
test {
doFirst {
def weaver = configurations.runtimeClasspath.find { it.name.contains("aspectjweaver") }
}
useJUnitPlatform()
systemProperty 'junit.jupiter.extensions.autodetection.enabled', true
}
Далее у нас есть 3 тк, они намеренно сломаны, чтобы мы могли увидеть отчеты
import com.microsoft.playwright.Browser;
import com.microsoft.playwright.BrowserType;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.Playwright;
import com.microsoft.playwright.assertions.PlaywrightAssertions;
import com.microsoft.playwright.options.AriaRole;
import lombok.SneakyThrows;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class MySimpleTest {
Page page;
Browser browser;
Playwright playwright;
@BeforeEach
@SneakyThrows
void setUp() {
playwright = Playwright.create();
browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(false).setSlowMo(500));
page = browser.newPage();
page.setDefaultTimeout(15_000);
page.navigate("http://playwright.dev");
}
@AfterEach
void tearDown() {
browser.close();
playwright.close();
}
@Test
void test1() {
page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("Get Started")).click();
}
@Test
void test2() {
page.getByRole(AriaRole.BLOCKQUOTE, new Page.GetByRoleOptions().setName("G%ed")).click();
}
@Test
void test3() {
PlaywrightAssertions.assertThat(page.getByText("Не существующий текст")).isVisible();
}
}
Если тк не исправен или находит ошибку, в отчете сможет разобраться только инженер. Давайте посмотрим :


Исправим ! Напишем наш класс LogAspect в котором мы будем перехватывать все исключения и создавать свои кастомные отчеты
package ru.play;
import com.microsoft.playwright.PlaywrightException;
import io.qameta.allure.Allure;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
@Slf4j
public class LogAspect {
@Pointcut("within(MySimpleTest)")
void myFirstPointcut() {
}
@AfterThrowing(pointcut = "myFirstPointcut()", throwing = "error")
public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
String methodName = joinPoint.getSignature().getName();
String errorName = error.getMessage();
if (errorName != null) {
if (errorName.contains("Timeout")) {
Allure.addAttachment("Отчет ошибки", errorName);
throw new PlaywrightException("Вышло время ожидания элемента в методе %s".formatted(methodName));
}
if (errorName.contains("Locator expected to be visible")) {
Allure.addAttachment("Отчет ошибки", errorName);
throw new PlaywrightException("Не найден элемент в методе %s".formatted(methodName));
}
}
}
}
Подчеркну, что класс LogAspect намеренно создан максимально просто и использует if , дублирование и т.д . Когда вы будете делать свои аспекты , можете сделать кастомную ошибку, которая будет обрабатывать и выводить то, что вам необходимо.
В этом классе мы используем pointcut (точка среза) который будет перехватывать все методы класса с полным списком вы можете ознакомиться тут . Аннотация @AfterThrowing - это advice , он будет перехватывать все ошибки после выполнения метода . Ознакомится с advice (советами) можете тут.
Создаем дирректорию META-INF и в нем файл aop.xml пример
<aspectj>
<aspects>
<aspect name="ru.play.LogAspect"/>
</aspects>
</aspectj>
Запускаем наши тесты и снова смотрим отчет


Подведем итог
Отчеты стали более информативными для коллег, которым нужно узнать состояние целевого продукта без разбора технической части. Они сразу по описанию понимают, что случилось, а также можно прикрепить скриншот. Мы можем использовать также для логирования своих API , чтобы видеть какие есть ошибки в предусловии / постусловии взаимодействий .
Представим , что у нас 200 тк в каждом тк в среднем 10 шагов - это 2000 try/catch или супер класса обертки. Думаю, основная мысль ясна и надеюсь пригодится в работе. Спасибо =)