Как распараллелить работу тестов?

Добрый день.
Вопрос тот же, но касается скорей java.
Вот пример проекта https://github.com/maxslon/mamba.git, суть проблемы, как я понимаю, в том, чтобы ApplicationManager была для каждого метода своя. Пробовал объявлять ApplicationManager в каждом методе - работает, но как это сделать в одном месте, а не добавлять в каждый метод?

Например, сделать в классе TestBase метод getApp(), который будет возвращать новый экземпляр ApplicationManager так, как это сейчас сделано в методе setUp().

Или вопрос не в этом?

То есть в классе TestBase просто создать метод

public ApplicationManager getApp() {
	return new ApplicationManager();
}

и в методе setUp() делать вызов

app = getApp();

я правильно понимаю? Только чем это отличается от текущей реализации, когда new ApplicationManager(); объявляется в самом setUp()?

Разница в том, что экземпляр ApplicationManager должен передаваться в тест, а не храниться в поле класса. У тебя правильно сделано через @BeforeMethod, но не правильно, что результат сохраняется в поле класса. Таким образом у тебя каждый новый тест инициирует обновление этого поля.

Так если ApplicationManager не будет полем класса, то тестовые методы его не увидят. Каким образом я могу передать в тестовый метод переменную ApplicationManager, если она не глобальная? Она объявляется в классе TestBase, именно поэтому она доступна из тестовых классов TestClass1 и TestClass2.

Попробовал сделать так:
Добавил еще один класс LocalApplicationManager, в котором есть метод

public static ApplicationManager getApplicationManager() { return new ApplicationManager(); }

При этом в методе setUp() вызываю app = LocalApplicationManager.getApplicationManager();.

Работает так же.
Все изменения там же https://github.com/maxslon/mamba.git

А методы вообще скипаются или фейлятся? Что в логе пишет?

Проблема, которую вы описываете очень схожа с тем, когда вы пытаетесь использовать драйвер в статическом thread-unsafe контексте. Нет времени изучать ваш код. Но я бы вообще не рекомендовал миксовать статику с нестатикой в случае драйвера. Тут лучше либо ThreadLocal контейнер использовать, либо наследование.

Проведите эксперимент: вместо статик геттера используйте наследование для получения доступа к менеджеру. Хотя, если у вас нигде больше нет статики, то вряд ли это будет проблемой. Anyway, пробуйте…

Хотя, не исключено, что это может быть и баг TestNG. Попробуйте вначале провести декомпозицию и явно указать методы в testng.xml. Можете еще поиграться с типом параллелизации: поставить tests и разбить 4 теста на 4 блока в xml.

  • Декомпозиция не помогла, видимо, testng работает корректно.
    В консоли для двух оставшихся (незакрытых) браузеров выводится

INFO: Command failed to close cleanly. Destroying forcefully (v2). org.openqa.selenium.os.UnixProcess$SeleniumWatchDog@552856f7

и процесс не завершается, пока я не удалю из памяти подвисшие chromedriver.exe. После выводится результат - все тесты рухнули и у всех ошибки

org.openqa.selenium.remote.UnreachableBrowserException: Error communicating with the remote browser. It may have died.

Caused by: org.openqa.selenium.WebDriverException: java.net.ConnectException: Connection refused: connect
  • Попробовал наследование - public class TestBase extends ApplicationManager - не помогло.
    По прежнему команды всех тестовых методов внутри одного тестового класса сыпятся в один браузер.

  • А каким образом можно использовать ThreadLocal? Я пробовал для ApplicationManager создавать переменную

    private static ThreadLocal< ApplicationManager > app = new ThreadLocal< ApplicationManager >();

Но это тоже не помогло, все сыпется в один браузер.

Так вы грид используете или обычный ChromeDriver? Если грид, то покажите конфиг нода. Там должны быть указаны опции макс числа допустимых инстансов браузера и сессий.

Касательно ThreadLocal:

private static final ThreadLocal<ApplicationManager> manager = new ThreadLocal<>();

Так у вас каждый поток будет иметь свой инстанс менеджера и они гарантировано не пересекутся. Но как правило его используют только для драйвера.

Тесты запускаются через грид, но прежде, чем их туда переносить, хочу настроить, чтобы локально работали, ведь отлаживать тоже придется.
По поводу инстансов браузера - эти настройки я не менял, по умолчанию вроде 5, но у меня каждая нода - это отдельная виртуалка и на ней работает только 1 браузер в одном экземпляре.
Ноды запускаются командой без доп. настроек:
java -jar selenium-server-standalone-2.40.0.jar -role node -hub http://192.168.2.174:4444/grid/register -browser browserName=chrome,version=… -port 5561

ThreadLocal именно так и пробовал - результат тот же.

Ну тогда надо смотреть в код.

Вы уже писали, что у вас нет времени, тем не менее, если выпадет свободная минутка https://github.com/maxslon/mamba.git буду благодарен за указание на ошибку

Так, посмотрел код:

  1. Сразу делаем глобальный рефакторинг и реформатинг. Не привыкайте писать на скорую руку. Такой код сложно читать и поддерживать.

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

  3. Необходимо убрать все проектные файлы / скомпилированный код из репозитория. Заливайте только сорсы, конфиги и помы.

  4. Грузил проект в IntelliJ. При использовании diamond operators, получил ошибку компиляции со ссылкой на 1.5 сорс / таргет. Не знаю, как у вас эклипс настроен, но для людей, работающих в другой среде, лучше добавить в pom следующее:

     <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <configuration>
             <source>1.7</source>
             <target>1.7</target>
         </configuration>
     </plugin>
    
  5. Слишком не углублялся в код по причине п.1,2, но все ваши проблемы с параллелизацией, как я уже ранее замечал, решаются ThreadLocal контейнером для ApplicationManager. Возможно, вы не до конца разобрались с принципом его работы. В TestBase рефакторим код следующим образом:

    private static final ThreadLocal app = new ThreadLocal<>();

    public void setUp(org.testng.ITestContext ctx, @Optional String configFile) throws Exception {
    // …
    app.set(new LocalApplicationManager().getApplicationManager());
    getManager().setProperties(props);
    // …
    }

    public ApplicationManager getManager() {
    if (app.get() == null) {
    app.set(new LocalApplicationManager().getApplicationManager());
    }

     return app.get();
    

    }

  6. В тестах, вместо app дергаем getManager():

    public void test1() throws Exception {
    getManager().getAuthorizationHelper().openMambaUrl();
    getManager().getAuthorizationHelper().clickIhaveAnketa();
    getManager().getAuthorizationHelper().loginMamba(user1);
    getManager().getSearchHelper().confirmationSearchLinkOnMainPage();
    getManager().getAuthorizationHelper().logOut();
    }

  7. Проверяем креденшалы, ибо тесты падают, ввиду не прохождения авторизации. Попробовал первых 2 шага в 4 потока, прошло нормально.

You’re welcome.

2 лайка

Огромное спасибо!
Общую суть претензий понял, буду курить форумы и рефакторить тесты.