@DataSupplier Как поменять runInParallel значение в runtime

Дано
Есть простой тестовый класс

public class LoginIT {
    @DataSupplier(flatMap = true, runInParallel = true)
    public List<UserCredential> accounts() {
        List<UserCredential> accounts = new ArrayList<>();
        accounts.add(UserCredential.builder().username("root").password("root").build());
        accounts.add(UserCredential.builder().username("admin").password("admin").build());
        return accounts;
    }
   
    @Test(dataProvider = "accounts")
    public void can_login_with_valid_credentials(final UserCredential account) {
        final LoginPage loginPage = new LoginPage();
        final AppliancePageObject page = loginPage.login(account);

        assertInstanceOf(page, TopPage.class, "Cannot log in with valid credentials");
    }
}

Вопрос
Можно ли поменять runInParallel значение (с true на false) в runtime если из вне до начала сьюта приходит параметр хочу я запускать DataSupplier в параллель или нет

<suite name="Debug Suite" verbose="10" parallel="methods" data-provider-thread-count="1">

Это имели ввиду?

К сожалению, нет.
Это в TestNG xml статически. Я ищу как это сделать в runtime в зависимости от приходящего из вне параметра.

data-provider-thread-count=“1” - указывает сколько потоков использовать для DataSupplier/DataProvider. Если поставить 4 но при этом runInParallel = false то параллельности не будет.

Использовать IAlterSuiteListener, который позволяет менять параметры сьюта, сам сьют.

1 лайк

задача абстрактная
можно же просто преписывать в джекнкинсе перед билддом файл xml с нужными параметрами

@DataSupplier(flatMap = true, runInParallel = true)
+

<suite name="Debug Suite" verbose="10" parallel="methods" data-provider-thread-count="1">

== runInParallel = false
поток же 1

перед билдом с билд скрипте указываете после скачки репозитория перетереть хмл с параметрами

<suite name="Debug Suite" verbose="10" parallel="methods" data-provider-thread-count="10">

получаете треды

а вообще не понятна цель всех этих манипуляций - зачем это?

2 лайка

Это не совсем рантайм…
я так понял нужно на лету менять перед билдом

Понял, что слишком абстрактный получился вопрос.
Вот полная детализация проблемы.

Бекграунд
Занимаюсь параллелизацией тестов. В тестах активно использую TestNG DataProvider/DataSupplier. Параллельность тестов настроена на уровне методов. Тесты, для которых указан DataSupplier тоже параллелятся с помощью атрибута runInParallel=true. Браузеры для потоков храню в переменной ThreadLocal<>. Билды запускаются на TeamCity. Одним из параметров билда является опция запуска параллельно или нет. Эта опция передаётся в Maven как System property.

Проблематика
Зависимо от упомянутой выше опции в @BeforeSuite выполняется следующий код

        if(isParallel()) {
            testContext.getSuite().getXmlSuite().setParallel(XmlSuite.ParallelMode.METHODS);
            testContext.getSuite().getXmlSuite().setThreadCount(TestConfig.getThreadCount());
            testContext.getSuite().getXmlSuite().setDataProviderThreadCount(TestConfig.getThreadCount());
        }
        else {
            testContext.getSuite().getXmlSuite().setParallel(XmlSuite.ParallelMode.NONE);
            testContext.getSuite().getXmlSuite().setThreadCount(1);
            testContext.getSuite().getXmlSuite().setDataProviderThreadCount(1);
        }

Браузеры запускаю в @BeforeMethod

 public void beforeTestMethod() {
        WebBrowserManager.startWebBrowser();
    }

Класс, отвечающий за управление браузерами (PS. WebBrowser - это врапер над WebDriver для удобства работы)

public class WebBrowserManager {
    private final static ThreadLocal<WebBrowser> webBrowser = new ThreadLocal<>();

    public static WebBrowser getWebBrowser() {
        return webBrowser.get();
    }

    public static void startWebBrowser() {
        if (getWebBrowser() == null) {
            final WebBrowser instance = WebBrowserFactory.createInstance(TestConfig.getBrowser());
            webBrowser.set(instance);
        }
    }

    public static void closeWebBrowser() {
        final WebBrowser webBrowser = getWebBrowser();
        if(webBrowser != null) {
            webBrowser.close();
        }

        webBrowser.remove();
    }
}

Теперь собственно о проблеме.
Если с TeamCity приходит команда запускать тесты НЕ параллельно, то целесообразно не открывать для каждого теста новый браузер, а открыть его в первом тесте и переиспользовать в остальных.
Предполагалось, что это решится использование ThreadLocal переменной, которая для потока будет хранить браузер и отдавать его по требованию. Но вот, что обнаружилось.
Если Suite.ParallelMode = NONE (т.е. не параллелить тесты), то все @Test и @Before и @After выполняются в одном потоке. Исключением из этого стало использование runInParallel=true для DataSupplier. В этом случае для такого теста используется отдельный поток, что приводит к запуску нового браузера. При runInParallel=false всё ок и тесты с DataSupplier выполняются в том же потоке и в том же браузере, что и остальные.

DataSupplier -ров использую много по всему коду тестов. Как им всем в @BeforeSuite проставить runInParallel=false не нашёл.

Предположительные пути решения проблемы
Вариант 1:
В классе WebBrowserManager вводить дополнительную переменную, в которой будет хранится и отдаваться браузер в случае НЕ параллельного запуска тестов. Переменная будет одна для всех потоков. Как по мне выглядит немного кастыльно
Вариант 2:
Искать пути переопределения runInParallel.

Остановился пока на 2-ом, но решения не нашёл.

Попробовал использовать IAlterSuiteListener. Что-то не получается.

Создал класс.
Заоверрайдил метод.
Поставил брек поинт на методе.
Добавил класс как @Listeners({AlterSuiteListener.class}) к тестовом у классу.
Запускаю дебаг и ничего. Брек поинт не отработал.

Или я неправильно подключаю мою имплементацию IAlterSuiteListener к тестовому классу?

В какой момент я должен попадать в метод alter? До Suite? во время? после?

Также попробовал использовать IAnnotationTransformerInterceptor для DataSupplier.
Проблема аналогичная. Брек поинт для переопределённого метода не срабатывает. Подключал так как указано тут test-data-supplier

Из JavaDoc:
This listener can be added ONLY via the following two ways :

  1. listeners tag in a suite file.
  2. via Service loaders

Note: This listener will NOT be invoked if it is wired in via the @Listeners annotation.

Ответил тут: Не получается продебажить DataSupplier Interceptor в IntelliJ IDEA и подскажите как его правильно подключать - #3 от пользователя ArtOfLife

1 лайк

AlterSuite, AnnotationTransformers не могут быть залинкованы через анноттацию @Listeners.

пруф: IAnnotationTransformer doesn't run when specified as an @Listeners · Issue #446 · testng-team/testng · GitHub

Т.е. их надо обьявлять в физической xml’ине…

1 лайк