java factory pattern issue - java

I am using selenium webdriver for testing and want to give the opportunity to select which browser to run each set of tests in. I have used a factory to achieve this but for some reason I can't seem to get it working. Here is what I have
class WebDriverFactory {
private WebDriver driver;
private String baseUrl;
private StringBuffer verificationErrors = new StringBuffer();
private WebDriverFactory() {
}
public static void setChromePath() {
// Set file path here
}
public static void setIEPath() {
// Set file path here
}
public static WebDriver getWebDriver(String type) {
System.out.println("choose a browser:");
Scanner scan = new Scanner(System.in);
scan.next();
if (type.equalsIgnoreCase("chrome")) {
return createChrome();
} else if (type.equalsIgnoreCase("firefox")) {
return createFirefox();
} else if (type.equalsIgnoreCase("IE")) {
return createInternetExplorer();
} else {
return null;
}
}
private static WebDriver createChrome() {
System.setProperty("webdriver.chrome.driver", "C:/Program Files
(x86)/Google/Chrome/Application/chromedriver_win32_2.1/chromedriver.exe");
DesiredCapabilities capabilities = DesiredCapabilities.chrome();
ChromeOptions chromeOptions = new ChromeOptions();
capabilities.setCapability("chrome.binary",
"C:/AppData/Local/Google/Chrome/Application/chrome.exe");
WebDriver driver = new ChromeDriver(capabilities);
return driver;
}
private static WebDriver createFirefox() {
WebDriver driver = new FirefoxDriver();
return driver;
}
private static WebDriver createInternetExplorer() {
File file = new File("C:/Utils/IEDriverServer_Win32_2.33.0/IEDriverServer.exe");
System.setProperty("webdriver.ie.driver", file.getAbsolutePath());
WebDriver driver = new InternetExplorerDriver();
return driver;
}
}
And in the test class I have:
String type = null;
WebDriverFactory.getWebDriver(type);
When I enter a string the scanner doesn't seem to do anything and the browser doesn't open? Could anyone help me out?
I now have this in the test class:
System.out.println("choose a browser:");
Scanner scan = new Scanner(System.in);
String type = scan.next();
WebDriverFactory.getWebDriver(type);

As Jayan mentioned, you should have got a NPE since you are passing null to the getWebDriver method. However, keeping that aside, in your code, you are reading a string using scan.next() statement, but you haven't assigned the read value to the variable type.
Saying that, your statement should be as below:
Scanner scan = new Scanner(System.in);
type = scan.next();
Apart from that, I don't see any use of passing type variable to the getWebDriver method, as you are anyhow, taking the input from user for type in getWebDriver method.

You should first correct the method getWebDriver(type) so it doesn't return null always! And then see where you go. As Mubin suggested, you could let go of the input parameter type if you are going to read the input inside the method. However, Based on the discussion in comments to this answer here's how the method getWebDriver(type) should look like:
public static WebDriver getWebDriver(String type) {
if (type.equalsIgnoreCase("chrome")) {
return createChrome();
} else if (type.equalsIgnoreCase("firefox")) {
return createFirefox();
} else if (type.equalsIgnoreCase("IE")) {
return createInternetExplorer();
} else {
return null;
}
}
OP was passing null to this method in the test implementation, and also not assigning the value read by Scanner. It isn't clear what the OP was expecting. As Jayan mentioned, we should get an NullPointerException.

Related

Extent Report 3 Add Screenshot

I"m trying to build selenium with extent report but i could not get the save screenshot function working because i cannot reference the WebDriver object in the ITestListener class. Below is my sample code:
Test Runner.java:
#Listeners({com.peterwkc.Listener.TestListener.class})
public class ChromeTestManager {
private WebDriverManager webDriverManager = new WebDriverManager();
private WebDriver driver;
private LoginPages loginPages;
private AdminPages adminPages;
#BeforeClass
//#Parameters({"browser"})
public void setupTest(/*String browser*/) throws MalformedURLException {
System.out.println("BeforeMethod is started. " + Thread.currentThread().getId());
// Set & Get ThreadLocal Driver with Browser
webDriverManager.createDriver("chrome");
driver = webDriverManager.getDriver();
// Pages Object Initialization
loginPages = PageFactory.initElements(driver, LoginPages.class);
logoutPages = PageFactory.initElements(driver, LogoutPages.class);
adminPages = PageFactory.initElements(driver, AdminPages.class);
}
#DataProvider(name = "loginCredentials")
public static Object[][] getLoginCredentials() {
return new Object [][] {{ "Admin123", "admin123" }, {"testUser", "test"}, {"test", "test"}};
}
#Test(groups= {"Login"}, description="Invalid Login", priority = 0, dataProvider = "loginCredentials", invocationCount = 3)
public void login_invalid(String username, String password) {
loginPages.login_invalid(driver, username, password);
}
}
TestListener.java
public class TestListener implements ITestListener {
//Extent Report Declarations
private static ExtentReports extent = ExtentManager.createInstance();
private static ThreadLocal<ExtentTest> test = new ThreadLocal<>();
public TestListener() {
}
#Override
public synchronized void onTestFailure(ITestResult result) {
System.out.println((result.getMethod().getMethodName() + " failed!"));
test.get().fail("Exception Error : \n" + result.getThrowable());
/*String feature = getClass().getName();
String screenShot;
try {
screenShot = CaptureScreenshot.captureScreen(driver, CaptureScreenshot.generateFileName(feature));
test.get().addScreenCaptureFromPath(screenShot);
test.get().log(Status.FAIL, screenShot);
} catch (IOException ex) {
LogManager.logger.log(Level.INFO, "Exception: " + ex.getMessage());
}*/
}
}
Questions:
How to pass the WebDriver object from TestRunner.java to TestListener
class?
How to save screenshot in extent report 3?
Anything wrong with my code?
please help, thanks in advance!
Below are the steps to do this :
1 : Passing WebDriver object to Listener class
First create below method in ChromeTestManager class or at any another location from where you can call it, here suppose that it is present in ChromeTestManager class:
public static ITestContext setContext(ITestContext iTestContext, WebDriver driver) {
iTestContext.setAttribute("driver", driver);
return iTestContext;
}
It will set the driver object to the TestContext.
Now change your #BeforeClass setUp method to accept parameter ITestContext, below is the code :
public class ChromeTestManager {
private WebDriverManager webDriverManager = new WebDriverManager();
private WebDriver driver;
private LoginPages loginPages;
private AdminPages adminPages;
private static ITestContext context; // creating a ITestContext variable
#BeforeClass
//#Parameters({"browser"})
public void setupTest(ITestContext iTestContext) throws MalformedURLException {
System.out.println("BeforeMethod is started. " + Thread.currentThread().getId());
// Set & Get ThreadLocal Driver with Browser
webDriverManager.createDriver("chrome");
driver = webDriverManager.getDriver();
this.context = setContext(iTestContext, driver); // setting the driver into context
// Pages Object Initialization
loginPages = PageFactory.initElements(driver, LoginPages.class);
logoutPages = PageFactory.initElements(driver, LogoutPages.class);
adminPages = PageFactory.initElements(driver, AdminPages.class);
}
When you run this, it will run smoothly and will not produce an error (If you are thinking that how I will pass ITestcontext context, It is handled internally)
Now the driver has been added as an object to the ITestcontext ;
Now Accessing the driver in Listener :
#Override
public synchronized void onTestFailure(ITestResult result) {
WebDriver driver = (WebDriver) result.getTestContext().getAttribute("driver"); // here we are accessing the driver object that we added in Test class
}
2. Saving ScreenShot in extent report 3 (I am using dependency 3.1.5 in maven)
#Override
public synchronized void onTestFailure(ITestResult result) {
System.out.println("!!!!!!!!!!!!!!!!!!!! Test Failed !!!!!!!!!!!!!!!!!!!!");
WebDriver driver = (WebDriver) result.getTestContext().getAttribute("driver"); // accessing driver here
String feature = getClass().getName();
String screenShot;
try {
screenShot = CaptureScreenshot.captureScreen(driver, CaptureScreenshot.generateFileName(feature));
test.addScreenCaptureFromPath(screenShotPath); // I am assuming that the "screenShot" is fully qualified path with extension e.g "C:\Users\12345\Desktop\sfgfdh.PNG"
} catch (IOException ex) {
LogManager.logger.log(Level.INFO, "Exception: " + ex.getMessage());
}
}
3. Is there anything wrong with your code ?
No
You just need driver in Listener class and while adding screenshot in extent report ,
make sure that the path to screenshot is correct and is fully qualified path with extension.
Please let me know if you face an issue in this.
First of all don't instantiate Your webDriver in #BeforeClass, because this is called only once as annotation say before class, try using interface ITestListener and using beforeInvocation implementation for initialisation of WebDriver.
Second, You can't call PageFactory for all PageObjects at once, how do You think all 3 pages are initialised at once, this should be achieved in constructor for each page object, and when You init you page object (new Login) the elements are initialised as well, so this is not ok:
// Pages Object Initialization
loginPages = PageFactory.initElements(driver, LoginPages.class);
logoutPages = PageFactory.initElements(driver, LogoutPages.class);
adminPages = PageFactory.initElements(driver, AdminPages.class);
Third I don't see initialisation of ExtentReport test. It should looks something like this:
ExtentTest extentTest = ExtentTestManager.startTest(method.getName(), "");
Here is an example part of code from my implementation of calling screenshot, I'am calling it from afterInvocation, because I'm using concurrent driver initialisation, and so it had to be from here, but also can be achived via onTestFailure implementation:
public synchronized void afterInvocation(IInvokedMethod method, ITestResult testResult){
if (method.isTestMethod() && testResult.getStatus()==2) {
File scrFile = (dataMethod.getAndroidDriver()).getScreenshotAs(OutputType.FILE);
String dest = System.getProperty("user.dir") + "/resources/screenshots/" + dataMethod.getDriver().getSessionId() + ".png";
File destination = new File(dest);
try {
FileUtils.copyFile(scrFile, destination);
dataMethod.setScreenshotPath(destination.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
System.err.println("Path:" + dataMethod.getScreenshotPath());
}
You have to think more about structure,
Hope this helps...

How to quit the page if i type hello world on google search using selenium?

Hi guys I want to quit the page afte I type "Hello World" in google search using firefox browser and selenium
WebDriver driver = null;
public static void main(String args[]) {
SimpleSelenium ss = new SimpleSelenium();
ss.openBrowser();
ss.getPage();
ss.quitPage();
}
private void openBrowser() {
System.setProperty("webdriver.gecko.driver", "C:/geckodriver.exe");
driver = new FirefoxDriver();
}
private void quitPage() {
driver.quit();
}
private void getPage() {
driver.get("http://www.google.com");
}
1) Create a Junit test class
2) Initialize the driver in your setup method like
ChromeDriver driver = new ChromeDriver();//Download chromeDriver.exe file and point to location where you have installed the like as you mentioned. `driver.System.setProperty("webdriver.gecko.driver", "C:/geckodriver.exe");`
3) Create a test method with your business logic to type hello world
3) Create After and Before Class annotations for the methods .In After class annotation method you can write driver.quit.
You can refer to following link for more clarity
https://www.guru99.com/selenium-tutorial.html
I am Added sample format which is written Using java and testNG..Here Every time First before method will run then 1st test case will execute then after method will work then again before method work then next test case......In this way you can manage your test case and it will also generate Report also.Here you will get better explanation.
public class GoogleTest {
FirefoxDriver driver;
#BeforeMethod
public void setUp1() throws Exception {
System.setProperty("webdriver.gecko.driver", "D:\\\\ToolsQA\\trunk\\Library\\drivers\\geckodriver.exe");
WebDriver driver = new FirefoxDriver();
driver.get("https://www.google.com");
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Test
public void GoogleInputField() throws InterruptedException {
System.out.println("Hello world");
System.out.println("Hello world");
//Write Your test case for test case 1
}
#Test
public void google suggestion() throws InterruptedException {
//Write Your test case for test case 1
}
#AfterMethod
public void getResult(ITestResult result) throws IOException {
driver.quit();
}
}
Dont forget to add Firefox driver on gecko.driver path
I am assuming that you want to open the browser using selenium, load google and then listen till you MANUALLY enter "hello world" in the input box. The method listenForHelloWorld() will do that.
public static void main(String args[]) {
SimpleSelenium ss = new SimpleSelenium();
ss.openBrowser();
ss.getPage();
ss.listenForHelloWorld();
ss.quitPage();
}
private void listenForHelloWorld() {
// Get the search field
WebElement searchField = driver.findElement(By.name("q"));
int count = 1;
while (count++ < 20) {
// if search field value is "hellwo world" break loop which will eventallu lead to `quit()` as it is the next method to exit.
if (searchField.getAttribute("value").equalsIgnoreCase("hello world")) {
break;
}
Thread.sleep(5000)
}
}
If you are asking how to enter "hello world" in browser automatically use below.
driver.findElement(By.name("q")).sendKeys("hello world");

Multiple Browser Profiles for Multiple (Concurrent) Test Execution?

Multiple Browser Profiles for Multiple (Concurrent) Test Execution?
Is this even possible?
For example I can execute two tests at the same time but when two tests open at the same time within the same browser they seem to share the same cookies.
Please find my main Browser factory class listed below, can anyone advise the best way to alter my code, or settings required which will enable me to meet my objective?
Thanks for your help
public class BrowserFactory implements ISuiteListener {
private static WebDriver webdriver;
public static WebDriver getDriver() throws Exception {
try {
Properties p = new Properties();
FileInputStream fi = new FileInputStream(Constant.CONFIG_PROPERTIES_DIRECTORY);
p.load(fi);
String browserName = p.getProperty("browser");
switch (browserName) {
//firefox setup
case "firefox":
if (null == webdriver) {
System.setProperty("webdriver.gecko.driver", Constant.GECKO_DRIVER_DIRECTORY);
webdriver = new FirefoxDriver();
}
break;
//chrome setup
case "chrome":
if (null == webdriver) {
System.setProperty("webdriver.chrome.driver", Constant.CHROME_DRIVER_DIRECTORY);
DesiredCapabilities caps = DesiredCapabilities.chrome();
LoggingPreferences logPrefs = new LoggingPreferences();
logPrefs.enable(LogType.BROWSER, Level.ALL);
caps.setCapability(CapabilityType.LOGGING_PREFS, logPrefs);
webdriver = new ChromeDriver(caps);
}
break;
//IE setup
case "ie":
if (null == webdriver) {
System.setProperty("webdriver.ie.driver", Constant.IE_DRIVER_DIRECTORY);
webdriver = new InternetExplorerDriver();
}
break;
}
} catch (Exception e) {
System.out.println("Unable to load browser! - Exception: " + e.getMessage());
}
return webdriver;
}
#AfterClass
public void quitDriver() throws Exception {
if (null != webdriver) {
getDriver().manage().deleteAllCookies();
webdriver.quit();
webdriver = null;
}
// Output the time when a test class has ended
String tempTimeEndClass = new SimpleDateFormat("hh.mm.ss").format(new Date());
System.out.println("\nTEST CLASS END TIME: " + tempTimeEndClass);
}
}
Yes, of course it is possible!
They share the same cookies because the instance of the WebDriver you are creating is static, trying removing the static modifier so on every launch of the webdriver you get a unique instance.
public webdriver driver; public WebDriver getDriver() throws Exception
{ }
If the above isn't enough and you want to do additional things with the profiles; just pass it as a parameter via xml or as a String var in the method:
currentProfile = "user-data-dir=/path/to/your/custom/profile";
ChromeOptions options = new ChromeOptions();
options.addArguments(currentProfile);
Again, careful here currentProfile needs to be an instance variable not a static one!
Best of luck!

Selenium Webdriver TestNG tests are "overwriting" each other

I am trying to run Selenium Webdriver tests in parallel on a single machine, using TestNG. I have 3 #Test methods, where 3 different users log in to the same application and reach the home page. I need #Test methods to run in parallel, and write to an ExtentReports report.
My problem is, despite 3 completely different methods in different classes, one of the users will be logged into 2 out of 3 of the browsers, leaving a user out.
The login method is located in a PageFactory page object class.
Here are my 3 test methods:
#Test(enabled = true, priority = 0)
public void JohnLogin() throws Exception {
ExtentTest t = ClientReportFactory.getTest();
try {
Login objLogin = new Login(getDriver());
String username = "John";
String password = "Password";
objLogin.SignIn(username, password);
HomePage objHomePage = new HomePage(getDriver());
assertTrue(objHomePage.clientName.getText().c‌​ontains("John"));
} catch (Exception e) {
}
}
#Test(enabled = true, priority = 1)
public void BobLogin() throws Exception {
ExtentTest t = ClientReportFactory.getTest();
try {
Login objLogin = new Login(getDriver());
String username = "Bob";
String password = "Password";
objLogin.SignIn(username, password);
HomePage objHomePage = new HomePage(getDriver());
assertTrue(objHomePage.clientName.getText().c‌​ontains("Bob"));
} catch (Exception e) {
}
}
#Test(enabled = true, priority = 2)
public void SamLogin() throws Exception {
ExtentTest t = ClientReportFactory.getTest();
try {
Login objLogin = new Login(getDriver());
String username = "Sam";
String password = "Password";
objLogin.SignIn(username, password);
HomePage objHomePage = new HomePage(getDriver());
assertTrue(objHomePage.clientName.getText().c‌​ontains("Sam"));
} catch (Exception e) {
}
}
So, if I pause the tests on the Homepage. I will have 2 browser windows opened as "John", one "Bob" and no "Sam"... Causing failures.
Here's the PageFactory Object's login method.
public void SignIn(String strUsername, String strPassword) throws InterruptedException {
WebDriverWait wait = new WebDriverWait(driver, 15);
username.clear();
username.sendKeys(strUsername);
password.clear();
password.sendKeys(strPassword);
submit.click();
wait.until(ExpectedConditions.visibilityOf(homePagePanel));
}
At first I was sure the problem was in the #BeforeMethod threading (As in, the tests were in a different thread than the #Before and #After). But I don't see how that could be the case. The Base Test method successfully opens and closes 3 browsers. It just seems like the #Test methods use each other's data! But just in case, here's my #Before and #After, with my Threading code.
public class BaseTest {
public String browser;
private ThreadLocal<WebDriver> threadedDriver = new ThreadLocal<WebDriver>();
#BeforeMethod(alwaysRun = true)
#Parameters({ "browser"})
public void setup(String browser)throws MalformedURLException,
InterruptedException {
WebDriver driver = null;
if (browser.equalsIgnoreCase("Internet Explorer")) {
System.setProperty("webdriver.ie.driver", "C:\\Selenium\\IEDriverServer.exe");
driver = new InternetExplorerDriver();
} else if (browser.equalsIgnoreCase("Firefox")) {
System.setProperty("webdriver.gecko.driver", "C:\\Selenium\\geckodriver.exe");
driver = new FirefoxDriver();
} else if (browser.equalsIgnoreCase("chrome")) {
System.setProperty("webdriver.chrome.driver", "C:\\Selenium\\chromedriver.exe");
driver = new ChromeDriver();
} else if (browser.equalsIgnoreCase("MicrosoftEdge")) {
System.setProperty("webdriver.edge.driver", "C:\\Selenium\\MicrosoftWebDriver.exe");
driver = new EdgeDriver();
}
setWebDriver(driver);
this.browser = browser;
ClientReportFactory.getTest(ExtentTestName, ExtentTestDescription);
baseURL = "testApp.com";
driver.get(baseURL);
driver.manage().window().maximize();
}
public WebDriver getDriver(){
return threadedDriver.get();
}
public void setWebDriver(WebDriver driver) {
threadedDriver.set(driver);
}
#AfterMethod
public void afterMethod() {
ClientReportFactory.closeTest(ExtentTestName, ExtentTestDescription);
getDriver().quit();
threadedDriver.set(null);
}
#AfterSuite
public void afterSuite() {
ClientReportFactory.closeReport();
if (getDriver() != null) {
getDriver().quit();
} else {
System.out.println("Drivers already closed");
}
}
Assuming that all of your #Test methods are in different classes, I am guessing that the problem is perhaps due to the fact that your ThreadLocal variable is NOT STATIC but is an instance variable. This causes the behaviour to be per thread per instance rather than the desired behaviour viz., per thread across all instances. You can refer to this StackOverFlow thread for a better explanation on this.
You would resort to using an instance variant of ThreadLocal if and only if all your #Test methods belong to the same test class (Because now you are only trying to ensure that the class level data member WebDriver is shared in a thread safe manner across all the test methods that belong to the same test class)
So if each of your #Test methods reside in its own Test class, then please try changing:
private ThreadLocal<WebDriver> threadedDriver = new ThreadLocal<WebDriver>();
to
private static ThreadLocal<WebDriver> threadedDriver = new ThreadLocal<WebDriver>();
You could try this.
public class DriverFactory(){
private static ThreadLocal<WebDriver> driverThread;
public WebDriver driver;
#Parameters("browser")
public WebDriver instantiateDriverObject(String browser) {
DriverFactory factory = new DriverFactory();
driver = factory.createInstance(browser); //Driver instantiation goes here
driverThread = new ThreadLocal<WebDriver>() {
#Override
protected WebDriver initialValue() {
webDriverPool.add(driver);
return driver;
}
};
return driver;
}
public WebDriver getDriver() {
return driverThread.get();
}
}

HtmlUnitDriver browser emulate mode

I have created a simple factory method that creates a HtmlUnitDriver in a specified mode. For example
public static HtmlUnitDriver createHtmlUnitDriver(String browserMode) {
switch(browserMode.toLowerCase()) {
case "chrome":
return new HtmlUnitDriver(DesiredCapabilities.chrome());
case "firefox":
return new HtmlUnitDriver(DesiredCapabilities.firefox());
case "ie":
case "internet explorer":
return new HtmlUnitDriver(DesiredCapabilities.internetExplorer());
case default:
return new HtmlUnitDriver(true);
}
}
I'd like to write a quick unit test of this method, but I can't see how to tell which "mode" the returned HtmlUnitDriver is in. How would I do this?
Using reflection, you can get the private field webClient
#Test
public void test() throws Exception {
WebDriver driver = new HtmlUnitDriver(BrowserVersion.CHROME);
WebClient webClient = (WebClient) get(driver, "webClient");
System.out.println(webClient.getBrowserVersion());
System.out.println(webClient.getBrowserVersion().isIE());
driver.close();
}
private static Object get(Object object, String field) throws Exception {
Field f = object.getClass().getDeclaredField(field);
f.setAccessible(true);
return f.get(object);
}

Categories

Resources