Ситуация - есть лиснер extends TestListenerAdapter
, в котором драйвер получаю из контекста и использую его для скриншота по фейлу теста.
@Override
public void onTestFailure(ITestResult result) {
driver = (WebDriver) result.getTestContext().getAttribute("driver");
screenshoter = new Screenshoter(driver);
screenshoter.takeShot();
}
Есть базовый класс от которого наследуются все тестовые классы и там в afterClass()
вызывается метод который убивает драйвер.
Так вот, если фейлится последний тест, то метод onTestFailure
не может отработать и хватает нул поинтер так как драйвера уже нету, его убило афтеркласом, соответственно в контексте пусто.
Что посоветуете?
joemast
(Александр Таранков)
April 30, 2015, 7:30am
#2
Не уверен, что @AfterClass
- это правильная аннотация для прибивания драйвера. Я её не использовал, но из описания похоже, что метод, помеченный этой аннотацией, будет вызываться после последнего теста в каждом классе. Вряд ли это ожидаемое поведение?
ArtOfLife
(Sergey Korol)
April 30, 2015, 9:26am
#3
Покажите, как сетите / убиваете драйвер + сам exception. @AfterClass
должен вызываться после onTestFailure
. Ради интереса, подключите логирование и посмотрите последовательность вызовов.
Кстати, какого рода фейл возникает? Assert или от драйвера? Как обрабатываете?
Ну в данном случает я паралелю тесты именно по классам, потому посчитал логичным убивать драйвер в афтерклас методе
Это класс отвечающий за создание драйвера
public class WebDriverStorage {
private static final Logger log = LoggerFactory.getLogger(WebDriverStorage.class);
private WebDriver driver;
private String DRIVER_SYS_PROPERTY = BuildPropertyGetter.DRIVER.getValue();
private String HUB_SYS_PROPERTY = BuildPropertyGetter.GRID_HUB.getValue();
private Map<WebDriver, String> storage = new HashMap<>();
public WebDriverStorage() {
log.debug("WebDriverStorage is created for " + Thread.currentThread().getName() + " " + WebDriverStorage.
public WebDriver getDriver() {
return driverFactoryMechanism(createCapabilities(), getHub());
}
public WebDriver getDriver(DriverModification modification) {
return driverFactoryMechanism(createModifiedCapabilities(modification), getHub());
}
public WebDriver getCurrentDriver() {
return driver;
}
public void dissmissDriver(WebDriver driver) {
if (storage.get(driver) == null) {
throw new Error("Driver is not owned by the factory: " + driver);
}
if (driver != this.driver) {
throw new Error("Driver does not belong to the current thread: " + driver);
}
driver.quit();
storage.remove(driver);
}
public void dissmissAllThreadDrivers() {
log.debug("Before removal storage contains such drivers: " + storage);
String threadKey = Thread.currentThread().getName() + "-" + Thread.currentThread().getId()+ "-StorageKey-";
new HashSet<String>(storage.values()).stream().filter(value -> value.contains(threadKey)).forEach(value -> {
storage.keySet().stream().filter(dr -> storage.get(dr).equals(value)).forEach(WebDriver::quit);
storage.values().remove(value);
});
log.debug("Now storage contains such drivers: " + storage);
}
public void dismissAllDrivers() {
log.debug("Before removal storage contains such drivers: " + storage);
if (storage != null) {
for (WebDriver driver : new HashSet<WebDriver>(storage.keySet())) {
driver.quit();
storage.remove(driver);
}
}
log.debug("Now storage contains such drivers: " + storage);
}
public void setDefaultTimeOut(int pageLoadTimeout, int implicitlyWait, int setScriptTimeout) {
driver.manage().timeouts().implicitlyWait(implicitlyWait, TimeUnit.SECONDS);
driver.manage().timeouts().pageLoadTimeout(pageLoadTimeout, TimeUnit.SECONDS);
driver.manage().timeouts().setScriptTimeout(setScriptTimeout, TimeUnit.SECONDS);
}
private DesiredCapabilities createCapabilities() {
DesiredCapabilities capabilities = null;
if (DRIVER_SYS_PROPERTY != null) {
if (DRIVER_SYS_PROPERTY.equals(DriverType.FIREFOX.getValue()))
capabilities = DesiredCapabilities.firefox();
if (DRIVER_SYS_PROPERTY.equals(DriverType.CHROME.getValue()))
capabilities = DesiredCapabilities.chrome();
if (DRIVER_SYS_PROPERTY.equals(DriverType.SAFARI.getValue()))
capabilities = DesiredCapabilities.safari();
if (DRIVER_SYS_PROPERTY.equals(DriverType.OPERA.getValue()))
capabilities = DesiredCapabilities.operaBlink();
if (DRIVER_SYS_PROPERTY.equals(DriverType.IEXPLORER.getValue()))
capabilities = DesiredCapabilities.internetExplorer();
if (DRIVER_SYS_PROPERTY.equals(DriverType.HTMLUNIT_WITH_JS.getValue()))
capabilities = DesiredCapabilities.htmlUnitWithJs();
if (DRIVER_SYS_PROPERTY.equals(DriverType.PHANTOMJS.getValue()))
capabilities = DesiredCapabilities.phantomjs();
} else {
capabilities = DesiredCapabilities.firefox();
}
return capabilities;
}
private Capabilities createModifiedCapabilities(DriverModification modification) {
DesiredCapabilities capabilities = createCapabilities();
String browserType = capabilities.getBrowserName();
switch (modification) {
case BROWSERMOBPROXY:
BMProxy bmProxy = BMProxy.INSTANCE;
bmProxy.init();
capabilities.setCapability(CapabilityType.PROXY, bmProxy.getProxy());
case USERAGENT:
if (browserType.equals(BrowserType.FIREFOX)) {
FirefoxProfile firefoxProfile = new FirefoxProfile();
firefoxProfile.setPreference("general.useragent.override", "userAgent");
capabilities.setCapability(FirefoxDriver.PROFILE, firefoxProfile);
}
else if (browserType.equals(BrowserType.CHROME)) {
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.addArguments("--user-agent=" + "userAgent");
capabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions);
}
}
return capabilities;
}
private WebDriver driverFactoryMechanism(Capabilities capabilities, URL hub) {
String newKey = createKey(capabilities, hub);
if (driver == null) {
createDriver(capabilities, hub);
} else {
String key = storage.get(driver);
if (key == null) {
createDriver(capabilities, hub);
} else {
if (!newKey.equals(key)) {
dissmissDriver(driver);
createDriver(capabilities, hub);
} else {
try {
driver.getCurrentUrl();
} catch (Throwable throwable) {
createDriver(capabilities, hub);
}
}
}
}
return driver;
}
private void createDriver(Capabilities capabilities, URL hub) {
String newKey = createKey(capabilities, hub);
if (hub == null) {
driver = createDriverLocal(capabilities);
storage.put(driver, newKey);
} else {
driver = createDriverRemote(capabilities, hub);
storage.put(driver, newKey);
}
}
private WebDriver createDriverLocal(Capabilities capabilities) {
String browserType = capabilities.getBrowserName();
if (browserType.equals(BrowserType.FIREFOX))
driver = new FirefoxDriver(capabilities);
if (browserType.equals(BrowserType.IE))
driver = new InternetExplorerDriver(capabilities);
if (browserType.equals(BrowserType.CHROME))
driver = new ChromeDriver(capabilities);
if (browserType.equals(BrowserType.SAFARI))
driver = new SafariDriver(capabilities);
if (browserType.equals(BrowserType.PHANTOMJS))
driver = new PhantomJSDriver(capabilities);
if (browserType.equals(BrowserType.HTMLUNIT))
driver = new HtmlUnitDriver(capabilities);
if (browserType.equals(BrowserType.OPERA_BLINK))
driver = new OperaDriver(capabilities);
setDefaultTimeOut(15, 15, 15);
return driver;
}
private WebDriver createDriverRemote(Capabilities capabilities, URL hub) {
driver = new RemoteWebDriver(hub, capabilities);
setDefaultTimeOut(15, 15, 15);
return driver;
}
private URL getHub() {
if (HUB_SYS_PROPERTY != null) {
try {
return new URL(HUB_SYS_PROPERTY);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
return null;
}
private String createKey(Capabilities capabilities, URL hub) {
String createdKey = Thread.currentThread().getName() + "-" + Thread.currentThread().getId()+ "-StorageKey-" + capabilities.toString() + ":" + hub;
log.debug("Key created: " + createdKey);
return createdKey;
}
}
Это базовый тестовый класс
public abstract class BaseTest {
private static final Logger log = LoggerFactory.getLogger(BaseTest.class);
protected WebDriver driver;
protected PropertyStorage propertyStorage = PropertyStorage.getInstance();
protected Screenshoter screenshoter;
protected WebDriverStorage webDriverStorage = new WebDriverStorage();
@BeforeSuite(alwaysRun = true)
public void beforeSuite() {
log.debug("");
}
@BeforeTest
public void beforeTest() {
log.debug("");
}
@BeforeClass(alwaysRun = true)
public void beforeClass() {
log.debug("");
driver = webDriverStorage.getDriver();
webDriverStorage.setDefaultTimeOut(15, 15, 15);
}
@BeforeMethod(alwaysRun = true)
public void beforeMethod(ITestContext context) {
driver = webDriverStorage.getCurrentDriver();
context.setAttribute("driver", driver);
driver.manage().deleteAllCookies();
}
@AfterMethod(alwaysRun = true)
public void afterMethod() {
log.debug("");
}
@AfterClass(alwaysRun = true)
public void afterClass() {
log.debug("");
webDriverStorage.dissmissAllThreadDrivers();
}
@AfterTest
public void afterTest() {
log.debug("");
}
@AfterSuite(alwaysRun = true)
public void afterSuite() {
log.debug("");
webDriverStorage.dismissAllDrivers();
}
}
joemast
(Александр Таранков)
April 30, 2015, 10:22am
#6
Ну и баги в TestNG тоже никто не отменял Посмотри, можешь уже есть такой. Если нет, потом новый заведешь
ArtOfLife
(Sergey Korol)
April 30, 2015, 11:08am
#7
Я бы рекомендовал начать с ревайза выбранного подхода:
Слишком много лишних переприсваиваний.
Локальные сторэджи / мапы для единственного драйвера. Я так понимаю, что идея заключалась как раз таки в общем хранилище драйверов для многопоточной среды?
@BeforeClass/Method
не согласованы. Зачем вам вообще инстанс драйвера на уровне BaseTest
, если вы его используете для точечных локальных действий? Или же зачем дергать геттер из стореджа, если у вас уже есть созданный инстанс в @BeforeClass
?
Инстанс сетится в контекст перед каждым методом, хотя драйвер создается для уровня класса.
Зачем хранить инстанс драйвера на уровне слушателя, который дергается миллион раз во время экзекьюшена?
Потенциальная проблема может крыться где угодно, т.к. уж очень много переплетений.
П.С. К слову, какую версию testng используете?
Да идея заключалась в едином классе хранилище драйверов для всех потоков.
В целом я запускаю драйвер для класса, классов у меня много, в них есть некоторые тесты, которые требуют подхатчить капабилити браузера, например драйвер с проксей, или драйвер с другим юзер агентом, или другим профайлом, если тест этого требует то я делаю необходимый драйвер прямо в нем, путем запроса в хранилище драйвера с модификациями. Как вы видели используется фабрика, драйвер из которой отдается по аргументу энуму. И дальше тесты продолжаются с тем же драйвером.
ArtOfLife
(Sergey Korol)
April 30, 2015, 1:33pm
#10
Вся проблема в том, что ваше хранилище существует сугубо внутри своего потока, оно не общее для всех. То же касается и мапы. TestNG запустит ваши классы в параллели в отдельных потоках, которые создадут свои собственные инстансы сторэджа с единственным драйвером внутри.
Нужно написать синглтон и обернуть драйвер в Threadlocal или какие ваши советы?
ArtOfLife
(Sergey Korol)
April 30, 2015, 1:40pm
#12
Статический сторэдж + мапа с поддержкой конкаренси. HashMap не катит для многопоточного read | write аксеса.
П.С. Посмотрите, как реализован WebDriverRunner (его сторедж) + сам контейнер с мапой внутри.
2 Likes