I'm using cucumber-jvm picocontainer to share selenium driver between classes. I have ShareDriver and WebDriverFactory class.
My problem is the following:
1. If I run 2 test cases, the driver/browser instance is closed after the first test case, new browser instance is created and run the second one. I would like to use only 1 browser instance and run the tests, then close it.
IEDriverServer.exe and one java.exe are stucked on task manager after the test, however the browser is closed. I need to kill them manually. Every run creates a new one from these tasks. I tried all ideas from stackoverflow, but none of them could solve this problem.
Thanks!
My SharedDriver class:
public class SharedDriver extends EventFiringWebDriver implements Startable {
public SharedDriver() {
super(WebDriverFactory.localInternetExplorerWebDriver());
}
#After
public void embedScreenshot(Scenario scenario) {
try {
byte[] screenshot = getScreenshotAs(OutputType.BYTES);
scenario.embed(screenshot, "image/png");
} catch (WebDriverException somePlatformsDontSupportScreenshots) {
System.err.println(somePlatformsDontSupportScreenshots.getMessage());
}
}
#Override
public void start() {
}
#Override
public void stop() {
quit();
}
}
My WebDriverFactory class:
class WebDriverFactory {
static {
System.setProperty("webdriver.ie.driver", "src/test/resources/webDrivers/IEDriverServer.exe");
}
static WebDriver localInternetExplorerWebDriver() {
DesiredCapabilities returnCapabilities = DesiredCapabilities.internetExplorer();
System.setProperty("webdriver.ie.driver", "src/test/resources/webDrivers/IEDriverServer.exe");
//returnCapabilities.setCapability("nativeEvents", false);
returnCapabilities.setCapability("requireWindowFocus", true);
returnCapabilities.setCapability(InternetExplorerDriver.ENABLE_PERSISTENT_HOVERING, false);
returnCapabilities.setCapability(InternetExplorerDriver.IE_ENSURE_CLEAN_SESSION, true);
returnCapabilities.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);
returnCapabilities.setCapability("ignoreZoomSetting", true);
return new InternetExplorerDriver(returnCapabilities);
}
}
The implementation of SharedDriver is not correct. You need a static webdriver field in the shareddriver class, create a shutdown thread, add this thread to the jvm shutdown hook. Use this one
If you wanna kill that too use this. Add this to the shutdown hook.Add it inside the run method of the thread after call to REAL_DRIVER.quit().
Related
I like to test my site in a lot of threads. But when I trying to do that, I see one problem. All motions when I like are happening in last opened window. So, the first window just stuck in background.
public class Core extends Thread{
private static FirefoxDriver firefoxDriver;
public Core(){
firefoxDriver = new FirefoxDriver();
}
#Override
public void run() {
firefoxDriver.get("https://google.com/");
firefoxDriver.close();
}
}
public class Main {
public static void main(String[] args) throws AWTException {
System.setProperty("webdriver.gecko.driver", "/home/riki/Downloads/geckodriver-v0.30.0-linux64/geckodriver");
Core core = new Core();
Core core2 = new Core();
core.start(); // This thread is stuck in back
core2.start(); // This thread goes to google.com twice
}
}
I really don't understand why it happens. You can see it here. After that the code has run, the first window keeps hanging in back. It don't close. When the second thread closes after executing code
This is because of you using static field for Forefox driver.
Static means the one per all instances. So remove static here.
private FirefoxDriver firefoxDriver;
and after, each thread will use it's own firefoxDriver field.
Static fields should be used carefully if you going to modify them.
I have a code with many classes.
There is a class which creates the driver -
public class DriverDelegate {
private String baseURL = "someLink";
private WebDriver driver;
private WebDriverWait wait;
DriverDelegate(String url) {
System.setProperty("webdriver.chrome.driver", "${directory}");
driver = new ChromeDriver();
driver.get(baseURL + url);
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
wait = new WebDriverWait(driver, 5);
}
public WebDriver getDriver() {
return driver;
}
I create new driver for every test. And most of my lines are the ones containing assertTrue like this-
public class UserInterfaceTests extends BaseTest{
#Test
public void headerClicker() throws java.lang.Exception {
//Startup
DriverDelegate driverDelegate = new DriverDelegate("url");
WebDriver driver = driverDelegate.getDriver();
//Some random assertTrue
assertTrue("Test(HeaderClicker) - NoSuchElementException click", TestHelper.headerClicker(schedule, driver));
//I hope that it is not neccessary to put up all helper classes like TestHelper or BaseTest
Now I launch my tests from a class called Startup -
public class Startup{
#Test
public void HeaderClicker() throws Exception{ UserInterfaceTests UI = new UserInterfaceTests(); UI.headerClicker();}
My question here is how to close the browser after the assertTrue fails. Things like #AfterTest, #AfterSuite etc do not work because other methods can not use the same driver that was used in the test.
Any ideas?
Ok there are a few things I want to touch on here. First off, #shutdown -h now is correct in their comment that you shouldn't be programmatically creating test classes and running their #Test methods yourself. Let the test running framework handle that (e.g. TestNG, JUnit, etc).
To the point of your actual question, though, is that you want pre-test and post-test methods to handle behavior that occurs before and / or after your actual test method. For these to work, though, you need to let the test framework handle the running of the tests. You mentioned #AfterTest and #AfterSuite as not being correct for your use case, though not for the reason you specified (entirely). #AfterTest in TestNG only is executed once after all the test methods in all the classes inside of a <test> node specified in a suite. #AfterSuite is only executed once after all the test methods in the entire suite. What you are looking for is the #AfterMethod annotation.
Example:
public class FooTest {
private DriverDelegate driver;
#BeforeMethod
public void setup() {
try {
driver = new DriverDelegate("url");
} catch (Exception ignore) { }
}
#AfterMethod
public void tearDown() {
try {
driver.quit();
} catch (Exception ignore) { }
driver = null;
}
#Test
public void foo() {
// do test stuff
}
}
In the above code, when TestNG runs this test class, each method annotated with #Test in this class will have a corresponding #BeforeMethod execution that initializes the driver and an #AfterMethod that closes the driver even if the test fails. Couple of points to make about this type of setup with TestNG:
(1) TestNG does not create separate instances of the test class so if you save state on the class object then you cannot run the test methods themselves in parallel within a class since you would have multiple methods trying to create new drivers and save them to the same variable, corrupting the state of the other tests that are running. You can run with parallel mode of per class (e.g. have 5 threads, each running a separate test class at the same time).
(2) Why did I wrap the #BeforeMethod and #AfterMethod bodies in a try-catch block? Because TestNG takes a fail quickly on configuration method exceptions and can cause other tests that haven't run yet to be skipped so you need to deal with any code that could possibly fail. By wrapping the creating and closing of the web driver you can ignore the error and continue on running other tests. If the driver fails to be created, for instance, the driver variable will be null and that #Test method will fail but others might succeed. Ideally, you should probably have some logging of the exception so that you can investigate why the driver failed to be created, for instance.
Also, if you need to use a driver in many test classes, you can make a base class that does the creation of the driver along with the closing of it after each method and have your test classes extend that. If you have a class with a method annotated with #Test on it, it will run any #BeforeMethod methods on that test class AND on all of the super classes as well. There is guaranteed ordering of the methods between classes (though not if you have multiple #BeforeMethod methods in the same class).
public abstract class A {
#BeforeMethod
public void setupA() { }
}
public class B extends A {
#BeforeMethod
public void setupB() { }
#Test
public void foo() { }
}
In the above, when foo is run, it will have run first setupA and then setupB. After methods work in the same way, but in the reverse order.
I have an selenium integration test that launches browser and checks the webstore for any broken functionality. However the entire test runs too fast and finishes before I can even see which page is getting executed. How can I decrease the execution speed from my code. Currently I have following file that actually launches the test.
AbstractSeleniumIt.java
#Before
public void setUp() throws Exception {
urlProp = GenericUtils.loadProperties("url.properties");
this.BASE_URL = urlProp.getProperty("webstoreUrl");
xpathProp = GenericUtils.loadProperties("xpath.properties");
driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
}
#After
public void tearDown() {
driver.quit();
}
You can play with EventFiringWebDriver.
WebDriver driver = new FirefoxDriver();
EventFiringWebDriver slowDriver = new EventFiringWebDriver(driver);
slowDriver.registerListener(new ListenerThatAddsPauses(5, TimeUnit.SECONDS));
You will have to write your class ListenerThatAddsPauses which will extend AbstractEventFiringListener. In ListenerThatAddsPauses you will have to override methods from parent class and for example add needed pauses. Something like:
#Override
public void beforeClickOn(WebElement element, WebDriver driver) {
Thread.sleep(timeout);
}
Here is a great example
i am having issues in acquiring correct driver instance.
following is my setup
public class SeleniumBase{
public static WebDriver driver;
public static void setUp(url,browser,port){
driver = new FirefoxDriver();
}
public static void tearDown(){
driver.manage().deleteAllCookies();
driver.close();
driver.quit();
}
}
public class BuildTest extends SeleniumBase{
#BeforeClass
public static void seleniumSetup(){
try{
// read properties
url = prop.getProp("baseUrl");
browser = prop.getProp("browser");
port = prop.getProp("port");
}
SeleniumBase.setUp(url,browser,port);
waitForLoginPage();
App.login();
}
#AfterClass
public static void seleniumTearDown(){
App.logOut();
SeleniumBase.tearDown();
}
}
#RunWith(Suite.class)
#Suite.SuiteClasses(
{
Test1.class,
Test2.class
})
public class SmokeSuite {
}
now, for Test1.class everything works fine but when Test2.class is invoked from the suite, new driver instance is created with the setUp method, but App.Login() throws error saying "The FirefoxDriver cannot be used after quit() was called"
is anything going wrong in my setup/teardown..?
As the comments on your question already mention, your setUp() and tearDown() methods as well as your WebDriver instance are static. So once you call driver.quit(), your driver couldn't be used any more. A new driver needs to be acquired.
However, you do not use JUnit's #Before and #After annotations but rather #BeforeClass and #AfterClass. So I guess you have multiple tests in your Test2 class, the driver quits after the first one and is not reinitialized before the second test.
Better make WebDriver, setUp() and tearDown not static and use #Before and #After in your test-classes. Then your problems should go away.
When you use driver.quit(); you close all open browsers and quit the driver. However, your driver is not set to null but remains in the void between WebDriver instance to null.
Why does this matter? Because when you call driver = new FirefoxDriver(); the constructor is "cheating" and hands you the old WebDriver instance, which considered viable because it doesn't see null, instead of initializing new instance.
Assigning null after calling quit should solve the problem.
public void tearDown() {
driver.quit();
driver = null;
}
GlobalVariables class holds different variables which are used across my framework one of them is WebDriver instance:
public class GlobalVariables
{
public static WebDriver driver;
//Some other static global variables required across my framework
public GlobalVariables(String propertiesFile)
{
initializeVariables(propertiesFile);
}
public void initializeVariables(String propertiesFile)
{
GlobalInitializer obj=new GlobalInitializer();
obj.initialize(String propertiesFile);
}
}
GlobalInitializer contains methods to initialize all the GlobalVariables:
public class GlobalInitializer extends GlobalVariables
{
public void initialize(String propertiesFile)
{
//Some logic to read properties file and based on the properties set in it, call other initialization methods to set the global variables.
}
public void initializeDriverInstance(String Browser)
{
driver=new FireFoxDriver();
}
//Some other methods to initialize other global variables.
}
I have many GetElement classes which use the driver instance to get UI control elements E.g:
public class GetLabelElement extends GlobaleVariables
{
public static WebElement getLabel(String someID)
{
return driver.findElement(By.id(someId));
}
//Similar methods to get other types of label elements.
}
public class GetTextBoxElement extends GlobaleVariables
{
public static WebElement getTextBox(String someXpath)
{
return driver.findElement(By.xpath(someXpath));
}
//Similar methods to get other types of text box elements.
}
I have other classes which perform some actions on the UI Control(This classes too use the global variables) E.g:
public class GetLabelProperties extends GlobalVariables
{
public static String getLabelText(WebElement element)
{
return element.getText();
}
}
public class PerformAction extends GlobalVariables
{
public static void setText(String textBoxName,String someText)
{
driver.findElement(someLocator(textBoxName)).setText("someText");
}
//Some other methods which may or may not use the global variables to perform some action
}
My test class in testng looks like this:
public class TestClass
{
GlobalVariables globalObj=new GlobalVariables(String propertiesFile);
#Test(priority=0)
{
GlobalVariables.driver.get(someURL);
//Some assertion.
}
#Test(priority=1)
{
WebElement element=GetLabelElement.getLabel(someID);
String labelName=GetLabelProperties.getLabelText(element);
//Some assertion.
}
#Test(priority=2)
{
WebElement element=GetTextBoxElement.getTextBox(someXpath);
PerformAction.setText(element.getText(),someText);
//Some assertion.
}
}
I have similar multiple test classes based on scenarios.
Now this tests are running fine if i am running them individually. But when i try to run them in parallel, then this tests are failing in some weird fashion. On analyzing i found out that its the static global variables which are getting initialized by each tests thus leaving the other tests to fail. Now how should i go about achieving my objective to run multiple tests parallely with minimal changes in my framework design? i have tried searching for options, and i have come across some option i.e 1) use of synchronized. 2) Create ThreadLocal instance(Note : I have tried this solution but still same issue. tests are mixing up with each other resulting in failure. I had marked the WebDriver instance as ThreadLocal and overriden the initialValue method of ThreadLocal to initialize the driver instance. Still i am not sure whether i had implemented it correctly or not.). Now i am not sure how best to implement any one of this solution in the given scenario. Any help is appreciated. TIA!
I have found out the solution : Use of ThreadLocal is the best solution to run tests in a huge multithreaded environment.
Code snippet to use WebDriver in multithreaded environment:
public static ThreadLocal<WebDriver> driver;
driver=new ThreadLocal<WebDriver>()
{
#Override
protected WebDriver initialValue()
{
return new FirefoxDriver(); //You can use other driver based on your requirement.
}
};
Now every time a test thread is created a new browser will open. ThreadLocal will make sure that there's only one copy of static webdriver instance per thread. [NOTE: Make sure your other global variables are too ThreadLocals. In my case they were not thats why i was running into test goof up issue]. Some extra knowledge which i would like to share so that others may find it informative. In ThreadLocal whenever the ThreadLocal.get() method is called you have to make sure that there is a provision to initialize the thread local as shown above in initialValue() method or you may run into null pointer exception. Thanks everyone.
If you are going to run non-parallel, then using a static webdriver member (or a instance shared between test classes by passing by reference) is fine because it is a good way to not have to close the webdriver instance between test classes. If you want to run parallel though, you need to have one instance of webdriver for EACH thread and so in that case using a static member is the WRONG way to go. Instead you need to create or pass a webdriver instance when the test case class is invoked.
Also, you are breaking a test into separate tests for each step of the test. That is very unusual and I do not see the reason why you are doing that. You could really simplify your tests by keeping all the test steps within one singe test case like people usually do.
You are getting this because of how JVM handles static members and methods.
You can't have a static webdriver object if you are going to run in parallel.
Source: The automated regression system i implemented where I work - we had this issue.
you can try something like this
public class DriverManager {
private static final ThreadLocal<WebDriver> threadLocal = new ThreadLocal<WebDriver>();
public static WebDriver getDriver() {
return threadLocal.get();
}
public static void setDriver(WebDriver driver) {
threadLocal.set(driver);
}
public static void closeDriver() {
if (getDriver() != null) {
try {
getDriver().close();
} catch (Exception e) {
e.printStackTrace();
}
try {
getDriver().quit();
} catch (Exception e) {
e.printStackTrace();
}
}
threadLocal.remove();
}
}