3

My test code has a lot of repetition.

First I login to my page then go to the client registration page. Here you can see the repeated code for login.

For each page I write first login then current page tests.

How do I avoid so much repeated code?

Here is my login page..

package Pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;


public class HomePage 
{
    private WebDriver driver;

       //Page URL
     //  private static String PAGE_URL="https://54.68.159.20/qmsadm";
       @FindBy(name="username")
       WebElement username;
       @FindBy(name="password")
       WebElement password;
       @FindBy(className="button")
       WebElement button;
       public HomePage(WebDriver driver)
       {
        //initialize elements
          PageFactory.initElements(driver, this);

       }
       public void set_username(String usern)
       {
        username.clear();
        username.sendKeys(usern);
       }
       public void set_password(String userp)
       {
        password.clear();
        password.sendKeys(userp);
       }
       public void click_button()
       {
        button.submit();
       }
}

Here is the login test code.

package Test;
import org.testng.annotations.Test;
import Pages.ClientPage;
import Pages.HomePage;
import org.testng.Assert;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.BeforeTest;



public class ClientTest
{
WebDriver driver;
@BeforeTest
public void setup()
{
    System.setProperty("webdriver.firefox.marionette","pathToGeckodriver");
    driver=new FirefoxDriver();
    driver.manage().window().maximize();
    driver.get("http://54.68.159.204/qmsadm");
}
@Test
public void verify()
{
    HomePage home=new HomePage(driver);
    home.set_username("");
    home.set_password("");
    home.click_button();
    Assert.assertTrue(driver.getPageSource().contains("Hello Sony George"));
}
}

Here is the user test code.

package Test;
import org.testng.annotations.Test;
import Pages.HomePage;
import Pages.UsersPage;

import org.testng.Assert;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.BeforeTest;



public class UsersTest
{
WebDriver driver;
@BeforeTest
public void setup()
{
    System.setProperty("webdriver.firefox.marionette","pathToGeckodriver");
    driver=new FirefoxDriver();
    driver.manage().window().maximize();
    driver.get("http://54.68.159.204/qmsadm");
}
@Test
public void verify()
{
    HomePage home=new HomePage(driver);
    home.set_username("");
    home.set_password("");
    home.click_button();
    Assert.assertTrue(driver.getPageSource().contains("Hello Sony George"));
}
@Test(priority=1)
public void verify1()
{
    UsersPage user=new UsersPage(driver);   
    user.clickOnAdmin();
    user.clickonusers();
    user.clickonsearch();
    Assert.assertTrue(driver.getPageSource().contains("Hello Sony George"));
}
@Test(priority=2)
public void verify2()
{
    UsersPage user=new UsersPage(driver);
    user.set_username("k");
    user.check_uname();
    user.set_password("kk");
    user.set_list("Database admin [Demo]");
    user.set_email("[email protected]");
    Assert.assertTrue(driver.getPageSource().contains("Hello "));
}
@Test(priority=3)
public void verify3()
{
    UsersPage user=new UsersPage(driver);
    user.set_username("k");
    user.check_uname();
    user.set_password("kk");
    user.set_list("none");
    user.set_email("[email protected]");
    Assert.assertFalse(driver.getPageSource().contains("Hello "));
}
}

2 Answers 2

3

You're on the right track towards DRY test code (Don't Repeat Yourself).

This is how I'd refactor what you have:

  • Data-drive the tests - you have a series of almost identical tests here. For each type of test (login test, create user test), you create a data file - CSV or XML works. There is an example with TestNG here. For the login page I'd do something like username,password,success and modify the assert to use an isLoggedOn routine from the login page object that returns true/false.
  • Use page object methods to determine success/failure - I'd look at using your page object classes more so that your asserts become more a case of Assert.assertTrue(user.Created(data.username, data.success)); where user.Created returns true if the outcome of the action matches the boolean success variable.
  • Add logging to distinguish your test cases - the final step here is to add logging to distinguish your test cases, so that the output from your tests includes a statement of what was tested and what was expected. That way, when the tests complete you'll have a record that looks something like this:
Test logon for username = george, expect success. PASS
Test add user username = fred, expect success. PASS
Test add user username = xx, expect add failure. PASS
  • Take small steps - don't try to do all this at once. Get your tests running with one of the changes I've suggested, then refactor to make a second change, and so forth. It's much easier to refactor one thing at a time than to try to do it all at once.
4
  • hai. thank you for your contribution. Actually I don't understand. please clarify me..
    – user21268
    Oct 11, 2016 at 11:54
  • Doesn't the logging just adding more code? (Which needs to be maintained and written) Shouldn't you only implement the logging when you really need it? Although I see some use-cases and its looks like a good idea I do wonder if it worth always doing. Oct 11, 2016 at 12:10
  • +1 for the Take small steps, this is really important when changing your code :) The cool thing of a test-suite is that you can keep re-running it to check you didn't break something (Atleast as long your application didn't also change at the same time ;-) Oct 11, 2016 at 12:11
  • @NielsvanReijmersdal - the key thing with logging is to supplement the built-in logging if it fails to tell you enough about the tests you're automating. That's why I'd place logging as the third item and use it only if the harness doesn't give me enough (I've found with data-driving that it's sometimes necessary to explicitly log which data I'm using)
    – Kate Paulk
    Oct 11, 2016 at 14:19
1

I think Page Objects methods (e.g. functions, in your case set_username) should be actions. The main action on a login page is "Login". As the action is nearly always the same you can make it a single method. Do you really want to test the setting of the username? Possibly yes, but in this case not, you want to login and test some steps in the application.

Here some pseudo code:

Login(userObject) {
  set_username(userObject.username)
  set_password(userObject.password)
  click(loginButton)

  return new pageObjectItRedirectsToo // e.g. OverviewPage
}

Now you only have one line to login and if every test logs in you could even move it to the setup().

The test code could look like this:

// Arrange
HomePage home=new HomePage(driver);
UserPage user = home.login(users["UserWithXRights");
// Act
user.doSomeAction()
// Assert
assert(actual, expected)

The users could be a central static class or file which has the credentials so you don't repeat them in all the tests. You will want to do this with all data you use for your tests so you only have to update it one location. Its like a PageObject but then for data. Personally I would prefer code instead of a separate data-file. If you use a file do put it under version-control it with the application code. (This to make sure you can run test-suites for different application versions, but only if you have different versions (e.g. branches))

For the setup() function as it is the same for all test classes you could create a base class it inherits from. Don't put to much in the base class, certainly no logic, since you would hide complexity and make the tests harder to read and maintain. (I would like feedback on this from others, not sure what the best approach is.)

Test in isolation

Also looking at your example you are re-using test state for the next tests steps. When one test fails all the others will also fail. Its better to isolate the tests and focus them on a single thing. Also read this answer: https://sqa.stackexchange.com/a/8516/3201

Names of test

Give you tests names which explain what they do. Not verify1-10. More like GivenIamLoggedInAsUserX_WhenIDoSomeAction_ThenIVerifyThisResult() or searchForUserAndChangeValidPassword. I always start with naming my tests as it makes me think about what do I really want to test and safeguard here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.