Есть отличная удаленная работа для php+codeception+jenkins+allure+docker спецов. 100% remote! Присоединиться к проекту

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


(Maxim) #1

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


Параллельный запуск тестов WebDriver + Maven + TestNG
(Александр Таранков) #2

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

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


(Maxim) #3

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

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

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

app = getApp();

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


(Александр Таранков) #4

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


(Maxim) #5

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

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

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

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

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


(Sergey Korol) #6

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

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

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

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


(Maxim) #7
  • Декомпозиция не помогла, видимо, 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 >();

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


(Sergey Korol) #8

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

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

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

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


(Maxim) #9

Тесты запускаются через грид, но прежде, чем их туда переносить, хочу настроить, чтобы локально работали, ведь отлаживать тоже придется.
По поводу инстансов браузера - эти настройки я не менял, по умолчанию вроде 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 именно так и пробовал - результат тот же.


(Sergey Korol) #10

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


(Maxim) #11

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


(Sergey Korol) #12

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

  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.


(Maxim) #13

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