t.me/atinfo_chat Telegram группа по автоматизации тестирования

How to add console log to allure report after each step (Java/Selenium/JUnit/Cucumber)?

bdd
infrastructure
reporting
cucumber
java
selenium
allure
Теги: #<Tag:0x00007f21ddb2d558> #<Tag:0x00007f21ddb2d378> #<Tag:0x00007f21ddb2d198> #<Tag:0x00007f21ddb2cf90> #<Tag:0x00007f21ddb2cdb0> #<Tag:0x00007f21ddb2cc20> #<Tag:0x00007f21ddb2ca40>

(Albert) #1

The problem (Question) is that I need to add the console log after each test step to the allure report, well, or the entire output at the end of the test. Whatever tried … - in vain. Throw any ideas, thanks!


(Vladislav Abramov) #2

нормально с таким ником писать на русскоязычном форуме на английском

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


(Albert) #3

Спасибо, я учту, первый раз тут)
А не могли бы подсказать название библиотечки, не могу найти, либо если в качестве описания, то как это должно выглядеть?
Пробовал писать листенеры, ниче не работает что-то
Если есть под рукой пример записи лога и передачи параметром, буду очень благодарен, если поделитесь, спасибо.


(Vasiliy Rakshin) #4

Получить лог
LogEntries logEntries = driver.manage().logs().get(LogType.BROWSER);

Прикрепить что-либо к отчету:
https://docs.qameta.io/allure/#_attachments_3


(Albert) #5

Спасибо за совет, получаю следущее:
Allure.addAttachment("Console log: ", String.valueOf(webDriverActions.getDriver().manage().logs().get(LogType.BROWSER)));
После этой команды прикрепляет всего одну строку:
org.openqa.selenium.remote.RemoteLogs@160ad8eb
Команду записал в метод After, в чем может быть причина?


(Vasiliy Rakshin) #6

добавьте .getAll()


(Albert) #7

Теперь просто пустой массив [] возвращает(
Уверены, что лог браузера нужен? мне хотя бы весь вывод консоли прикрепить
Но в идеале нужно будет потом после каждого степа лог аттачить


(Vasiliy Rakshin) #8

Значит в консоли пусто.
Попробовал в FF проверить - получил ошибку в драйвере:
org.openqa.selenium.UnsupportedCommandException: POST /session/1113293e-105e-437b-935e-868656618cc3/log did not match a known command - создал тикет для geckodriver, т.к. для Хрома она работает, но не даёт тоже нужного результата - инфо-сообщения из консоли не попадают в лог. Даже если я ручками сеттю нужные уровни логов:

        LoggingPreferences logs = new LoggingPreferences(); 
        logs.enable(LogType.DRIVER, Level.ALL); 
        options.setCapability(CapabilityType.LOGGING_PREFS, logs);

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


(Vasiliy Rakshin) #9

На тикет для FF ответили - пока это не реализовано, т.к. в стандарте не описано что и как логгировать.
Да и для Хрома эта возможность тоже получается не очень задокументирована.


(Albert) #10

Понял, спасибо)
Буду искать библиотеки для связи с алюром или реализацию механизма логирования в AllureCucumber2Jvm и от этого пробовать.
Или искать как можно разрешить в Overview степов логи как на фото,


(Vasiliy Rakshin) #11

Ну, это легко, это можете добавить в конце теста, который завершился успешно:

Allure.getLifecycle().updateTestCase((t) -> { 
    t.setStatusDetails( t.getStatusDetails().setMessage("blablablabl"));
});


(Desalvo Dimon) #12

Всем привет, вот что использую я.

  1. Необходим листенер LogCatchListener extends TestListenerAdapter:
    где в beforeConfiguration( ITestResult tr)
    в котором вызывается класс MultithreadedConsoleOutputCatcher.startCatch();

  2. Сам класс: MultithreadedConsoleOutputCatcher

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.HashMap;

public class MultithreadedConsoleOutputCatcher {

    private static PrintStream originalStream;
    private static OutputStreamRouter router;

    public static synchronized void startCatch() {
        if (router == null) {
            originalStream = System.out;
            router = new OutputStreamRouter(originalStream);
            System.setOut(new PrintStream(router));
        }
        router.registryThread(Thread.currentThread());
    }

    public static String getContent() {
        return getContent(true);
    }

    public static synchronized String getContent(boolean clear) {
        if (router == null) {
            throw new RuntimeException();
        }
        return router.fetchThreadContent(Thread.currentThread(), clear);
    }

    public static synchronized void stopCatch() {
        if (router == null) {
            throw new RuntimeException();
        }
        router.unregistryThread(Thread.currentThread());
        if (router.getActiveRoutes() == 0) {
            System.setOut(originalStream);
            originalStream = null;
            router = null;
        }
    }

    static class OutputStreamRouter extends OutputStream {

        private HashMap<Thread, ByteArrayOutputStream> loggerStreams = new HashMap<>();
        private OutputStream original;

        public OutputStreamRouter(OutputStream original) {
            this.original = original;
        }

        public void registryThread(Thread thread) {
            if (loggerStreams.containsKey(thread)) throw new RuntimeException();
            loggerStreams.put(thread, new ByteArrayOutputStream(4096));
        }

        public void unregistryThread(Thread thread) {
            if (!loggerStreams.containsKey(thread)) throw new RuntimeException();
            loggerStreams.remove(thread);
        }

        public int getActiveRoutes() {
            return loggerStreams.size();
        }

        public String fetchThreadContent(Thread thread, boolean clear) {
            if (!loggerStreams.containsKey(thread)) throw new RuntimeException();
            String result = loggerStreams.get(thread).toString();
            if (clear) {
                loggerStreams.get(thread).reset();
            }
            return result;
        }

        @Override
        public synchronized void write(int b) throws IOException {
            original.write(b);
            if (loggerStreams.containsKey(Thread.currentThread())) {
                loggerStreams.get(Thread.currentThread()).write(b);
            }
        }

        @Override
        public synchronized void flush() throws IOException {
            original.flush();
        }

        @Override
        public synchronized void close() throws IOException {
            original.close();
        }
    }

}

  1. в том же листенере где вызывается start: создаем метод
@SuppressWarnings("UnusedReturnValue")
    @Attachment(value = "Test Log", type = "text/plain")
    public String stopCatch() {
        String result = MultithreadedConsoleOutputCatcher.getContent();
        MultithreadedConsoleOutputCatcher.stopCatch();
        return result;
    }

и вызываем stopCatch() в методах:

  • onConfigurationSkip
  • onConfigurationFailure
  • onTestFailure
  • onTestSuccess
  • onTestSkipped

В итоге в отчете это будет выглядеть вот так:


Вывод лога output в отчет Allure+Specflow 2.4
(Vasiliy Rakshin) #13

Неплохо. Но я думал, что идёт речь о консоли браузера.


(Albert) #14

Спасибо большое, но я вас наверное запутал, прошу прощения.
У меня кукумберовские тесты ранятся через junit же, а этот адаптер от testng, поэтому мне нужно написать листенер для junit(


(Vasiliy Rakshin) #15

1 пункт делается по аналогии с примером из https://junit.org/junit5/docs/current/user-guide/#extensions-lifecycle-callbacks-before-after-execution
И вы так и не уточнили какой именно лог нужен.


(Albert) #16

В идеальном варианте мне нужно, чтобы к каждому степу алюр репорта прикреплялся лог(все что попало в консоль (System.out)) от его начала (степа) до его конца. Или хотя бы приаттачить весь лог консоли в конец теста, надеюсь понятно объяснил :slight_smile:


(Desalvo Dimon) #17

То что я расписал, это для testng (забыл уточнить).
Он и перехватывает все что кладется в system.out, а точнее все что формирует log4j (я использую это).
В данном случае вы можете взять этот же класс, но использовать его в листенере junit


(Артем Борсук) #18

Что-то вы слишком заморачиваетсь

public class BrowserLogs {
    private static List logList;

    private static List getConsoleLog() {
        return Selenide.getWebDriverLogs(LogType.BROWSER);
    }

    public static void checkLogs() {
        logList = getConsoleLog();
        if (logList.size() > 0)
            addConsoleLogToReport();
    }

    @Attachment(value = "Browser console log", type = "text/plain")
    private static String addConsoleLogToReport() {
        StringBuilder sb = new StringBuilder();
        for (Object line : logList) {
            sb.append(line);
            sb.append("\t");
        }
        return sb.toString();
    }
}

Вызываем метод CheckLogs в конце теста или где там вам надо


(Vasiliy Rakshin) #19

Артём, а вы плохо читаете что именно хотят. Притом, такое решение .getWebDriverLogs(LogType.BROWSER) уже давалось.