TestWatcher with Selenium - java

I have implemented TestWatcher to do certain things based on test output, however the function that i need to call which is createScreenShot in testFailed needs a driver as incoming property, sadly driver is protected and not static since all my tests running at the same time.
Trying to achieve: to have a screenshot of each failed test.
What have i tried: I tried adding an additional instance property to the class and pass it to the constructor but i get a failure with the following exception
TestResultLoggerExtension.<init>()
java.lang.NoSuchMethodException: com.opngo.nowos.utils.TestResultLoggerExtension.<init>()
WebSettings
#ExtendWith(TestResultLoggerExtension.class)
public class WebDriverSettings {
protected WebDriver driver;
protected String TARGET_URL;
#BeforeEach
public void setUp() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver(new ChromeOptions()
.addArguments("--headless")
.addArguments("window-size=1920x1480"));
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
loginToEnvironment();
}
public class TestResultLoggerExtension implements TestWatcher, AfterAllCallback {
#Override
public void testFailed(ExtensionContext context, Throwable cause) {
ScreenShotCreator.takeScreenShot(); <- driver needs to get here somehow since on any failed test i need to snap a screenshot
}

You can use ExtensionContext and Reflection to retrieve the WebDriver instance in your TestWatcher:
Object test = extensionContext.getRequiredTestInstance();
Field field = test.getClass().getDeclaredField("driver");
field.setAccessible(true);
WebDriver driver = (WebDriver) field.get(test);
If the WebDriver is not declared in the test class but inherited, use e.g. test.getClass().getSuperclass() or loop through all superclasses.
The methods in TestWatcher get invoked after the methods with #AfterAll/Each annotations. If you quit your WebDriver there, move it to your TestWatcher.

Related

How to run tests without closing webdriver using PO?

Please help me with my Selenium project.
Link for GIT repository: https://kodov#bitbucket.org/kodov/addressbook5.git
I have an application Addressbook ( https://sourceforge.net/projects/php-addressbook/ ) which I want to test.
I made several tests for Login page and page for creating a new contact.
The case is to make:
Negative test for login
Positive test for login
Then I don't need to close the browser but to run tests for creating a contact.
The problem is that I made POM and my tests and pages are in different classes, so I don't know how to quit the
Webdriver just after all tests, but not after the first one.
Maybe I need to change annotations.
You can create a Base Test class and make the other tests class extend that class.
For example:
public class BaseTest {
public static WebDriver driver;
private static boolean isTestsStarted;
public static WebDriverWait wait;
#BeforeClass
public static void setup() {
// To run setup method before all tests that extends this base class
if(!isTestsStarted) {
System.setProperty("webdriver.chrome.driver", ConfProperties.getProperty("chromedriver_path"));
driver = new ChromeDriver();
wait = new WebDriverWait(driver, Duration.ofSeconds(10));
driver.get(ConfProperties.getProperty("base_url"));
isTestsStarted = true;
}
}
// To quit driver after all tests run
// This will run only once, after all tests ran
// Another advantage of this is, it will quit driver even though you stop the program manually
static {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
driver.quit();
}));
}
}
Then extend this base class in your test classes
for your loginTest class
public class LoginTest extends BaseTest {
public static LoginPage loginPage;
public static MainPage mainPage;
#BeforeClass
public static void setup() {
// You can call driver object since you initialized it in your parent class
loginPage = new LoginPage(driver);
mainPage = new MainPage(driver);
driver.get(ConfProperties.getProperty("base_url"));
}
#Test
public void testUserCanNotLoginWithInvalidCredentials() {
loginPage.clearUsernameField().clearPasswordField()
.enterUsername(ConfProperties.getProperty("invalid_username"))
.enterPassword(ConfProperties.getProperty("invalid_password"))
.clickLoginButton();
assertThat(loginPage.getLoginButton().isDisplayed()).isTrue();
}
#Test
public void testUserCanLoginWithValidCredentials() {
loginPage.clearUsernameField().clearPasswordField()
.enterUsername(ConfProperties.getProperty("valid_username"))
.enterPassword(ConfProperties.getProperty("valid_password"))
.clickLoginButton();
assertThat(mainPage.getUserName())
.isEqualTo(ConfProperties.getProperty("valid_username"));
}
}
With the same way, you can use your driver object in other classes if you make those classes extend BaseTest class
You need to login no matter what, so you have to include the login part in both of your tests.
You can also try #Test(dependsOnMethods = { "testName" }) but I am not sure not if this works when your test is in another file.

TestNG Parallel execution always fails when i run more than one testcase, Among all only one testscript was passing

I am using Testng for parallel execution of my web testcase. Totally i am having 5 classes.
BaseClass - for initializing and closing of my browser
Core class - Mediator for all drivers initialized
Reusable methods - Click, settext, gettext... [extends Step #2 Core class, so driver comes from there only]
Page Object Class - To store all locators like name,ID,xpath.Uses all those reusable methods like click, gettext,settext.
Main Test Class.
Base Class
public class TestNGBase {
ThreadLocal<WebDriver> localdriver = new ThreadLocal<>();
#BeforeMethod
public void initialize(){
System.setProperty("webdriver.chrome.driver","C:\\SeleniumTest\\chromedriver.exe");
localdriver.set(new ChromeDriver());
}
public WebDriver driver(){
Core.setDriver(localdriver.get());
return localdriver.get();
}
#AfterMethod
public void teardown(){
localdriver.get().close();
localdriver.remove();
}
}
Core Class:
public class Core {
protected static WebDriver driver;
public static void setDriver(WebDriver driverr) {
driver = driverr;
}
}
Reusable Class:
public class WebMethods extends Core {
public WebMethods() {
}
public static void Click(By by) {
driver.findElement(by).click();
}
PageObject Class
public class pagemethods(){
By login = By.name("login");
public void login(){
WebMethods.click(login);}
}
MainTestclass1 : Will use above Pageobject
MainTestclass2 : Will use above Pageobject
MainTestclass3 : Will use above Pageobject
So in above 3 testcase when i trigger all those using testng.xml file. 3 new browser gets initialized and it successfully opens the url. But when i start using the all those reusable methods such as click(). Out of 3 Testcase, any of the two testcase is always getting failed.
I think problem starts Core class as it receives all drivers at the same time. It's collapsing something.
Can some one help me to solve this parallel execution failure problem.
Thanks
Try to not make the main class static. Create a class that makes an instance of the class and then executes. When you make a static class, the method is hanging off of that class, not an instance.
E.g.
public WebDriver
{
WebDriver myWebDriver = new WebDriver();
myWebDriver.whateverMethod();
}
When using threads avoid static. Try that first.

Create a test base page for calling desired capabilities

I was hoping for some help setting up my test frame work as its causing me issue. Im currently using zalenium in conjunction with my selenium tests. Currently im setting the desired capabilities in the #BeforeTest section of my tests:
#BeforeTest
#Parameters("browser")
public void setup(String br)throws MalformedURLException {
de = new DesiredCapabilities();
if (br.equals("chrome")) {
de.setCapability(CapabilityType.BROWSER_NAME, BrowserType.CHROME);
de.setCapability(CapabilityType.PLATFORM_NAME, org.openqa.selenium.Platform.LINUX);
}
else if(br.equals("firefox")){
de.setCapability(CapabilityType.BROWSER_NAME, BrowserType.FIREFOX);
de.setCapability(CapabilityType.PLATFORM_NAME, Platform.LINUX);
}
URL url = new URL("http://localhost:4444/wd/hub");
driver = new RemoteWebDriver(url,de);
driver.get(URL);
This allows me to run my testing in the docker environment and not on my local machine and is working correctly.
However i would like to create a base for these capabilities so i don't have to keep stating the desired capabilities for each test.
I want to do this also because I would like to set up separate classes for each page. Currently when i try this im getting a null pointer exception because the driver isnt declared. I tried to inject the Remote webdriver like so:
#Test
public void loginTest( RemoteWebdriver driver){
WebElement user_account_menu = driver.findElement(By.cssSelector("[data-user-account-settings-button]"));
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.elementToBeClickable(user_account_menu));
user_account_menu.click();
System.out.println("Login clicked successfully");
}
Im receiving the error: Cannot inject #Test annotated Method [loginTest] with [class org.openqa.selenium.remote.RemoteWebDriver
So im basically trying to figure out how i can set up these capabilities for the driver in a class and then extend them onto my tests.
From this link, it appears, you're not adding the right annotation, namely #Parameters.
However, in some other testing frameworks, it's not typical to be able to pass in variables that aren't determined at runtime, i.e. variables for objects like RemoteWebdriver won't work, but variables for strings or ints will work. From this link, it appears that what you're trying to accomplish is doable. But I recommend the following approach.
Have an enumeration:
enum BrowserType
{
Chrome, Firefox, IE;
}
Have your test base page:
public class BrowserSetup
{
public static RemoteWebdriver Initialize(string browserType)
{
switch (browserType)
{
case BrowserType.Chrome:
// set chrome capabilities and initialize your browser here
break;
case BrowserType.Firefox:
// set firefox capabilities and initialize your browser here
break;
default:
// set default case, or fail if browser type doesn't match
break;
}
}
Then, from you're test class, assuming you have a private RemoteWebdriver driver initialized somewhere in the test class, you can do the following:
#BeforeTest
#Parameters("Chrome")
public void setup(String browserType) throws MalformedURLException
{
driver = BrowserSetup.Initialize(browserType);
}

Creating a webdriver instance across classes | selenium Java

Just pitching into Java! Trying to implement BDD style framework...
I'm running into this issue
My Driver.java looks like this:
public class Driver {
public static WebDriver Instance;
#Before
public void InitializeTest() {
System.setProperty("webdriver.chrome.driver", "C://chromedriver.exe");
Instance = new ChromeDriver();
}
#After
public void TearDownTest(Scenario scenario) {
//close the browser
if (scenario.isFailed()) { //take Screenshot
System.out.println(scenario.getName());
}
Instance.close();
}
}
And my Step-definition file:
public class MyStepdefs {
public static String Url = "https://ebay.com/staging/";
LoginPage loginPage = new LoginPage();
#Given("^I login to Ebay as \"([^\"]*)\"$")
public void iLoginToEbayAs(String username) throws Throwable {
Driver.Instance.navigate().to(Url);
loginPage.setUserName().sendKeys(username);
loginPage.setPassword().sendKeys("seeeev");
}
Receiving this error:
java.lang.NullPointerException
at Steps.MyStepdefs.iLoginToEbayAs(MyStepdefs.java:4)
MyStepdefs.java:4 == Driver.Instance.navigate().to(Url);
Help me pass through this!
Your Driver class never gets initialized, I'm betting Instance is null. I think you have at least 2 options, possibly more ways but this is what i'm thinking.
Instead of using #Before, which never gets executed because you don't have any test methods in that class, just make that a static "Init" method that you call in order to initialize your Instance variable
Make your Driver class an abstract class that your MyStepDefs class extends from. When you run your method iLoginToEbayAs() it will by default call the #Before method in the parent class and initialize your Instance variable as you expected.
Currently if you set a breakpoint in your #Before method I'm betting it's never getting executed hence NPE.
You are missing the argument in the step definition. it should be given below.
#Given("^I login to Ebay as \"([^\"]*)\"$")
public void iLoginToEbayAs(String role) throws Throwable {
Driver.Instance.navigate().to(Url);
}
have you imported the driver class?

How to pass selenium webdriver instance to another class

I browsed through the site but did not find the answer I am looking.
I have
Superbase class- here I just create object of a webdriver
Baseclass- In this class I extend Superbase class, invoke the driver, and open the URL.
Clicklink class- In this class, I again extend the Superbase Class but only to find a null pointer exception. I think I am getting the exception as the driver object is not initialized.
I am just a beginner, and have not tried the browserfactory and other options, as I want to start with simple flow.
Superclass
Public class Superclass
{
public webdriver Driver;
}
Baseclass
public class Baseclass extends Superclass
{
setting capabilities and launching the browser
}
ClickLink
public class Clicklink extends Superclass
{
here I want to click on a link
driver.findelement(by.xpath("xpath").click());
// after this statement I get a null pointer exception
}
Can you please guide me here? how can I achieve the same.
Thanks much!
SuperClass and BaseClass are very poor names. Do not use the language of coding to name your classes use the language of the problem. In this case, web site application testing, use LoginPage, CartPage, ProfilePage, etc. Use the Page Object Pattern.
I suggest you use the Factory Pattern to provide the instances of WebDriver for each test. Since all those fit the idea is a page, use class extension from a standard PageObject to provide this capability. When navigating, have the current page construct an instance of the new page and pass it the current webDriver connection instance. Then any manipulations you apply to that PageObject will be automatically applied to that webDriver instance and its associated browser instance. You should also use a PageFactory to provide instances of the pageObject.
public abstract class PageObject {
public WebDriver driver;
PageObject() {
// Page can initialise its self
this.driver = BrowserFactory.webDriver();
}
PageObject(final WebDriver webDriver) {
this.driver = webDriver;
}
}
This is lot of guesswork done from my side, but please make sure, that your Superclass actually sets the driver and returns it. You can actually make it in both methods:
public class Superclass
{
public WebDriver driver;
public Superclass(){
driver = new FirefoxDriver();
}
public WebDriver getdriver(){
if (driver == null){
driver = new FirefoxDriver();
return driver;
}else{
return driver;
}
}
}
And later in methods you call it by:
public class Clicklink extends Superclass
{
getdriver().findelement(by.xpath("xpath").click());
}
If you doesn't want pass driver instance to Page Objects constructor you could create some container class for driver and put it before test and remove it after run. For example:
class Driver {
public static ThreadLocal<IWebDriver> driverInstance = new ThreadLocal<IWebDriver>();
public static IWebDriver GetDriver() {
return driverInstance.Value;
}
public static void SetDriver(IWebDriver driver) {
driverInstance.Value = driver;
}
}
and make this container field ThreadLocal to avoid problems with parallel run.
I have taken a slightly different approach than most on this thread. When I start a test session, I pass the browser name as an argument (i.e. -Dbrowser=chrome) in order to be able to test my web application with different browsers. Then I used the "browser" system property to obtain the browser name when setup() is called by my test framework. In my case, I use JUnit annotations in order to JUnit to setup all needed dependencies prior to running any tests.
#BeforeClass
public static void setup() throws Exception {
// Set up other stuff
String browser = System.getProperty("browser");
try {
SessionDataProvider.driver = TestUtils.createDriver(browser);
} catch (Exception e) {
...
}
}
The createDriver(String) is a factory method that instantiates the correct driver.
public static WebDriver createDriver(String browserName) throws Exception {
WebDriver driver = null;
try {
switch(browserName) {
case "firefox":
// code to system props and instantiate the driver
break;
case "chrome":
// code to system props and instantiate the driver
break;
case "ibrowser":
// code to system props and instantiate the driver
break;
case "edge":
// code to system props and instantiate the driver
break;
case "safari":
// code to system props and instantiate the driver
break;
default:
throw new Exception("Unsupported browser: " + browserName);
}
return driver;
}
Then, when I execute a step definition, I simply obtain the driver from the data provider class:
#And("(I click)/Click on {string}")
public void click(String arg) {
// Parse String arg and create web element locator...
try {
By locator = ...;
WebElement element = new WebDriverWait(SessionDataProvider.driver, 2)
.until(ExpectedConditions.elementToBeClickable(locator));
element.click();
} catch (Exception e) {
// handle exception
}
}
I did use below code in utility class like below
public static WebDriver setup(WebDriver driver)
{
if(driver == null) {
driver = new FirefoxDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
return driver;
}else
return driver;
//System.out.println("in method "+ driver.getTitle() );
}

Categories