Selenium Grid, после метода threadDriver.get().close() драйвер null и в AfterSuite нет к нему доступа

Есть такой код:

package grid.driver;

import java.net.URL;

import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;

public class GridDriver {

	/* Browsers constants */
	public static final String CHROME = "chrome";
	public static final String FIREFOX = "firefox";
	
	//Remote Driver
	private RemoteWebDriver webDriver;

	public RemoteWebDriver getInstance(String browser) throws Exception {
			DesiredCapabilities capabilities = new DesiredCapabilities();
			capabilities.setJavascriptEnabled(true);
			
			//parallel
			if (CHROME.equals(browser)) {
				capabilities = DesiredCapabilities.chrome();
				capabilities.setBrowserName(DesiredCapabilities.chrome().getBrowserName());
				webDriver = new RemoteWebDriver(new URL("http://localhost:5556/wd/hub"), capabilities);
			} else if (FIREFOX.equals(browser)) {
				capabilities = DesiredCapabilities.firefox();
				capabilities.setBrowserName(DesiredCapabilities.firefox().getBrowserName());
		        webDriver = new RemoteWebDriver(new URL("http://localhost:5555/wd/hub"), capabilities);
			} else throw new RuntimeException("invalid browser setup");
		
		return webDriver;
	}
}

и клас с тестами:

package grid.selenium.test;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import grid.driver.GridDriver;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;

public class TestBaseGrid {

protected RemoteWebDriver rmtWebDriver;
protected ThreadLocal<RemoteWebDriver> threadDriver = new ThreadLocal<RemoteWebDriver>();;
protected GridDriver gridDriver;

@Parameters({ "browserName" })
@BeforeMethod
public void init(String browserName) throws Exception {
	gridDriver = new GridDriver();
	rmtWebDriver = gridDriver.getInstance(browserName);
	threadDriver.set(rmtWebDriver);
	getDriver().manage().timeouts()
			.implicitlyWait(30, TimeUnit.SECONDS);
}

@AfterMethod
public void reopenApp() throws Exception {
	getDriver().close();
}

@AfterSuite
public void reopenApp1() throws Exception {
	// HERE IS NULL !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	System.out.println(getDriver());
}

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

@Test
// frames example
public void simpleRemoteDriverTest1() {
	try {
		getDriver().get("http://www.lits.com.ua");
		getDriver().findElement(
				By.xpath("//a[contains(@class,'red-btn-small')]")).click();
		getDriver().switchTo().frame(
				getDriver().findElement(By.id("JotFormIFrame")));
		Assert.assertTrue(getDriver().findElement(By.id("input_4_area"))
				.getAttribute("placeholder").equals("067"));
	} catch (Exception e) {

	} finally {
		//getDriver().switchTo().defaultContent();
	}
}

@Test
// Select example
public void simpleRemoteDriverTest2() throws InterruptedException {
	getDriver().get("http://www.auto.ria.ua");
	Select autoType = new Select(getDriver().findElement(
			By.id("select_auto_used_bodystyle")));
	autoType.selectByIndex(5);

	Assert.assertTrue(autoType.getOptions().size() == 12);

	Thread.sleep(5000);
}

@Test
// Explicit Waiters examples
public void simpleRemoteDriverTest3() throws IOException,
		InterruptedException {
	getDriver().get("http://google.com.ua/");
	getDriver().findElement(By.id("gbqfq")).sendKeys("LQAS" + "\n");

	WebDriverWait wait = new WebDriverWait(getDriver(), 15);
	wait.until(ExpectedConditions.textToBePresentInElement(getDriver()
			.findElement(By.id("resultStats")), "000"));

	Assert.assertTrue(getDriver().findElements(By.xpath("//h3")).get(0)
			.getText().contains("Lviv"));
}

}

и xml для запуска:

<?xml version="1.0" encoding="UTF-8"?>
	<test name="SingleTestSuite1" parallel="methods" thread-count="2">
		<parameter name="browserName" value="chrome" />
		<classes>
			<class name="grid.selenium.test.TestBaseGrid" />
		</classes>
	</test>
	
	<test name="SingleTestSuite2" parallel="methods" thread-count="2">
		<parameter name="browserName" value="firefox" />
		<classes>
			<class name="grid.selenium.test.TestBaseGrid" />
		</classes>
	</test>

</tests>

ТАм где коммент “// HERE IS NULL !!!” драйвер с потока возвращаеться как null , сотвественно NullPointerException если хочешь что то делать с драйвером…

В чем проблема ?

А смысл юзать ThreadLocal в данном контенте без статика?
Ну да ладно.
А что вы хотите увидеть в AfterSuite, если parallel='method'?
После окончания метода ваш экземпляр объекта “самоубивается”.

в статике нет необходимости сейчас…так как етопройсто пример, тоесть паралельно нужно запускать сюти или тести ?

Ну как вам надо - так и запускайте. Просто нужно понимать лайфтайм экземпляра класса в зависимости от способа паралеллизации.

я так понял на каком уровне паралельно запущени тести, на таком уровне threadlocal инстанс и живет ?

В вашем случае protected ThreadLocal<RemoteWebDriver> threadDriver - это обычный филд и живет он ровно столько, сколько существует экземпляр класса его содержащий.
Еще раз: ThreadLocal в данном случае без static’а использовать нету смысла.

тогда статический threadLocal.get() будет везде возвращать поточний инстанс драйвера для каждого паралельного потока ?

Забудьте пока про ThreadLocal, подумайте когда и сколько живут ваши потоки. И почему попытка обращаться к ним в AfterSute, при parallel='method' обречена на неудачу.

ну сообственно об етом и бил вопрос…есть какая инфа почитать об етом ?
Если parallels=“method” убрать то tests только будут запускаться паралельно и threadLocal.get() все равно будет null.

Имхо .
или убрать паралельность или перенести закрывание драйвера в @AfterTest

о спасибо, то что нужно…я так понял уровень закрития драйвера должен бить тот же что и в параметре паралелизации…?

ваш метод под анотацией @Test заканчиваеться
после метода идет анотация @AfterMethod в ней у вас закрываеться движок .
дальше в пытаетесь вызвать уже закрытый движок в анотации @AfterSuite.
Посмотрите порядок выполнения анотаций .

да ето понятно, тоесть после close драйвер становиться null ?

С потоками все понятно, моя ошибка била в методе close () ))

тоесть после close драйвер становиться null ?

Не совсем. driver.close() закрывает окно браузер по текущему хендлу. Если это было единственное окно - сессия браузера уничтожается т.к. браузер закрыт. С браузером уже нельзя взаимодействовать, но можно работать с сервером: например его закрыть - driver.quit()

ну да да, в етой статье что я кинул так и написано…у каждого потока же свой хендл один в даном случае, по етому понятно почему оно так) спасибо