Запуск теста одновременно на нескольких нодах


(Stalexmr) #1

Здравствуйте!

Делаю большие шаги на пути автоматизации с помощью вашего ресурса.

Большое спасибо за помощь!

Возник следующий вопрос:

  1. У меня есть три ноды. На каждой из них стоит по одной версии IE (11/10/9)
  2. Есть простенький тест. Версию браузера, в котором хочу чтобы тест выполнился, выбираю с помощью этого:

    DesiredCapabilities capability = DesiredCapabilities.internetExplorer();
    capability.setBrowserName("internet explorer");
    capability.setVersion("11");

  3. Если хочу выполнить тест в другой версии браузера, просто изменяю capability.setVersion("");

  4. Все работает отлично. Нода с нужной версией браузера находится. Все замечательно.

  5. Вопрос такой: если я хочу, чтобы мой один тест запустился один раз, одновременно на трех нодах, и именно в браузерах IE (на нодах стоят и другие браузеры), что мне нужно сделать для этого?

Догадываюсь, что возможно нужно что-то добавить/править в testng.xml

Сейчас он выглядит так:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite" parallel="none">
  <test name="Test">
    <classes>
      <class name="com.redmine.tests.LoginTest"/>
      <class name="com.redmine.tests.BasicTestCase"/>
    </classes>
  </test>
</suite>

Пробовал изменять xml, но ни к каким результатам положительным пока не пришел.

Читал тему: http://automated-testing.info/t/parallelnyj-zapusk-neskolkih-testov-na-odnoj-mashine/4543
но ответа там не нашел.

Буду рад любой помощи.


(Sergey Korol) #2

Если вы хотите запускать один и тот же набор тестов на разных конфигурациях в параллели, можно явно указывать нужную в xml, а затем вытягивать ее из контекста. Обязательно почитайте по поводу parallel и thread-count атрибутах для того, чтобы определиться с тем, как именно вы хотите параллелить ваши тесты.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="IE test suite" parallel="tests" thread-count="3">
<test name="IE 9">
    <parameter name="browser" value="internet explorer"/>
    <parameter name="version" value="9"/>
    <parameter name="platform" value="WINDOWS"/>
    <classes>
        <class name="package.and.class.name">
            <methods>
                <include name="methodName"/>
            </methods>
        </class>
    </classes>
</test>

<test name="IE 10">
    <parameter name="browser" value="internet explorer"/>
    <parameter name="version" value="10"/>
    <parameter name="platform" value="WINDOWS"/>
    <classes>
        <class name="package.and.class.name">
            <methods>
                <include name="methodName"/>
            </methods>
        </class>
    </classes>
</test>

<test name="IE 11">
    <parameter name="browser" value="internet explorer"/>
    <parameter name="version" value="11"/>
    <parameter name="platform" value="WINDOWS"/>
    <classes>
        <class name="package.and.class.name">
            <methods>
                <include name="methodName"/>
            </methods>
        </class>
    </classes>
</test>

ITestContext позволит вам получить доступ ко всем параметрам xml. Если захотите опуститься до параметризации на уровне метода, то вам еще может пригодиться Method:

@BeforeMethod
public void setUp(final ITestContext context, final Method method) {
    // get your parameters from context
}

(Stalexmr) #3

Здравствуйте!

Сделал следующим образом:

XML:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="IE test suite" parallel="tests" thread-count="1">

<test name="IE10">
    <parameter name="browser" value="IE10"/>
    <packages>
      <package name="com.redmine.tests"/>
    </packages>   
</test>

<test name="IE11">
    <parameter name="browser" value="IE11"/>
    <packages>
      <package name="com.redmine.tests"/>
    </packages>  
</test>

</suite>

BasicTestCase:

public class BasicTestCase {

	protected static WebDriver driver;

	@Parameters({"browser"})
	@BeforeTest

 public static WebDriver getDriverInstance (String browser)  
		  {  
		     WebDriver driver=null;  
		     if(browser.equals("IE10"))  
		     {  
		    	DesiredCapabilities capabilityIE10 = DesiredCapabilities.internetExplorer();
				capabilityIE10.setBrowserName("internet explorer"); 
				capabilityIE10.setVersion("10");
				try {

			driver = new RemoteWebDriver(new URL("http://192.168.1.148:4444/wd/hub"), capabilityIE10);

			} catch (MalformedURLException e) {

				e.printStackTrace();
			}
				driver.manage().timeouts().implicitlyWait(Long.parseLong(ConfigProperties.getProperty("imp.wait")), TimeUnit.SECONDS);  
		     }  
		     else if(browser.equals("IE11"))  
		     {  
		    	DesiredCapabilities capabilityIE11 = DesiredCapabilities.internetExplorer();
				capabilityIE11.setBrowserName("internet explorer"); 
				capabilityIE11.setVersion("11");
				try {

			driver = new RemoteWebDriver(new URL("http://192.168.1.148:4444/wd/hub"), capabilityIE11);

			} catch (MalformedURLException e) {

				e.printStackTrace();
			}
				driver.manage().timeouts().implicitlyWait(Long.parseLong(ConfigProperties.getProperty("imp.wait")), TimeUnit.SECONDS);		
		     }  
		     return driver;  
		  }  

		@AfterTest
		public void tearDown() throws Exception {			
			if (driver != null)
			driver.quit();

		}
}

LoginTest

public class LoginTest extends BasicTestCase {


	private LoginPage loginPage = PageFactory.initElements(driver, LoginPage.class);
	private HomePage homePage;

	@Test
	public void testUntitled() throws Exception {

		loginPage.open();
		homePage = loginPage.loginAs(admin);
		AssertJUnit.assertTrue(homePage.isLoggedIn());
		homePage.logout();
		AssertJUnit.assertTrue(homePage.isLoggedOut());

	}

}

Не знаю, следовало ли сюда пихать весь код.

Но проблема в следующем:

На каждой виртуалке (в данном случае их две) запускается по одному разу тест, но останавливается на запуске браузера IE.
В браузере следующий текст:

This is the initial start page for the WebDriver server.

В консоли ноды следующее:

Listening on port 10467
02:01:38.050 INFO - Done: [new session: Capabilities [{platform=WINDOWS, ensureC
leanSession=true, browserName=internet explorer, version=11}]]
02:01:38.060 INFO - Executing: [implicitly wait: 10000])
02:01:38.070 INFO - Done: [implicitly wait: 10000]

Через пять минут браузер закрывается:

02:06:38.176 INFO - Executing: [delete session: cf972031-dc0e-468a-be3a-9621af3d
33e6])
02:06:39.418 INFO - Done: [delete session: cf972031-dc0e-468a-be3a-9621af3d33e6]

Читал про Protected Mode. На всех зонах проставил Enable Protected Mode - не помогло.

Сам тест запускаю как Run As -> Run Configuration -> TestNG -> В поле Suite выбираю файл xml testng.xml

В чем может быть проблема?


(Stalexmr) #4

В Google Chrome схожая ситуация. Открывается браузер и все. Видимо где-то ошибаюсь и тест не получает URL по которому нужно перейти.


(Sergey Korol) #5

Проблем сразу несколько:
1) Я не зря писал о том, чтобы вы обязательно почитали по поводу parallel и thread-count атрибутах. В итоге, в вашем xml thread-count = 1. Каким образом вы собираетесь параллелить тесты в 1 поток?
2) Не знаю, откуда все нахватались static'ов (наверное самая распространенная ошибка), но прежде чем декларировать любую переменную с модификатором static, необходимо почитать из java basics, что это, и с чем его едят. В двух словах, статическая переменная является переменной класса, а не его инстансов. В случае, когда вы пытаетесь распараллелить ваши тесты, сессия драйвера будет шариться между всеми потоками. В итоге, кто последний успел, того и тапки. А остальные потоки будут "покуривать" в сторонке. Если архитектура построена на статике, то позаботьтесь о потокобезопасности ваших переменных. Достигается это при помощи ThreadLocal контейнера. Второй вариант - отказ от статики и использование наследования. В этом случае testng гарантирует вам безопасность ваших переменных, ибо каждый драйвер будет жить в своем собственном потоке.
3) Если вы инициализируете драйвер в BeforeTest, а убиваете в AfterTest, то не удивляйтесь тому, что весь ваш пакет тестов будет запускаться в единственном экземпляре браузера. Что само по себе накладывает доп. проблемы учета прекондишенов / посткондишенов, борьбу с куками и кешом.
4) Код инициализатора браузера - сплошной копипаст. Проанализируте написанное: ваш if / else делает одно и то же, отличие лишь в версии браузера. Задумайтесь о выделении схожих участков в независимый метод. Плюс ко всему, зачем собственно вы возвращаете драйвер из getDriverInstance?


(Stalexmr) #6

Здравствуйте!

Спасибо за развернутый ответ.
Решил воспользоваться ThreadLocal.
У меня опять вопрос. Точнее ошибка:

BasicTestCase

public class BasicTestCase {

protected ThreadLocal<RemoteWebDriver> threadDriver = null;

@BeforeMethod
public void setUp() throws MalformedURLException {

    threadDriver = new ThreadLocal<RemoteWebDriver>();
    DesiredCapabilities dc = new DesiredCapabilities();
    dc.setBrowserName(DesiredCapabilities.chrome().getBrowserName());
    threadDriver.set(new RemoteWebDriver(new URL("http://192.168.1.129:4444/wd/hub"), dc));
}

public WebDriver getWebDriver() {
	return threadDriver.get();   	
}

@AfterMethod
public void closeBrowser() {
    getWebDriver().quit();

  }
}

test

public class test extends BasicTestCase{

	private LoginPage loginPage = PageFactory.initElements(getWebDriver(), LoginPage.class);

	@Test
	public void test() throws Exception {

		loginPage.open();		
	}		
}

LoginPage

public class LoginPage extends Page {

	public LoginPage(WebDriver driver) {
		super(driver);
	}

	@Override
	public void open() {

		driver.get(ConfigProperties.getProperty("login.url"));		
	}
}

Page

public abstract class Page {

protected WebDriver driver;

public Page(WebDriver driver) {

	this.driver = driver;

}

public abstract void open();		

}

Ошибка:

org.testng.TestNGException:
Cannot instantiate class test

Мне кажется ошибка при инициализации объектов LoginPage:

private LoginPage loginPage = PageFactory.initElements(getWebDriver(), LoginPage.class);

Никак не могу понять в чем дело. Логика кажется понятной, но в чем ошибка.


(Sergey Korol) #7

1) Зачем вам ThreadLocal в нестатическом контексте при использовании наследования?
2) Сразу читаем про инкапсуляцию и модификаторы доступа. protected / не final ThreadLocal контейнер с public геттером без проверки на null - однозначно принесет вам очень много неприятностей.
3) Посмотрите примеры работы с DesiredCapabilities.
4) Обзовите нормально тестовый класс и тестовый метод. Не привыкайте писать как-попало.
5) Как вы подключаете xml? Чем запускаете тесты - maven / testng run configuration?


(Stalexmr) #8

Взял готовое решение, которое работало.
http://blog.wedoqa.com/2013/07/how-to-run-parallel-tests-with-selenium-webdriver-and-testng-2/Постарался применять к своему тесту, где использую PageFactory. Не получилось.
Запускал как "Run as TestNG" кликая по xml`ику.


(Sergey Korol) #9

Не стоит доверять всем ресурсам подряд. Парень явно нахватался всему понемногу без особого понимания - лишь бы работало.

Так какой собственно ваш последний вариант xml? Вообще говоря, проще будет, если вы зашарите полный код где-нибудь на гитхабе, например. Будет легче найти проблему.


(Stalexmr) #10

Здравствуйте. Выложил весь проект на google drive.
https://docs.google.com/file/d/0B3pPpDbsD_XTalBzU1I3Vkd4N00/edit?pli=1
Наверное запутал уж всех.
1. Тесты запускаю с помощью testng, кликая по xml
2. Все тесты (их пять и они лежат в папке payture.LoginPage.tests) одинаковы.
3. На данный момент инициализации проходит внутри каждого теста.

	@Test
	public void testLoginPageTC5() throws Exception {

		LoginPage loginPage = PageFactory.initElements(LocalDriverManager.getDriver(), LoginPage.class);
		loginPage.open();
		System.out.println(LocalDriverManager.getDriver());
		loginPage.loginAs(admin);

	}

}

При этом все запускается.

Никак не могу понять, если инициализацию проводить в том же методе, но за пределами блока @Test, то инициализация не проходит.

То есть, если делать так:

public class LoginPageTC1 {

	public UserData admin = new UserData("123", "test");

	private LoginPage loginPage = PageFactory.initElements(LocalDriverManager.getDriver(), LoginPage.class);


	@Test
	public void testLoginPageTC5() throws Exception {

//		LoginPage loginPage = PageFactory.initElements(LocalDriverManager.getDriver(), LoginPage.class);
		loginPage.open();
		System.out.println(LocalDriverManager.getDriver());
		loginPage.loginAs(admin);

	}

}

То выдает ошибку:

java.lang.NullPointerException
	at org.openqa.selenium.support.pagefactory.DefaultElementLocator.findElement(DefaultElementLocator.java:59)
	at org.openqa.selenium.support.pagefactory.internal.LocatingElementHandler.invoke(LocatingElementHandler.java:34)
	at com.sun.proxy.$Proxy6.clear(Unknown Source)
	at payture.page.Page.type(Page.java:20)
	at payture.page.LoginPage.loginAs(LoginPage.java:31)
	at payture.LoginPage.tests.LoginPageTC2.testLoginPageTC5(LoginPageTC2.java:25)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
	at org.testng.internal.Invoker.invokeMethod(Invoker.java:714)
	at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
	at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)

Собственно вопросы:
1. Почему так может происходить?
2. В каких еще местах я допускаю ошибки, с точки зрения проектирования теста


(vmaximv) #11

А что собственно не понятно? У вас браузер создается в beforeInvocation.
Когда вы выносите инит класса из метода - он "хватает" null.


(Sergey Korol) #12

Правильно выше сказали: beforeInvocation выполнится после прохода по филдам теста. Т.е. вы в initElements передаете null driver, который собственно делает последующие обращения к веб элементам бесполезными. Фабрику можно дернуть прямо из конструктора пейдж обджекта.

Техническая часть - все, что касается драйвера, - вообще не должна присутствовать в тестах. Вы из пейдж обджектов в мейне ссылаетесь на драйвер, который управляется из тестов. У меня IntelliJ сразу ругнулась на то, что невозможно найти LocalDriverManager.

Зачем закоментировали maven-surefire-plugin? Запускайте тесты через него. Листенер также можно прямо в нем подключить.

В Page классе, не смотря на его крохотный размер, много технического брака.

Отформатируйте код. Ctrl + Alt + L в IntelliJ, а то аж глаз режет.

Не выкладывайте лишних файлов в паблик. Только сорсы, конфиги, poms. Всякие IDE-specific, VCS файлы никому в помине не нужны.