Selenium应该是目前前端测试最主流的测试工具吧,支持主流浏览器如Firefox、Chrome、IE等,无论你熟悉Python还是Java,都可以方便的使用Selenium提供的丰富的测试套件,还有其它很多基于Selenium二次封装开发的测试框架如Robot Framework可供选择。

这次是第一次使用Java操作WebDriver,以TestNG作为测试框架,实现简单的Web前端测试。

IDE: Eclipse Oxygen.3a Release (4.7.3a)
Step1:打开Eclipse,在Help - Install New Software…中搜索并安装TestNG插件

安装重启之后可在Window - Show View - Other里,Java下查看到TestNG

Step2:使用Eclipse自带的Maven插件(或者在Window - Preferences - Maven - Installations添加系统安装的其它版本Maven),新建一个Maven项目

输入groupId和artifactId

Step3:在testExample/pom.xml中添加TestNG、Selenium作为dependencies,若只想使用特定的WebDriver实现,如Firefox,可直接添加selenium-firefox-driver作为dependency。

<project xmlns="http://maven.apache/POM/4.0.0" xmlns:xsi="http://www.w3/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache/POM/4.0.0 http://maven.apache/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>testExamples</groupId>
  <artifactId>testExamples</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-firefox-driver</artifactId>
        <version>3.13.0</version>
    </dependency>
    <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>6.14.3</version>
    </dependency>
  </dependencies>
</project>

Alt+F5选择项目进行更新,可以在Maven Dependencies里检查依赖包是否存在

右键点击JRE System Library, 改成系统默认

Step4:下载浏览器对应的driver放到特定目录下(如C:\webdriver),可查看官网下载地址 。如使用Firefox,需要到这里下载最新的geckdriver.exe。
Step5:右键点击项目,创建一个新的TestNG class并指定package。
可选:
- Annotations,后面可在定义测试类的时候添加
- XML suite file,在这里添加可直接在package里生成suite文件;也可以缺省,创建好测试类之后右键选择项目TestNG - Convert to TestNG,会在项目下生成suite文件包含项目里不同package的所有测试类。本文暂时不涉及Test Suite的使用。

Step6:实现测试类
用例1:
AUT - http://live.guru99/index.php/

测试类如下

package selenium;

import org.testng.annotations.Test;
import static org.testng.Assert.assertTrue;
import java.util.List;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;

public class testPractice1 {
  private String aut_url = "http://live.guru99/index.php/";
  private WebDriver driver;

  @Test
  public void checkMainTitle() {
      driver.get(aut_url);
      String title1 = getTitle();
      assertTrue(title1.contains("THIS IS DEMO SITE"));
  }

  @Test
  public void checkMobileTitle() {
      driver.findElement(By.linkText("MOBILE")).click();
      String title2 = getTitle();
      assertTrue(title2.equals("MOBILE"));
  }

  @Test
  public void checkSortByName() {   
      List<WebElement> list1 = getProductList();
      list1.sort((e1, e2) -> e1.getAttribute("title")pareTo(e2.getAttribute("title")));       
      Select sort_type = new Select(driver.findElement(By.xpath("//select")));
      sort_type.selectByVisibleText("Name");
      List<WebElement> list2 = getProductList();
      for (WebElement a:list2) {
          int index = list2.indexOf(a);
          assertTrue(a.getAttribute("title").equals(list1.get(index).getAttribute("title")));
      }
  }

  @BeforeClass
  public void setUp() {
      System.setProperty("webdriver.gecko.driver", "C:\\webdriver\\geckodriver.exe");
      driver = new FirefoxDriver();
  }

  @AfterClass
  public void afterTest() {
      driver.quit();
  }

  public List<WebElement> getProductList() {
      List<WebElement> list = driver.findElements(By.xpath("//h2[@class='item last']/a"));
      return list;  
  }

  public String getTitle() {
      WebElement title = driver.findElement(By.className("page-title"));
      String t = null;
      if (title.isDisplayed()) {
          t = title.getText();
          System.out.println("Current title is:"+t);
      }
      return t; 
  }
}

用例2:
AUT - https://tokenpad.io (借用朋友公司开发的产品页面)
1. 注册新用户
- 测试注册成功,页面提示下一步
- 测试注册失败,页面提示警告信息
2. 登陆
- 测试登陆成功,页面跳转到登陆之后的Dashboard
- 测试 登陆失败,页面提示警告信息
在多用例的测试类中调试单个用例的时候可以用类似@Test(enabled=false),@AfterTest(enabled=false)来阻止其它用例或步骤运行。

package testPractice;

import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import java.util.List;

public class Practice2 {
  private String base_url = "https://tokenpad.io";
  private String login_url = base_url+"/auth/login";
  private String signup_url = base_url+"/auth/sign-up";
  private String dashboard_url = "https://tokenpad.io/invest/dashboard/onboard";
  private WebDriver driver;
  private String userId1 = "gracesheep@126";
  private String passwd1 = "1234qwer";
  private String userbase = "test";
  private String passwd = "12345678";
  private int delay = 3;
  WebDriverWait waitVar = null;

  @Test
  public void checkLoginSuccessfully() throws InterruptedException {
      String expected_text = "Welcome to Tokenpad";
      login(userId1, passwd1);
      Thread.sleep(5000);
      if (driver.findElement(By.tagName("app-init")).isEnabled()) {
      assertTrue(driver.getCurrentUrl().contains(dashboard_url));
      assertTrue(driver.findElement(By.xpath("//div[@class='title']")).getText().contains(expected_text));
      }
      else {
          fail();
      }
  }

  @Test
  public void checkLoginFailed() {
      String expected_alert = "The email or password you have entered is invalid.";
      login(userId1, passwd);
      waitVar.until(ExpectedConditions.visibilityOfElementLocated(By.tagName("alert")));
      assertTrue(driver.findElement(By.xpath("//div[@role='alert']")).getText().contains(expected_alert));
  }

  @Test
  public void checkSignUpFailed() {
      String country = "China";
      String expected_alert = "Your email is already been used.";
      signUp(userId1, passwd1, country);
      waitVar.until(ExpectedConditions.visibilityOfElementLocated(By.tagName("alert")));
      assertTrue(driver.findElement(By.xpath("//div[@role='alert']")).getText().contains(expected_alert));
  }

  @Test
  public void checkSignUpSucceed() {
      String country = "Sweden";
      String userId = userbase+String.valueOf(System.currentTimeMillis())+"@test";    
      signUp(userId, passwd, country);
      waitVar.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.className("send-validation")));
      assertTrue(driver.findElement(By.xpath("//h2[text()='Confirm Your Email']")).isDisplayed());
  }

  public void signUp(String user, String password, String country) {
      driver.get(signup_url);
      waitVar.until(ExpectedConditions.visibilityOfElementLocated(By.className("login-form")));
      driver.findElement(By.name("email")).sendKeys(user);
      driver.findElement(By.name("password")).sendKeys(password);
      driver.findElement(By.className("ng-arrow")).click();
      setCountry(country);    
      driver.findElement(By.xpath("//button[text()='Create Account']")).click();
  }

  public void setCountry(String country) {
      if (driver.findElement(By.tagName("ng-dropdown-panel")).isEnabled()) {
          List<WebElement> countries = driver.findElements(By.xpath("//div[@role='option']//span[@class='ng-option-label ng-star-inserted']"));
          for (WebElement item:countries) {
              if (item.getText().equals(country)) {
                  int index = countries.indexOf(item)+1;
                  String path = "//div[@class='ng-dropdown-panel-items scroll-host']//div["+index+"]";
                  driver.findElement(By.xpath(path)).click();
                  break;
              }
          }
      }
  }

  public void login(String user, String password) {
      driver.get(login_url);
      if (driver.findElement(By.tagName("app-login")).isEnabled()) {
          driver.findElement(By.name("email")).sendKeys(user);
          driver.findElement(By.name("password")).sendKeys(password);
          driver.findElement(By.xpath("//button[text()='Login']")).click();
      }  
  }

  @BeforeTest
  public void setUp() {
      System.setProperty("webdriver.gecko.driver", "C:\\webdriver\\geckodriver.exe");
      driver = new FirefoxDriver();
      System.out.println("Initialize browser");
      //可以采用隐式设置通用的等待时间
      //driver.manage().timeouts().implicitlyWait(delay, TimeUnit.SECONDS);
      //也可以采用显式根据条件判断结果设置等待时间
      waitVar = new WebDriverWait(driver, delay);     
  }

  @AfterTest
  public void afterTest() {
      driver.quit();
  }
}

总结两点使用心得:
1. 使用Selenium最重要的一点是在不同的web框架之中准确定位元素,在使用xpath的时候,可以借助浏览器插件如Chrome我使用的是ChroPath来方便的获取到。
2. 在assert之前给页面充分的加载时间,比如添加等待时间并通过判断元素是否显示、存在的方式确定页面加载完全了。

更多推荐

TestNG+Selenium实现简单的Web前端测试