Session not found exception with Selenium Web driv

2019-09-11 12:35发布

问题:

This is my test class where I want to execute each input of Data Provider in new browser parallely. I am able to open new browsers but I get Session Not Found Exception and no such element exception

public class DemoTest {
private WebDriver driver;
@Test(dataProvider = "dp")
public void f(Integer n, String s) {
  try {
      System.out.println("Driver: "+driver.toString());
      driver.get("www.google.com");
      driver.findElement(By.id("lst-ib")).sendKeys("1234567");
      System.out.println("method f id:"+Thread.currentThread().getId()+" n:"+n+" s:"+s);
  }
  catch(Exception e) {
      e.printStackTrace();
  }
}
@BeforeMethod
public void beforeMethod() {
  try {
  driver= new FirefoxDriver();
  driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
  driver.manage().window().maximize();
  System.out.println("Before method id:"+Thread.currentThread().getId());
  }
  catch (Exception e) {
      e.printStackTrace();
  }
 }

@AfterMethod
public void afterMethod() {
  try {
      System.out.println("After method id:"+Thread.currentThread().getId());
  if(driver != null ) {
  driver.quit();
 // driver.close();
  }
  }
  catch(Exception e) {
      e.printStackTrace();
  }
}


@DataProvider(parallel=true)
public Object[][] dp() {
return new Object[][] {
  new Object[] { 1, "a" },
  new Object[] { 2, "b" },
  new Object[] { 3, "c" },
  new Object[] { 4, "d" },

};
}
}

This is testng.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="Suite" parallel="methods"> 
<test name="prelogin">
<classes>
    <class name="com.package.DemoTest"></class>
</classes>
</test>
</suite> 

Could anyone tell me why I get this exception & how to solve it?

回答1:

You're sharing a WebDriver instance across multiple threads. As such, one test can try to use the WebDriver after another test's afterMethod has already quit it (hence the session not found exception) or hasn't loaded google.com yet (hence the no such element exception).

If you want to run Selenium WebDriver tests using TestNG in parallel then you can use a ThreadLocal to maintain a WebDriver instance per thread.

e.g.

public class DemoTest {
    private static final ThreadLocal<WebDriver> WEB_DRIVER_THREAD_LOCAL = new InheritableThreadLocal<>();

    @Test(dataProvider = "dp")
    public void f(Integer n, String s) {
        WebDriver driver = WEB_DRIVER_THREAD_LOCAL.get();
        System.out.println("Driver: " + driver.toString());
        driver.get("www.google.com");
        driver.findElement(By.id("lst-ib")).sendKeys("1234567");
        System.out.println("method f id:" + Thread.currentThread().getId() + " n:" + n + " s:" + s);
    }

    @BeforeMethod
    public void beforeMethod() {
        WebDriver driver = new FirefoxDriver();
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
        driver.manage().window().maximize();
        WEB_DRIVER_THREAD_LOCAL.set(driver);
        System.out.println("Before method id:" + Thread.currentThread().getId());
    }

    @AfterMethod
    public void afterMethod() {
        WebDriver driver = WEB_DRIVER_THREAD_LOCAL.get();
        System.out.println("After method id:" + Thread.currentThread().getId());
        if (driver != null) {
            driver.quit();
        }
    }


    @DataProvider(parallel = true)
    public Object[][] dp() {
        return new Object[][]{
                new Object[]{1, "a"},
                new Object[]{2, "b"},
                new Object[]{3, "c"},
                new Object[]{4, "d"},

        };
    }
}

I've omitted wrapping each test with a try-catch block as TestNG already catches exceptions thrown by test/configuration methods and prints stack traces for them, etc.

The above example will create a WebDriver instance associated with the test's thread for each test. i.e. If they were to all run truly in parallel you would have 4 instances of Firefox open (which I think is what you want).

If you have more tests than threads and want to reuse WebDriver instances then you can use a ThreadLocal with an initial value.

e.g.

public class DemoTest {
    private static final Set<WebDriver> WEB_DRIVERS = new HashSet<>();
    private static final ThreadLocal<WebDriver> WEB_DRIVER_THREAD_LOCAL = InheritableThreadLocal
            .withInitial(new Supplier<WebDriver>() {
                @Override
                public WebDriver get() {
                    WebDriver driver = new FirefoxDriver();
                    driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
                    driver.manage().window().maximize();
                    WEB_DRIVERS.add(driver);
                    System.out.println("Before thread id:" + Thread.currentThread().getId());
                    return driver;
                }
            });

    @Test(dataProvider = "dp")
    public void f(Integer n, String s) {
        WebDriver driver = WEB_DRIVER_THREAD_LOCAL.get();
        System.out.println("Driver: " + driver.toString());
        driver.get("www.google.com");
        driver.findElement(By.id("lst-ib")).sendKeys("1234567");
        System.out.println("method f id:" + Thread.currentThread().getId() + " n:" + n + " s:" + s);
    }

    @AfterClass
    public void afterClass() {
        for (WebDriver webDriver : WEB_DRIVERS) {
            try {
                webDriver.quit();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    @DataProvider(parallel = true)
    public Object[][] dp() {
        return new Object[][]{
                new Object[]{1, "a"},
                new Object[]{2, "b"},
                new Object[]{3, "c"},
                new Object[]{4, "d"},

        };
    }
}

And a testng.xml to reduce the total number of threads to demonstrate this:

<suite name="Suite" parallel="methods" thread-count="2" data-provider-thread-count="2">
    <test name="prelogin">
        <classes>
            <class name="DemoTest"/>
        </classes>
    </test>
</suite>

Example output (see how it only creates 2 FirefoxDriver instances, runs two tests in parallel, and then runs the next two tests in parallel, using all the available threads and reusing the web drivers for each thread):

Before thread id:14
Driver: FirefoxDriver: firefox on WINDOWS (e924b5f1-9ace-46e5-a6b3-d1494de7e94d)
method f id:14 n:2 s:b
Before thread id:13
Driver: FirefoxDriver: firefox on WINDOWS (8dc8a53c-5a96-41e8-9383-e552c7c8f680)
method f id:13 n:1 s:a
Driver: FirefoxDriver: firefox on WINDOWS (8dc8a53c-5a96-41e8-9383-e552c7c8f680)
method f id:13 n:3 s:c
Driver: FirefoxDriver: firefox on WINDOWS (8dc8a53c-5a96-41e8-9383-e552c7c8f680)
method f id:13 n:4 s:d


回答2:

Issue has nothing to with data providers. The problem lies in driver.get("www.google.com");. WebDriver api sends RESTfull request to Selenium server to execute a command. REST services uses http or https protocol for request/response. Hence when we call the url without mentioning the protocol prefix (in this case http), exception is thrown.

Change to :

driver.get("http://www.google.com/");

This will work.