I have a test class using testNG, with 4 test methods, testing one website for 4 different browsers, like the following code:
public class MyTest {
#Test
public void acceptanceFFTest()
{
}
#Test
public void acceptanceChromeTest()
{
}
#Test
public void acceptanceIETest()
{
}
#Test
public void acceptanceSafariTest()
{
}
#AfterMethod
public void tearDown()
{
webDriver.quit();
}
}
After one test method finished, the webdriver for that browser is killed in the #AfterMethod, the next browser starts with new webdriver respectively, and so on.
My problem is: if I run these test methods one by one, then they are always passed. But if I run from the test class level as a test suite with 4 test methods, then sometimes one or two of them are failed with NoElementException, But the exception is not always at one page or one element, sometimes here, sometimes there. If I run the failed tests alone again, then they are passed.
I think for the test class level, maybe the http signal sometimes is fast and sometimes is slow, the test class level is not stable as single test method, I am not sure.
Is there anyway to stabilize them? Thank you.
I would rewrite the tests so that you have a single test that runs on all browsers. You may run into some slight differences in the browser that you may have to handle but I think this overall approach is much better and more manageable. Here's a simple example of how to do this. For this example I just hardcoded a string browserType. I'm assuming you would pass this in or read it from a file or whatever. The basic idea is that you define your driver variable as a generic WebDriver. Once you determine what browser you want for that run, you instantiate the specific driver and execute the test(s).
String browserType = "firefox"; // hardcoded for the example
WebDriver driver;
switch (browserType)
{
case "firefox":
driver = new FirefoxDriver();
break;
case "ie":
driver = new InternetExplorerDriver();
break;
case "chrome":
driver = new ChromeDriver();
break;
default:
throw new Exception("browserType: " + browserType + " not defined.");
}
// do test case
driver.get("http://www.google.com");
// ... and so on
Related
I got two products to be tested by the Selenium code. As their versions are slightly different, I think having two Selenium codes for each product is better. So I created a Java that has a switch block to determine which version of Selenium Java code will run.
public class TestInit {
#Parameters("appver")
#Test
void test(String version) {
System.out.println("App version: " + version);
switch (version) {
case "8.00.25":
TestRun1 test1 = new TestRun1();
break;
case "8.00.35":
TestRun2 test2 = new TestRun2();
break;
default:
System.out.println("default version will be running");
break;
}
}
}
My textNG.xml passes parameters to the above Java class and one of the parameters is appver. However, I don't know how to run my code based on this parameter to run TestRun1 class. The variable appver receives the value from testNG.xml properly.
public class TestRun1 {
#Test
void test1(){
System.out.println("This is test run in Testrun1");
}
}
Additionally, how do you manage to handle different test cases for different versions? Do you create new Java and then override test cases on it?
Can somebody help me, please? I'm working with Selenide framework using TestNG and Java.My test are getting failed while running multiple #Test annotations for single #Test it is workingConsole error eclipse
public class LoginTest {
#BeforeTest
public void beforeTest() {
System.setProperty("webdriver.chrome.driver", ".//src//test//resources//Drivers//chromedriver.exe");
Configuration.browser = "chrome";
Configuration.timeout = 5000;
open("https://opensource-demo.orangehrmlive.com/");
}
#Test
public void Test1() {
$(By.id("txtUsername")).setValue("Admin");
$(By.id("txtPassword")).setValue("admin123");
$(By.id("btnLogin")).click();
$(By.id("welcome")).shouldHave(text("Welcome Admin"));
}
#Test
public void Test2() {
$(By.id("txtUsername")).setValue("Admin");
$(By.id("txtPassword")).setValue("admin123");
$(By.id("btnLogin")).click();
$(By.id("welcome")).shouldHave(text("Welcome Admin"));
$(By.id("welcome")).click();
$(By.xpath("//a[#href='/index.php/auth/logout']")).click();
}
}
from your console error you can see that Element not found
Element not found {By.id: txtUsername}
please check if the element -> exist of visible
First, you don't need
System.setProperty("webdriver.chrome.driver", ".//src//test//resources//Drivers//chromedriver.exe");
Since Selenide 4.7 contains WebDriverManager - a library that can automatically download latest webdriver binary file. You don't need to care about downloading geckodriver.exe or chromedriver.exe and adding it to PATH. So you can just remove this line.
Also, you may add Configuration.startMaximized = true;
For the parallel execution, I would suggest you create a new class for example
TestNGBase with the tag #BeforeClass(alwaysRun = true) and put there the action before each test. Also, each test inherits from this class
public class LoginTest extends TestNGBase
So, Test1() and Test2() should be in diff classes
If you want to have more than one test steps you can, but in the class as testStep1() , testStep2() for example.
if you want to keep this set up you already have you may use
#BeforeMethod instead of #BeforeTest
Console Output
Please help me to fix the below issue in Selenium WebDriver with Java.
I am validating eComm web based application. I have couple of test cases.
1. Sign in to the application
2. Add to item to the cart. This test case is a continuation of first test case. It means that after sign in, user is gonna add the item to the cart.
I have 2 class files, In that I have given #Test condition in both the class files.
In CommonFunctions class file, I am lauching browser in #BeforeSuite section. And closing the browser in #AfterSuite section.
I am doing it in Page object model and executing from TestNG XML.
On running the suite, First test case is getting passed. When it comes to the second test case (In the #Test condition present in the 2nd class file), the in it entering into the #Test section but getting failed immediately with out any reason.
I have tried with Implicit, Explicit wait and even thread.sleep as well. But no luck. Please can some one take a look and suggect me.
Appreciate your help!
I assume that it is getting failed for some exception and the stack trace is not getting printed somehow. Try explicitly getting it printed by:
#Test
public void SecondTestCase(){
try{
yourSecondTestCase;
}
catch(Throwable e){
e.printStackTrace();
}
}
Just don't reuse the driver.
Create it in #BeforeMethod then quit it in #AfterMethod and you should not meet any issues.
Update answering a comment question below:
In general it's not a good idea to have the tests depending on each other.
Regardless of how special you think your case is, you should never do it if you want to keep your dignity.
This does not neccessarily mean that you can't reuse 'logic' in tests, it just means that you should not expect any other test to have executed before the current one.
Let's put some thought about it:
Why would you want to do it at first place?
What is the effect that you're seeking?
My blind-guess answers are:
You want to have the login test executed first.
This will help you avoid repeating the same login code in all of the remaining tests, and you save time from starting/stopping the driver.
If my guesses are correct, then I can tell you that it's perfectly normal to want those things, and that everyone who ever needed to write more than 2 tests has been there
You may ask:
Am I not trying to achieve the same thing?
Where is my mistake then?
The following answers can be considered brief and quite insufficient compared to the truth that lies beneath, but I think they will serve you as a good starting point for where you're heading at.
Yes, you're trying to achieve the same thing.
Your approach can't be called mistake, but negliance - you're trying to write automated tests with Selenium while you lack basic coding skills. It's actualy not so bad, because you didn't knew what you're getting into, but after reading this you have the choice of either letting it slip or begin the journey to personal improvement. Take some time to learn Java - a really good starter would be the 'Thinking in Java' book. Do make the exercises after each chapter and the experience that you'll gain will be priceless. The book will help you get familiar with the Java language features and good idea of how code organization works.
Enough with the general notes, below is a simple guide for you to follow in implementing v0.1 of your newly-born 'automation project'.
(Yes, it is 'project' and 'framework' is having completely different meaning, to be clear.)
First you need to decide how to approach the page objects.
(Based on my own experience)
the best way to do it is to keep all the business-logic inside the page objects and by business-logic I mean:
all the methods that perform some action,
and all the methods that retrieve some data.
Examples include doSomething / getSometing / setSomething / isSomething / canSomething / waitSomething and so on.
You should not be doing any assertions inside the page objects
You should never throw AssertionError from your page objects
If you need to throw something from page-object method/constructor just
throw IllegalArgumentException
or RuntimeException.
All the assertions should happen exclusively in the test-method-body
You should never write 'assert...' statement outside #Test method
You can have Util classes to do some data transformations
but do not ever write assertion logic inside them
rather expose methods with boolean/other return type and assert on their result.
The following example is one of the best I've ever seen:
// Don't do this:
ColorVerifications.assertAreSame(pen.getColor(), ink.getColor());
// If you do, you'll eventually end-up having to write this:
ColorVerifications.assertAreNotSame(pen.getColor(), ink.getColor());
// Do this instead:
Assert.isTrue(ColorUtil.areSame(pen.getColor(), ink.getColor());
// Then you can actually 'reuse' the logic that's already in the Assert class:
Assert.isFalse(ColorUtil.areSame(pen.getColor(), ink.getColor());
// NOTE: Regarding the term "code reuse"
// - it is NOT referring to "reusing your own code"
// - it IS referring to "reusing all the existing code"
The idea is to clearly communicate both the assert intention and how the check is done.
Always keep the logic of 'checking' and 'asserting' separate, becuse those words are having different semantics.
It is really important to put a good amount of thinking when naming classes / methods / variables - really!
Don't be afraid to rename it when you come up with a better fit - go for it on the spot!
Now back to our page obects and login stuff - I won't be diving much into it but, you'll get where I'm going at.
Carefully organize the #Test methods in separate classes
Keep the ones that need common setup in the same class
Keep the number of test-class methods that are not annotated with #Test to the minimum
Do not inherit between test classes to reuse logic other than
Outputs/Results folders preparaton/colletion/archiving
Logs/Reports initialization/disposal
Driver creation/cleanup
In general: prefer composition to inheritance
Put other logic in the page objects / util classes and reuse them
Have a base DriverWrapper class for the generic UI checks/interactions
Have a base PageObject class that is hosting a DriverWrapper member
Don't use the #FindBy / PageFactory model (you'r life will be happier)
Use static final By locators instead
Log everything!
logging is not included in the examples
but do assume every method's first line is logging the method name and the passed arguments
always remember - your log is your best friend
Reading what happened in the log takes considerably less time than manually debugging the code (which is pratically re-running it at half-speed)
You are writing atuomation code, not production code so you can nevver be wrong when logging additional info.
Except for the cases of passwords and confidential data
those you should never log.
Now that you have been thaught some basic ideas, lets dive into code:
Page basics
Sample DriverWrapper:
class DriverWrapper {
protected final WebDriver driver;
public DriverWrapper(WebDriver driver){
this.driver = Objects.requireNotNull(driver, "WebDriver was <null>!");
}
// it's okay to have 'checked exceptions' declared by all wait* methods
// but it is tottaly not okay to have 'checked exceptions' for the others
public WebElement waitForVisibleElement(By locator, int timeoutMillis)
throws TimeoutException { // <- the 'checked exception'
return new WebDriverWait(driver)
.withTimeout(Duration.ofMillis(timeoutMillis))
.pollingEvery(Duration.ofMillis(100))
.until(
ExpectedConditions.visibilityOfElementLocatedBy(locator)
);
}
public boolean isVisible(By locator, int timeoutMillis){
try{
return waitForVisibleElement(locator, timeoutMillis) != null;
catch(TimeoutException ignored){
return false;
}
}
// .get(String url){...}
// .click(By locator){... elementToBeClickable(locator) ....}
// .typeInto(bool shouldLog, By locator, CharSequence... keys){...}
// .typeInto(By locator, CharSequence... keys){typeInto(true, locator, keys);}
// you got the idea ;)
}
Sample PageObject:
class PageObject{
protected final DriverWrapper driver;
public PageObject(WebDriver driver){
this.driver = new DriverWrappr(driver);
}
}
Sample LoginPage:
class LoginPage extends PageObjet{
// NOTE: keep the locators private
private static final By USERNAME_INPUT = By.id("usernameInput");
private static final By PASSWORD_INPUT = By.id("passwordInput");
private static final By LOGIN_BUTTON = By.id("loginButton");
private static final By ERROR_MESSAGE = By.id("errorMessage");
public LoginPage(WebDriver driver){
super(driver);
}
public LoginPage goTo(){
driver.get(url);
return this;
}
public void loginAs(String user, String pass){
// NOTE:
// Do not perform navigation (or other actions) under the hood!
// Resist the urge to call goTo() here!
// Page object methods should be transparent about what they do.
// This results in better level of control/transparency in the tests.
driver.typeInto(USERNAME_INPUT, user);
driver.typeInto(PASSWORD_INPUT, pass);
driver.click(LOGIN_BUTTON);
}
public boolean isErrorMessageVisible(int timeoutMillis){
// NOTE: We delegate the call to the driver
// Allowing the page-object to later define it's own isVisible method
// Without having collision with driver methods.
return driver.isVisible(ERROR_MESSAGE, timeoutMillis);
}
}
Infrastructure basics
Sample DriverManager class:
class DriverManager{
private static WebDriver driver;
public static WebDriver getDriver(){
return driver;
}
public static void setDriver(WebDriver driver){
// NOTE: Don't do null checks here.
DriverManager.driver = driver;
}
public static WebDriver createDriver(String name){
//...
return new ChromeDriver();
}
Sample TestBase class:
class TestBase{
// NOTE: just define the methods, do not annotate them.
public static void setUpDriver(){
// In v0.1 we'll be sharing the driver between tests in same class
// Assuming the tests will not be running in parallel.
// For v1.0 you can improve the model after reading about test-listeners
WebDriver driver = DriverManager.getDriver();
if(driver != null){
return;
}
driver = DriverManager.createDriver("chrome");
DriverManager.setDriver(driver);
}
public static void tearDownDriver(){
WebDriver driver = DriverManager.getDriver();
if(driver != null){
driver.quit();
DriverManager.setDriver(null);
}
}
}
Finally - a test class:
class LoginTests extends TestBase{
private LoginPage loginPage;
#BeforeClass
public static void setUpClass(){
setUpDriver();
}
#AfterClass
public static void tearDownClass(){
tearDownDriver();
}
#BeforeMethod
public void setUp(){
// actions, that are common for all test cases in the class
loginPage = new LoginPage(DriverManager.getDriver());
loginPage.goTo();
}
#AfterMethod
public void tearDown(){
// dispose the page objets to ensure no local data leftovers
loginPage = null;
}
#Test
public void testGivenExistingCredentialsWhenLoginThenNoError(){
loginPage.loginAs("TestUser", "plain-text password goes here");
boolean errorHere = loginPage.isErrorMessageVisible(30 * 1000);
Assert.assertFalse(errorHere, "Unexpected error during login!");
}
#Test
public void testGivenBadCredentialsWhenLoginThenErrorShown(){
loginPage.loginAs("bad", "guy");
boolean errorHere = loginPage.isErrorMessageVisible(30 * 1000);
Assert.assertTrue(errorHere, "Error message not shown!");
}
}
That's all there is to it.
Hope you enjoyed the ride.
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);
}
I am stuck whilst trying to run a selenium cucumber java script in two browsers (Chrome, Firefox). The test works fine when I use a single browser. I use selenium PageFactory class to initialise the web elements.
The problem is when I run the test for two browsers, the first browser opens, navigates to the URL and then nothing happens. Web elements are not initialised. The test moves on to the second browser, navigates to URL, web elements are initialised, subsequent test methods (testMethod1) run as expected. Why is the test not running on the first browser?
This is the PageFactory BasePage class holding the web elements:
public class BasePage {
private final WebDriver driver;
public BasePage(WebDriver driver) {this.driver = driver;} //constructor
#FindBy(id = "cc-amount")
public WebElement amountField;
This is the test class and how I have tried to run the test in two browsers:
public class Convert {
private static WebDriver driver;
private final BaseUtil baseUtil = new BaseUtil();
private static BasePage basePage;
private static int browser;
public void navigateToUrl(String url) throws InterruptedException {
for (browser = 1; browser <= 2; browser++) {
if (browser == 1) {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
} else if (browser == 2) {
WebDriverManager.firefoxdriver().setup();
driver = new FirefoxDriver();
}
driver.get(baseUtil.getMyUrl()); //Url coming from a utility class
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
basePage = PageFactory.initElements(driver, BasePage.class); //initialisation of the BasePage class conttaining the pagefactory web elements
After the browser initialisation, this method runs ok in the second browser (Firefox) but did not run at all in the first browser (Chrome):
public void testMetod1(String amount) throws InterruptedException {
basePage.amountField.click();
My suspicion is that PageFacory could not be initiated for both browsers in a single run but I do not know how to diagnose this further. It could also be a for loop error.
I believe it's a synchronisation issue. Just you need to create a class for each browser, then you can create another base class which contains all the shared code.
Creating a class for each browser will give you some advantages:
Cleaner code.
Your tests will run in parallel.
Specific test cases for a specific browsers (Specially for IE).
Solve this sync issue.
So basically instead of using Converter, you can create:
ChromeConverter
FirefoxConverter
And inside your TestExecuter class, just create instance for each one and run your tests.
And to be more detailed, you are using one class Convert and inside it you have two static variables:
private static WebDriver driver;
private static BasePage basePage;
Removing the static keyword from BasePage class won't solve the root cause as you are using BasePage which is a static class and can't be instantiated as instance class and it's inside an external library so you need to separate the classes as kind of wrapper.
Here you are trying to use the same exact initialisation with Chrome then Firefox which won't work in parallel or even in for loop until you can wait to the first test then dispose it and initialise all the stuff related to the second test again.
So the correct approach is to create a separate Convert for each browser like that:
ChromeConverter
FirefoxConverter
And In your TestExecuter you just need to initialise each one and use it, something like that:
#Given("...")
public void navigateToUrl(String url) throws InterruptedException {
chromeConverterApp.navigateToUrl(url);
firefoxConverterApp.navigateToUrl(url);
}
#When("^..")
public void enterCurrencies(String amount, String from, String to) throws InterruptedException {
chromeConverterApp.enterCurrencies(amount, from, to);
firefoxConverterApp.enterCurrencies(amount,from,to);
}
The for loop opens Chrome on the first run (browser=1), then opens Firefox on the second run (browser=2) and then continues with the test on the last opened browser (Firefox).
If you wish to run the complete test on both browsers, you need to put the rest of the test inside of the for loop.
Basically, you have something like:
public void navigateToUrl(String url) throws InterruptedException {
for (browser = 1; browser <= 2; browser++) {
//body of the for loop
}
//some more code
} //end of navigateToUrl
You need to change it to:
public void navigateToUrl(String url) throws InterruptedException {
for (browser = 1; browser <= 2; browser++) {
//body of the for loop
}
//some more code
testMethod1()
} //end of navigateToUrl
Your approach is not correct, if you need to run your tests in parallel you should go for either Selenium Grid or consider implementing other multi-threading option, i.e.:
Plain Java: ExecutorService
JUnit or TestNG: Maven Surefire Plugin Fork Options and Parallel Test Execution
In any case you will need to refactor your test to follow Parallel Tests - Best Practices at least:
Remove static modifier from the WebDriver declaration
Consider putting your WebDriver instance into the Thread Local Storage otherwise you will have clashes