Why won't my cross platform test automation framework run in parallel? - selenium

I am currently rewriting the automated testing framework for my company's mobile testing. We are attempting to use an interface which is implemented by multiple Page Object Models dependent on the Operating System of the mobile device the application is being run on. I can get this framework to run sequentially and even create multiple threads but it will not run in parallel no matter what I do. Of Note, we use Appium and something called the DeviceCart/DeviceConnect which allows me to physically remote into multiple devices, thus this isn't running on a grid. With that said I will link my pertinent code (this is my second version of this same code, I wrote one with and one without using ThreadLocal)
This should instantiate a new driver with a new thread for each Test
public class TLDriverFactory {
private ThreadLocal < AppiumDriver < MobileElement >> tlDriver = new ThreadLocal <>();
public synchronized void setTLDriver(OS platform, String server, String udid, String bundleID) {
switch (platform) {
case IOS:
tlDriver = ThreadLocal.withInitial(() -> {
try {
return new IOSDriver < MobileElement > (new URL(server), DesiredCapsManager.getDesiredCapabilities(OS.IOS, udid, bundleID));
} catch(MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
});
break;
case ANDROID:
tlDriver = ThreadLocal.withInitial(() -> {
try {
return new AndroidDriver < MobileElement > (new URL(server), DesiredCapsManager.getDesiredCapabilities(OS.ANDROID, udid, bundleID));
} catch(MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
});
break;
default:
break;
}
}
public synchronized ThreadLocal < AppiumDriver < MobileElement >> getTLDriver() {
return tlDriver;
}
}
This handles browser capbilities
public class DesiredCapsManager {
public static DesiredCapabilities getDesiredCapabilities(OS platform, String udid, String bundleID) {
//Set DesiredCapabilities
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("deviceConnectUserName", "User#Name.com");
capabilities.setCapability("deviceConnectApiKey", "API-Token-Here");
capabilities.setCapability("udid", udid);
capabilities.setCapability("platformName", platform);
capabilities.setCapability("bundleID", bundleID);
//IOS only Settings
if (platform.equals(OS.IOS)) {
capabilities.setCapability("automationName", "XCUITest");
}
else {
//Android only Settings
capabilities.setCapability("automationName", "appium");
}
return capabilities;
}
}
This is the Base Test class from which every test inherits
public class BaseTest {
protected AppiumDriver < MobileElement > driver;
protected AppiumSupport.TLDriverFactory TLDriverFactory = new AppiumSupport.TLDriverFactory();
public enum OS {
ANDROID,
IOS
}
#AfterMethod
public synchronized void tearDown() throws Exception {
driver.quit();
TLDriverFactory.getTLDriver().remove();
}
}
Here is the test case itself
public class Test_SignIn extends BaseTest {
protected SignInPage signInPage;
#Parameters(value = {
"udid",
"bundleID",
"platform",
"server"
})
#BeforeMethod
public void setup(String udid, String bundleID, OS platform, String server) throws MalformedURLException,
InterruptedException {
//Set & Get ThreadLocal Driver
TLDriverFactory.setTLDriver(platform, server, udid, bundleID);
driver = TLDriverFactory.getTLDriver().get();
Thread.sleep(5000);
switch (platform) {
case IOS:
signInPage = new SignInPageIOS(driver);
break;
case ANDROID:
signInPage = new SignInPageAndroid(driver);
break;
default:
break;
}
System.out.println("Current Thread ID BeforeTest: " + Thread.currentThread().getName());
}
#Test
public synchronized void Authenticate() throws Exception {
System.out.println("Current Thread ID Test 1: " + Thread.currentThread().getName());
signInPage.Login("Username", "Password");
}
}
Here is the testng.xml file
< !DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Test" parallel="tests" thread-count="4">
<test name="SignIn" parallel ="instances" thread-count="2">
<parameter name="udid" value="DeviceIdGoesHere" />
<parameter name="bundleID" value="Environment.address.here" />
<parameter name="platform" value="ANDROID" />
<parameter name="server" value="http://deviceconnect/appium" />
<classes>
<class name="Test.Test_SignIn">
</class>
</classes>
</test>
<test name="SignIn2" parallel="instances" thread-count="2">
<parameter name="udid" value="DeviceIdGoesHere" />
<parameter name="bundleID" value="Environment.address.here" />
<parameter name="platform" value="IOS" />
<parameter name="server" value="http://deviceconnect/appium" />
<classes>
<class name="Test.Test_SignIn">
</class>
</classes>
</test>
</suite>
What I'm looking for is if anyone can determine what mistake I've made or what the bottleneck is preventing the tests from running in parallel

Based on what you have shared so far, here's the cleaned-up and fixed code that should support your concurrency requirements.
The Driver factory class which is responsible for creation and clean-up of Appium driver instances for each and every thread, looks like below:
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import java.net.MalformedURLException;
import java.net.URL;
public class TLDriverFactory {
private static final ThreadLocal<AppiumDriver<MobileElement>> tlDriver = new ThreadLocal<>();
public static void setTLDriver(BaseTest.OS platform, String server, String udid, String bundleID) throws MalformedURLException {
System.out.println("Current Thread ID Driver Instantiation: " + Thread.currentThread().getName());
AppiumDriver<MobileElement> driver;
switch (platform) {
case IOS:
driver = new IOSDriver<>(new URL(server), DesiredCapsManager.getDesiredCapabilities(BaseTest.OS.IOS, udid, bundleID));
break;
default:
driver = new AndroidDriver<>(new URL(server), DesiredCapsManager.getDesiredCapabilities(BaseTest.OS.ANDROID, udid, bundleID));
break;
}
tlDriver.set(driver);
}
public static AppiumDriver<MobileElement> getTLDriver() {
return tlDriver.get();
}
public static void cleanupTLDriver() {
tlDriver.get().quit();
tlDriver.remove();
}
}
Here's how the BaseTest which I am guessing is supposed to be the base class for all tests, would look like
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Parameters;
public class BaseTest {
private static final ThreadLocal<SignInPage> signInPage = new ThreadLocal<>();
public enum OS {
ANDROID,
IOS
}
#Parameters(value = {"udid", "bundleID", "platform", "server"})
#BeforeMethod
public void setup(String udid, String bundleID, OS platform, String server) throws Exception {
//Set & Get ThreadLocal Driver
TLDriverFactory.setTLDriver(platform, server, udid, bundleID);
Thread.sleep(5000);
SignInPage instance;
switch (platform) {
case IOS:
instance = new SignInPageIOS(TLDriverFactory.getTLDriver());
break;
default:
instance = new SignInPageAndroid(TLDriverFactory.getTLDriver());
break;
}
System.out.println("Current Thread ID BeforeTest: " + Thread.currentThread().getName());
signInPage.set(instance);
}
#AfterMethod
public void tearDown() {
System.out.println("Current Thread ID AfterTest: " + Thread.currentThread().getName());
TLDriverFactory.cleanupTLDriver();
}
protected static SignInPage getPageForTest() {
return signInPage.get();
}
}
Here's how the constructor of your page classes would look like
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
public class SignInPageIOS extends SignInPage {
public SignInPageIOS(AppiumDriver<MobileElement> tlDriver) {
super(tlDriver);
}
}
Here's how a typical test case could look like
import org.testng.annotations.Test;
public class Test_SignIn extends BaseTest {
#Test
public void authenticate() {
//Get the instance of "SignInPage" for the current thread and then work with it.
getPageForTest().Login("Username", "Password");
}
}

Related

Extent Reports: Test Steps are getting merged in last test in Extent Reports, When executing test in Parallel

The Test Steps and Test Logs are getting merged in to the single last test.
Extent Report 3.2
Actual Reports
Function 1 logs
Function 2 logs [Having all steps]
My Project Structure is
HomePage.java
package pom;
import test.BaseTest;
public class HomePage extends BaseTest
{
public void setClick()
{
test.pass("This test is pass which is in click of home page");
}
public void setName()
{
test.fail("This test is fail which is in set of home page");
}
public void select()
{
test.pass("This test is info which is in selct of home page");
}
}
Test1.java
package test;
import org.testng.annotations.Test;
import pom.HomePage;
public class Test1 extends BaseTest
{
#Test
public void funtion1()
{
HomePage hp = new HomePage();
hp.setName();
hp.setClick();
hp.select();
test.pass("Test is Passed! ins funtion 2");
}
}
Test2.java
package test;
import org.testng.annotations.Test;
import pom.HomePage;
public class Test2 extends BaseTest
{
#Test
public void funtion2()
{
HomePage hp = new HomePage();
hp.setClick();
hp.select();
test.pass("Test is Passed!");
}
}
BaseTest.Java
package test;
import java.lang.reflect.Method;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.Status;
import com.aventstack.extentreports.markuputils.ExtentColor;
import com.aventstack.extentreports.markuputils.MarkupHelper;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
import com.aventstack.extentreports.reporter.configuration.ChartLocation;
import com.aventstack.extentreports.reporter.configuration.Theme;
public class BaseTest
{
public static ExtentHtmlReporter htmlReporter;
public static ExtentReports extent;
public static ExtentTest test;
#BeforeSuite
public void setUp()
{
htmlReporter = new ExtentHtmlReporter("./Reports/MyOwnReport.html");
extent = new ExtentReports();
extent.attachReporter(htmlReporter);
extent.setSystemInfo("OS", "Mac Sierra");
extent.setSystemInfo("Host Name", "Jayshreekant");
extent.setSystemInfo("Environment", "QA");
extent.setSystemInfo("User Name", "Jayshreekant S");
htmlReporter.config().setChartVisibilityOnOpen(false);
htmlReporter.config().setDocumentTitle("AutomationTesting.in Demo Report");
htmlReporter.config().setReportName("My Own Report");
htmlReporter.config().setTestViewChartLocation(ChartLocation.TOP);
//htmlReporter.config().setTheme(Theme.DARK);
htmlReporter.config().setTheme(Theme.STANDARD);
}
#BeforeMethod
public void startTest(Method m)
{
test = extent.createTest(m.getName(),"This is the description of Test" + m.getName());
}
#AfterMethod
public void getResult(ITestResult result)
{
if(result.getStatus() == ITestResult.FAILURE)
{
test.log(Status.FAIL, MarkupHelper.createLabel(result.getName()+" Test case FAILED due to below issues:", ExtentColor.RED));
test.fail(result.getThrowable());
}
else if(result.getStatus() == ITestResult.SUCCESS)
{
test.log(Status.PASS, MarkupHelper.createLabel(result.getName()+" Test Case PASSED", ExtentColor.GREEN));
}
else
{
test.log(Status.SKIP, MarkupHelper.createLabel(result.getName()+" Test Case SKIPPED", ExtentColor.ORANGE));
test.skip(result.getThrowable());
}
}
#AfterSuite
public void tearDown()
{
extent.flush();
}
}
testngall.xml
<suite name="Suite" parallel="tests">
<test name="Test 1 ">
<classes>
<class name="test.Test1" />
</classes>
</test>
<test name="Test 2">
<classes>
<class name="test.Test2" />
</classes>
</test>
</suite> <!-- Suite -->
So this is the entire project code structure, I am getting the logs appending in last test
This is your problem:
public static ExtentTest test;
Since this is static there is only ever one instance of it. When you run your tests in parallel this #BeforeMethod is called twice.
#BeforeMethod
public void startTest(Method m)
{
test = extent.createTest(m.getName(),"This is the description of Test" + m.getName());
}
The second time it is called the first test probably hasn't finished, but it is still referencing the test object so you will get the output of the second test and some parts of the first test that had no completed running at the point the #BeforeMethod was called.
You are going to need to rewrite your code to not use a static test object.
In order to keep your parallel execution thread safe, your ExtentTest have to use ThreadLocal class instance variable. try,
private static ThreadLocal<ExtentTest> test = new InheritableThreadLocal<>();
In the class where you create tests, you can make this a child class of the class where you define extent report classes and variables. Now in the child class (having tests) you can create multiple Extent Test instances.
So create a new instance for every new test

Parallel methods do not run as expected

I need all the tests to be part of single class and run these tests in parallel. I'm using parallel="methods" in Testng.xml. I have class like
Public class DemoParallel{
#Test
/* some code to launch Google.*/
#Test
/* some code to launch Facebook*/
}
Actual : 2 instances of chrome launches. Google test is running completely.Facebook test is only launched but does not run. Gets hanged. Only one test passes and also have tried implementing listeners but no luck.
Any suggestions would be helpful.
Local Driver Factory :
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
class LocalDriverFactory {
static WebDriver createInstance(String browserName) {
WebDriver driver = null;
if (browserName.toLowerCase().contains("firefox")) {
System.setProperty("webdriver.firefox.marionette","path to driver exe");
driver = new FirefoxDriver();
return driver;
}
if (browserName.toLowerCase().contains("internet")) {
driver = new InternetExplorerDriver();
return driver;
}
if (browserName.toLowerCase().contains("chrome")) {
System.setProperty("webdriver.chrome.driver","path to driver exe");
driver = new ChromeDriver();
return driver;
}
return driver;
}
}
use ThreadLocal class as follows :
public class LocalDriverManager {
private static ThreadLocal<WebDriver> webDriver = new ThreadLocal<WebDriver>();
public static WebDriver getDriver() {
return webDriver.get();
}
static void setWebDriver(WebDriver driver) {
webDriver.set(driver);
}
}
Create Webdriver Listener class :
import org.openqa.selenium.WebDriver;
import org.testng.IInvokedMethod;
import org.testng.IInvokedMethodListener;
import org.testng.ITestResult;
public class WebDriverListener implements IInvokedMethodListener {
#Override
public void beforeInvocation(IInvokedMethod method, ITestResult testResult) {
if (method.isTestMethod()) {
String browserName = method.getTestMethod().getXmlTest().getLocalParameters().get("browserName");
WebDriver driver = LocalDriverFactory.createInstance(browserName);
LocalDriverManager.setWebDriver(driver);
}
}
#Override
public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
if (method.isTestMethod()) {
WebDriver driver = LocalDriverManager.getDriver();
if (driver != null) {
driver.quit();
}
}
}
}
Test Class
public class ThreadLocalDemo {
#Test
public void testMethod1() {
invokeBrowser("https://www.google.com/");
}
#Test
public void testMethod2() {
invokeBrowser("http://www.facebook.com");
}
private void invokeBrowser(String url) {
System.out.println("Thread id = " + Thread.currentThread().getId());
System.out.println("Hashcode of webDriver instance = " + LocalDriverManager.getDriver().hashCode());
LocalDriverManager.getDriver().get(url);
}
}
Suite Xml File :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite" parallel="methods">
<listeners>
<listener class-name="path-to-class-WebDriverListener"></listener>
</listeners>
<test name="Test">
<parameter name="browserName" value="firefox"></parameter>
<classes>
<class name="path-to-class-ThreadLocalDemo" />
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->

java.lang.ClassCastException error when implementing extent reporting in TestNG XML file

I getting an error telling java.lang.ClassCastException: [My extent report class name] cannot be cast to org.testng.ITestNGListener when running the TestNG XML file as a Test suite.
I have automated a web page using page factory design technique using MAVEN and TestNG which consists of 6 page classes objects initialize in one package. I also written extent report listener class in another package. In addition to this I also has a base class in another package which is the super class of all 6 page object initialize classes. I have written test cases for 3 page classes and base class is the super class of these classes as well.
I have generated TestNG XML file by adding all 3 page test cases and adding extent report class as a listener for this XML file.
I will show the structure of my framework by including one class from each package below.
Page object initialize package - Login class
package com.crm.qa.pages;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import com.crm.qa.base.TestBase;
import com.crm.qa.util.TestUtil;
public class LoginPage extends TestBase {
#FindBy(name="username")
WebElement userName;
#FindBy(name="password")
WebElement password;
#FindBy(xpath="//input[#type='submit']")
WebElement loginBtn;
#FindBy(xpath="//button[contains(text(),'Sign Up')]")
WebElement signupBtn;
#FindBy(xpath="//img[#class = 'img-responsive']")
WebElement crmLogo;
//Initializing the page objects
public LoginPage() {
PageFactory.initElements(driver, this);
}
public String validateLoginPageTitle() {
return driver.getTitle();
}
public boolean validateCRMLogo() {
return crmLogo.isDisplayed();
}
public HomePage login (String un, String pwd) {
userName.sendKeys(un);
password.sendKeys(pwd);
loginBtn.submit();
driver.manage().timeouts().pageLoadTimeout(TestUtil.PAGE_LOAD_TIMEOUT, TimeUnit.SECONDS);
// Actions act = new Actions(driver);
// act.moveToElement(loginBtn).click().build().perform();
return new HomePage();
}
}
Base Package - Test Base class
package com.crm.qa.base;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.events.EventFiringWebDriver;
import com.crm.qa.util.TestUtil;
import com.crm.qa.util.WebEventListener;
public class TestBase {
public static WebDriver driver;
public static Properties prop;
public static EventFiringWebDriver e_driver;
public static WebEventListener eventListener;
public TestBase() {
try {
prop = new Properties();
FileInputStream ip = new FileInputStream("C:\\Users\\i7\\git\\TestDesignFramework1\\Suresh.com.automationLearning\\src"
+ "\\main\\java\\com\\crm\\qa\\config\\config.properties");
prop.load(ip);
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void initialization () {
String browserName = prop.getProperty("browser");
if(browserName.equals("chrome")) {
System.setProperty("webdriver.chrome.driver", "E:\\C\\Selenium\\Chrome Driver\\Extract\\chromedriver.exe");
driver = new ChromeDriver();
}
else if (browserName.equals("firefox")) {
System.setProperty("webdriver.gecko.driver", "E:\\C\\Selenium\\GeckoDriver\\Extract\\geckodriver.exe");
driver = new FirefoxDriver();
}
e_driver = new EventFiringWebDriver(driver);
eventListener = new WebEventListener();
e_driver.register(eventListener);
driver = e_driver;
driver.manage().window().maximize();
driver.manage().deleteAllCookies();
driver.get(prop.getProperty("url"));
driver.manage().timeouts().pageLoadTimeout(TestUtil.PAGE_LOAD_TIMEOUT, TimeUnit.SECONDS);
driver.manage().timeouts().implicitlyWait(TestUtil.IMPLICIT_WAIT, TimeUnit.SECONDS);
}
}
Test cases package - Login page test class
package com.crm.qa.pages.testcases;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeMethod;
import org.testng.Assert;
import com.crm.qa.base.TestBase;
import com.crm.qa.pages.HomePage;
import com.crm.qa.pages.LoginPage;
public class LoginPageTest extends TestBase {
LoginPage loginPage;
HomePage homepage;
public LoginPageTest() {
super();
}
#BeforeMethod
public void setUp() {
initialization();
loginPage = new LoginPage();
}
#Test(priority = 1)
public void loginPageTitle() {
// extentTest = extent.createTest("loginPageTitle");
String title = loginPage.validateLoginPageTitle();
Assert.assertEquals(title, "#1 Free CRM software in the "
+ "cloud for sales and service");
}
#Test(priority = 2)
public void crmLogoImageTest() {
// extentTest = extent.createTest("crmLogoImageTest");
boolean flag = loginPage.validateCRMLogo();
Assert.assertTrue(flag);
}
#Test(priority = 3)
public void loginTest() {
// extentTest = extent.createTest("loginTest");
homepage = loginPage.login(prop.getProperty("username"), prop.getProperty("password"));
System.out.println("Successfully login to the home page of freeCRM");
}
#AfterMethod
public void tearDown() {
driver.quit();
}
}
Test Util package - Extent report listener class
package com.crm.qa.ExtentReport;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.markuputils.ExtentColor;
import com.aventstack.extentreports.markuputils.MarkupHelper;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
public class ExtentReportListener {
public static ExtentHtmlReporter htmlReporter;
public static ExtentReports extent;
public static ExtentTest extentTest;
#BeforeSuite
public void setUp() {
htmlReporter = new ExtentHtmlReporter("C:\\Users\\i7\\git\\TestDesignFramework1\\Suresh.com.automationLearning\\Reporting\\ExtentReporting.html");
extent = new ExtentReports();
extent.attachReporter(htmlReporter);
}
#AfterMethod
public void getResult(ITestResult result) {
if (result.getStatus()==ITestResult.FAILURE) {
extentTest.fail(MarkupHelper.createLabel(result.getName()+" Test Case Failed", ExtentColor.RED));
extentTest.fail(result.getThrowable());
}
else if (result.getStatus()==ITestResult.SUCCESS) {
extentTest.pass(MarkupHelper.createLabel(result.getName()+" Test Case Passed", ExtentColor.GREEN));
extentTest.pass(result.getThrowable());
}
else {
extentTest.skip(MarkupHelper.createLabel(result.getName()+" Test Case Skipped", ExtentColor.ORANGE));
extentTest.skip(result.getThrowable());
}
}
#AfterSuite
public void tearDown() {
extent.flush();
}
}
TestNG XML file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Free CRM Test Application Regression Test Suite">
<listeners>
<listener class-name="com.crm.qa.ExtentReport.ExtentReportListener">
</listener>
</listeners>
<test thread-count="5" name="Free CRM app regression test cases">
<classes>
<class name="com.crm.qa.pages.testcases.LoginPageTest"/>
<class name="com.crm.qa.pages.testcases.HomePageTest"/>
<class name="com.crm.qa.pages.testcases.ContactsPageTest"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
TestNG is working as designed.
Whenever you add an entry such as the one below, into your testng suite xml file
<listeners>
<listener class-name="com.crm.qa.ExtentReport.ExtentReportListener"/>
</listeners>
TestNG expects that the class implements one of the sub-interfaces of org.testng.ITestNGListener
Your class doesn't do that, which is what is triggering the exception.
Please go through the relevant extent reports documentation to understand how to correctly work with extent reports.
The solution is to use "implements IReporter" inside testNG Report class.
In your case the testNG reporting class is "ExtentReportListener" so you have to type:
public class FinalReport implements IReporter
{}

Selenium "no such session" error on TestNG testing.xml

I am trying to run a simple test with crossbrowsers using testing.xml of TestNG. Script runs fine on the first run which is Chrome. But gives "no such session" error on the firefox test beginning.
I simplified the codes to make it easy to read. I hope it helps.
Here's my TestNG testing.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="DefaultSuite" thread-count="1" parallel="tests">
<test name="ChromeTest">
<parameter name="browser" value="Chrome" />
<classes>
<class name="loginTest">
</class>
</classes>
</test>
<test name="FirefoxTest">
<parameter name="browser" value="Firefox" />
<classes>
<class name="loginTest">
</class>
</classes>
</test>
</suite>
And here's my code
public class loginTest {
private static WebDriver driver;
#BeforeTest
#Parameters("browser")
public void testSetup(String browser) throws Exception {
//Check if parameter passed from TestNG is 'firefox'
if(browser.equalsIgnoreCase("firefox")){
driver = webBrowsers.browserAl("Firefox");
}
//Check if parameter passed as 'chrome'
else if(browser.equalsIgnoreCase("chrome")){
driver = webBrowsers.browserAl("Chrome");
}
else{
//If no browser passed throw exception
throw new Exception("Browser is not correct");
}
}
#Test(priority=0)
public void LoginTest(){
driver.get("http://www.hurriyet.com.tr/");
}
#AfterTest
public void kapat() {
webBrowsers.closeDriver();
}
}
and finally my webBrowsers.class
public class webBrowsers {
private static WebDriver driver = null;
private static String browserName;
public static WebDriver browserAl(String browserName) {
if (browserName.equals("Firefox")) {
if (driver == null) {
System.setProperty("webdriver.gecko.driver","./drivers/geckodriver.exe");
driver = new FirefoxDriver();
}
} else if (browserName.equals("Edge")) {
if (driver == null) {
System.setProperty("webdriver.edge.driver", "./drivers/MicrosoftWebDriver.exe");
driver = new EdgeDriver();
}
} else if (browserName.equals("Chrome")) {
if (driver == null) {
System.setProperty("webdriver.chrome.driver","./drivers/chromedriver.exe");
driver = new ChromeDriver();
}
}
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
driver.manage().timeouts().pageLoadTimeout(60, TimeUnit.SECONDS);
return driver;
}
public static void closeDriver() {
driver.close();
}
}
The problem is in your class webBrowsers. You are working with a static webdriver instance. So for the first <test> execution the driver instance will not be null and it would get instantiated properly. But when you execute the second <test> tag, the condition driver == null would be false, and you end up getting reference to the webdriver object that was created for the previous <test> tag. To make matters worse, you have a #AfterTest method which cleans up the driver instance as well. So you are now essentially working with a valid webdriver instance but for which the opened session has already been cleaned up.
End result : Your error.
Here's a cleaned up version of your code that should get past this problem.
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import java.util.concurrent.TimeUnit;
public class WebBrowsers {
public static WebDriver browserAl(String browserName) {
WebDriver driver = null;
if (browserName.equals("Firefox")) {
System.setProperty("webdriver.gecko.driver", "./drivers/geckodriver.exe");
driver = new FirefoxDriver();
} else if (browserName.equals("Edge")) {
System.setProperty("webdriver.edge.driver", "./drivers/MicrosoftWebDriver.exe");
driver = new EdgeDriver();
} else if (browserName.equals("Chrome")) {
System.setProperty("webdriver.chrome.driver", "./drivers/chromedriver.exe");
driver = new ChromeDriver();
}
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
driver.manage().timeouts().pageLoadTimeout(60, TimeUnit.SECONDS);
return driver;
}
public static void closeDriver(WebDriver driver) {
if (driver != null) {
driver.quit();
}
}
}
Here's how your test class would look like
import org.openqa.selenium.WebDriver;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
public class LoginTest {
private WebDriver driver;
#BeforeTest
#Parameters("browser")
public void testSetup(String browser) throws Exception {
//Check if parameter passed from TestNG is 'firefox'
if (browser.equalsIgnoreCase("firefox")) {
driver = WebBrowsers.browserAl("Firefox");
}
//Check if parameter passed as 'chrome'
else if (browser.equalsIgnoreCase("chrome")) {
driver = WebBrowsers.browserAl("Chrome");
} else {
//If no browser passed throw exception
throw new Exception("Browser is not correct");
}
}
#Test
public void loginTest() {
driver.get("http://www.hurriyet.com.tr/");
}
#AfterTest
public void kapat() {
WebBrowsers.closeDriver(driver);
}
}

How to close a window after running each test case in Selenium IntelliJ?

I have a set of three test cases, and I want to avoid keeping open multiple browser windows since I automated this process in Selenium. Is there a way to close a browser after each test case is finished without giving an error?
Using close() and quit() both give me error codes of 1.
We use below approach to handle these issue.
1) create a base class which has beforeSuite , beforeTest , afterTest , afterSuite methods which will run always.
2) Each test plan should extend this class to create driver and close the driver.
BasePage.java
package com.test.test3;
import java.lang.reflect.Method;
import java.util.Date;
import org.openqa.selenium.WebDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterSuite;
public class BasePage {
public WebDriver driver = null;
private Date start;
/*
* Below method will initialize the driver once test method started
* execution
*/
public void initializeDriver(WebDriver driver) {
this.driver = driver;
}
/*
* Below method will kill driver
*/
public void tearDown() {
if (this.driver != null) {
this.driver.quit();
}
}
#AfterMethod(alwaysRun = true)
public void afterTestMethod(Method method) {
// Clean ups for test level services
tearDown();
}
#AfterSuite(alwaysRun = true)
public void afterTestSuite() {
tearDown();
}
}
Testplan.java
package com.test.test3;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.Test;
public class TestPlan extends BasePage{
#Test(groups = { "test"})
public void test() {
FirefoxDriver driver = new FirefoxDriver();
driver.get("http://www.google.com");
System.out.println("TestAutomation test");
}
#Test(groups = { "test"})
public void test1() {
FirefoxDriver driver = new FirefoxDriver();
driver.get("http://www.google.com");
System.out.println("TestAutomation test");
}
#Test(groups = { "test"})
public void test2() {
FirefoxDriver driver = new FirefoxDriver();
driver.get("http://www.google.com");
System.out.println("TestAutomation test");
}
}
testNg.xml
<suite name="API TEST CASES">
<test name="api test" parallel="methods">
<groups>
<run>
<include name="test" />
</run>
</groups>
<classes>
<class name="com.test.test3.TestPlan" />
</classes>
</test>