本篇文章将记录作者和带领读者,使用Java+Selenium+Junit5测试框架,对个人博客系统进行自动化测试。作者的个人博客系统分为前台系统和后台系统,这次测试主要测试前台的注册、首页、详情页、个人中心界面,后台的登录和标签管理界面。下图是整个系统测试用例的设计。

博客系统自动化.png

由于掘金上传视频不方便,本篇文章中把视频转为gif展示了,有一点点糊🥲

想看视频的朋友点这里👉 【Java+Selenium+Junit5对个人博客进行自动化测试(包含代码和视频) - CSDN App】t.csdnimg.cn/Xevqv

后台系统

后台指的是:管理个人博客的后台系统

登录界面测试
1、编写测试用例

image-20240401093630863

2、准备工作

(1)新建maven工程

image-20240401094159139

(2)引入依赖

Selenium、Junit5、用于截图的工具包


  1.  <dependency>

  2.      <groupId>org.seleniumhq.selenium</groupId>

  3.      <artifactId>selenium-java</artifactId>

  4.      <version>4.18.1</version>

  5.  </dependency>

  6.  <!--   保存屏幕截图文件需要用到的包     -->

  7.  <dependency>

  8.      <groupId>commons-io</groupId>

  9.      <artifactId>commons-io</artifactId>

  10.      <version>2.6</version>

  11.  </dependency>

  12.  <dependency>

  13.      <groupId>org.junit.jupiter</groupId>

  14.      <artifactId>junit-jupiter</artifactId>

  15.      <version>5.8.2</version>

  16.      <scope>test</scope>

  17.  </dependency>

  18.  <dependency>

  19.      <groupId>org.junit.platform</groupId>

  20.      <artifactId>junit-platform-suite</artifactId>

  21.      <version>1.8.2</version>

  22.      <scope>test</scope>

  23.  </dependency>

 

(3)在test包下创建包的结构

image-20240401094610368

(4)准备一个工具类,用于创建driver驱动和隐式等待

image-20240401095614835

  • selenium浏览器驱动安装不再赘述,网上有很多资料
  • 隐式等待的原因是:我们在自动化测试的时候要频繁获取页面中的元素,很多时候页面元素的加载速度赶不上自动化代码的执行速度,会导致找不到元素的情况。为避免这种情况,我们进行隐式等待,让程序等我们一会。注意:隐式等待作用于WebDriver整个生命周期,只要没有执行driver.quit,即没有退出浏览器,隐式等待都是一直存在的

  1.  public class AutoTestUtils {

  2.      public static ChromeDriver driver;

  3.  ​

  4.      public static ChromeDriver createDriver() {

  5.          if(driver == null){

  6.              System.setProperty("webdriver.chrome.driver","D:\JavaSet\jdk\myjdk8\bin\chromedriver.exe");

  7.              ChromeOptions options = new ChromeOptions();

  8.              options.addArguments("--remote-allow-origins=*");

  9.              driver = new ChromeDriver(options);

  10.              // 设置隐式等待时间

  11.              driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));

  12.              System.out.println("ChromeDriver创建完毕......");

  13.         }

  14.          return driver;

  15.     }

  16.  }

3、测试页面显示是否正确

image-20240401100315566

  • @TestMethodOrder注解使用 OrderAnnotation 方法排序器,它会按照 @Order 注解中指定的顺序来执行测试方法。数字越小越先执行。
  • 在执行之后的测试方法之前,我们要先打开博客后台登录页面。
  • 通过页面的一些元素定位,加上断言进行验证页面显示是否正确。
4、测试登录失败的情况

image-20240401101037454

  • @ParameterizedTest + @CsvSource 注解用于参数化测试。减少重复的测试代码,提高测试的可维护性。
  • 由于WebDriver只能在一个页面上对元素识别与定位,而错误提示信息是隐藏弹窗,无法定位无法获取。这里采用强制等待+Js代码来定位隐藏的div弹窗。这里不用隐式等待的原因是: 作用不了非HTML页面的元素的,所以弹窗无法等待,看下是否在切换到弹窗之前弹窗还没有出现,终端报的错误是不是noalert。这里不用显示等待的原因是: 我们的登录界面测试类继承了AutoTestUtils类,这个类实现了隐式等待,隐式等待和显示等待十分不推荐共同使用,会带来意想不到的错误!
5、测试登录正常的情况

image-20240401102537181

  • 通过保存屏幕截图,可以更直观地了解测试执行时页面的状态,有助于定位问题和进行故障排查。
6、测试代码和视频

  1.  package BlogAdmin;

  2.  ​

  3.  import common.AutoTestUtils;

  4.  import org.apache.commons.io.FileUtils;

  5.  import org.junit.jupiter.api.*;

  6.  import org.junit.jupiter.params.ParameterizedTest;

  7.  import org.junit.jupiter.params.provider.CsvSource;

  8.  import org.openqa.selenium.*;

  9.  import org.openqa.selenium.chrome.ChromeDriver;

  10.  import java.io.File;

  11.  import java.io.IOException;

  12.  import java.time.Duration;

  13.  ​

  14.  /**

  15.   * @author 3A87

  16.   * @description 后台登录界面测试

  17.   */

  18.  @TestMethodOrder(MethodOrderer.OrderAnnotation.class) // 说明当前该类下面的测试方法要按一定的顺序执行

  19.  public class LoginTest extends AutoTestUtils {

  20.      public static ChromeDriver driver = createDriver();

  21.      @Test

  22.      @BeforeAll // 被@BeforeAll修饰的方法要是静态的

  23.      static void init() {

  24.          // 跳转到博客登录页面

  25.          driver.get("http://123.xx.xx.xxx:xxxx/");

  26.     }

  27.      /**

  28.       * 检查登录页面是否正常显示

  29.       */

  30.      @Test

  31.      @Order(1)

  32.      void loginPageTest(){

  33.          // 隐式等待--// 隐式等待,更加丝滑——》作用于下面的整个作用领域,这个方法中的所有元素,在这3秒内不断轮询

  34.          driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));

  35.          // 利用断言判断登录的文本内容显示是否正确

  36.          String expect = "Login Form";

  37.          String actual = driver.findElement(By.cssSelector(".title")).getText(); // 检查登录页面的登录文本是否存在

  38.          //System.out.println(actual);

  39.          Assertions.assertEquals(expect, actual);

  40.          // 检查提交按钮是否存在

  41.          driver.findElement(By.cssSelector("[type='button']"));

  42.     }

  43.  ​

  44.      /**

  45.       * 检查登录失败的情况,每写一个测试用例就测试一下

  46.       */

  47.      @Order(2)

  48.      @ParameterizedTest // 多个参数,不用加@Test

  49.      @CsvSource({"mie,666666","admin, 1234"})

  50.      void loginFailTest(String username, String password) throws InterruptedException {

  51.          // 把之前默认填充内容清空

  52.          driver.findElement(By.cssSelector("[name='userName']")).clear();

  53.          driver.findElement(By.cssSelector("[name='password']")).clear();

  54.          //输入用户名和密码

  55.          driver.findElement(By.cssSelector("[name='userName']")).sendKeys(username);

  56.          driver.findElement(By.cssSelector("[name='password']")).sendKeys(password);

  57.          //点击登录

  58.          driver.findElement(By.cssSelector("[type='button']")).click();

  59.          Thread.sleep(2000);

  60.          // 使用 JavascriptExecutor 执行 JavaScript 代码来操作隐藏的 div 弹窗

  61.          JavascriptExecutor js = driver;

  62.          WebElement hiddenDiv = (WebElement) js.executeScript("return document.querySelector("div[role='alert'][class='el-message el-message--error']")");

  63.  ​

  64.          System.out.println(hiddenDiv.getText());

  65.     }

  66.  ​

  67.  ​

  68.      /**

  69.       * 检查正常登录的情况

  70.       */

  71.      @Order(3)

  72.      @ParameterizedTest

  73.      @CsvSource({"mie, 1234"})

  74.      void loginRightTest(String username, String password) throws InterruptedException, IOException {

  75.          // 把之前的输入的内容清空

  76.          driver.findElement(By.cssSelector("[name='userName']")).clear();

  77.          driver.findElement(By.cssSelector("[name='password']")).clear();

  78.          //输入用户名和密码

  79.          driver.findElement(By.cssSelector("[name='userName']")).sendKeys(username);

  80.          driver.findElement(By.cssSelector("[name='password']")).sendKeys(password);

  81.          //点击登录

  82.          driver.findElement(By.cssSelector("[type='button']")).click();

  83.          Thread.sleep(1000);

  84.          // 上述步骤只是说明输入了账号和密码,但还不知道点击提交后是否会跳转到博客列表页

  85.          String expect = "http://123.56.154.146:8094/#/dashboard";

  86.          String actual = driver.getCurrentUrl();

  87.          Assertions.assertEquals(expect, actual); // 查看当前的url是否在博客详情页面

  88.          // 进行截图,看当前是否跳转到了登录界面

  89.          File srcFile =  driver.getScreenshotAs(OutputType.FILE);

  90.          String fileName = "loginRightTest.png";

  91.          FileUtils.copyFile(srcFile, new File(fileName));

  92.     }

  93.      // 这里我们不关闭driver驱动,因为我们要保证登录状态

  94.  }

标签模块功能测试

我们测试方法的顺序是增、改、查、删,这样保证最后不对已上线的博客系统造成影响

1、准备工作
(1)测试用例

增加标签:

image-20240402202703327

修改标签:

image-20240402202732536

查询标签:

image-20240402202758574

删除标签:

image-20240402202827894

(2)添加测试套件

通过测试套件可以同时执行多个类的测试用例。

在测试标签模块功能前,必须先进行登录,才可以进行对标签的增删改查,这里可以使用测试套件确保先登录。

junit5测试套件注解有两种:


  1.  // 第一种方法: @Suite && @SelectClasses

  2.  // 第二种方法: @Suite && @SelectPackages -- 指定包名类运行包下所有的测试用例

image-20240402204152310

(3)进入标签页面

image-20240402204514008

  • 在定位标签管理的时候,直接用driver定位,定位不到,这里采用js的方式。在前面登录界面测试中提到过。

image-20240402204413191

2、测试新增标签功能

image-20240402204850773

  • AutoTestUtils类里增加一个检查页面是否存在标签名为tag的标签的方法,在后面测试修改标签功能也会用到。

    image-20240402205034559

  • 在上面这个方法,我采用的方法是定位页面中标签名的位置,提取出所有标签名,再遍历是否包含我新增的标签名。
3、测试修改标签功能

image-20240402205616730

  • 要定位上个新增标签测试方法增加的标签,需要知道该标签所在的行数。在AutoTestUtils类增加一个查询tag所在的行号的方法,因为后面删除测试方法也要用到。

    image-20240402210029731

  • 修改tag的名字为tag+“——修改了”,便于后面做断言。
4、测试搜索标签功能

image-20240402210136825

5、测试删除标签功能

image-20240402210251527

6、测试代码和视频

  1.  package BlogAdmin;

  2.  ​

  3.  import common.AutoTestUtils;

  4.  import org.apache.commons.io.FileUtils;

  5.  import org.junit.jupiter.api.*;

  6.  import org.junit.jupiter.params.ParameterizedTest;

  7.  import org.junit.jupiter.params.provider.CsvSource;

  8.  import org.openqa.selenium.*;

  9.  ​

  10.  import java.io.File;

  11.  import java.io.IOException;

  12.  ​

  13.  /**

  14.   * @author 3A87

  15.   * @description 测试标签模块的增删改查功能

  16.   */

  17.  @TestMethodOrder(MethodOrderer.OrderAnnotation.class)

  18.  public class TagTest extends AutoTestUtils {

  19.      @Test

  20.      @BeforeAll

  21.      static void init() {

  22.          driver.get("http://123.xx.xxx.xxx:xxxx/?#/dashboard");

  23.          //点击内容管理

  24.          driver.findElement(By.xpath("//*[@id="app"]/div/div[1]/div/div[1]/div/ul/div[13]/li/div[1]")).click();

  25.          //点击标签管理

  26.          //driver.findElement(By.xpath("//*[@id="app"]/div/div[2]/div/div[1]/div/ul/div[13]/li/ul/div[4]")).click();

  27.          JavascriptExecutor js = driver;

  28.          WebElement tagDiv = (WebElement) js.executeScript("return document.querySelector("#app > div > div.sidebar-container > div > div.scrollbar-wrapper.el-scrollbar__wrap > div > ul > div:nth-child(13) > li > ul > div:nth-child(4)")");

  29.          tagDiv.click();

  30.     }

  31.  ​

  32.  ​

  33.      @Order(1)

  34.      @ParameterizedTest

  35.      @CsvSource({"测试tag1,测试remark"})

  36.      void addTagTest(String tag,String remark) throws InterruptedException {

  37.          //点击新增按钮

  38.          driver.findElement(By.xpath("//*[@id="app"]/div/div[2]/section/div/div[1]/div[1]/button")).click();

  39.  ​

  40.          Thread.sleep(100);

  41.          String expect = "添加标签";

  42.          String actual = driver.findElement(By.xpath("/html/body/div[2]/div/div[1]/span")).getText();

  43.          Assertions.assertEquals(expect,actual);

  44.  ​

  45.          driver.findElement(By.xpath("/html/body/div[2]/div/div[2]/form/div[1]/div/div/input")).sendKeys(tag);

  46.          driver.findElement(By.xpath("/html/body/div[2]/div/div[2]/form/div[2]/div/div/textarea")).sendKeys(remark);

  47.  ​

  48.          driver.findElement(By.xpath("/html/body/div[2]/div/div[3]/div/button[1]")).click();

  49.  ​

  50.          //刷新页面

  51.          driver.navigate().refresh();

  52.          Thread.sleep(300);

  53.  ​

  54.          //检查页面是否新增成功

  55.          boolean result = AutoTestUtils.containsTag(tag);

  56.          Assertions.assertTrue(result);

  57.     }

  58.  ​

  59.      @Order(2)

  60.      @ParameterizedTest

  61.      @CsvSource({"测试tag1,测试remark"})

  62.      void updateTagTest(String tag,String remark) throws InterruptedException {

  63.          //查找新增的标签对应的行数

  64.          int count = AutoTestUtils.getTagCount(tag);

  65.  ​

  66.          //System.out.println(count);

  67.  ​

  68.          //点击修改

  69.          driver.findElement(By.xpath("//tbody/tr["+count+"]/td[5]/div/button[1]")).click();

  70.          //定位tag输入框和remark输入框

  71.          WebElement tagElement = driver.findElement(By.xpath("/html/body/div[2]/div/div[2]/form/div[1]/div/div/input"));

  72.          WebElement remarkElement = driver.findElement(By.xpath("/html/body/div[2]/div/div[2]/form/div[2]/div/div/textarea"));

  73.          //清空输入框的内容

  74.          tagElement.clear();

  75.          remarkElement.clear();

  76.          //修改tag和remark

  77.          tagElement.sendKeys(tag+"——修改了");

  78.          remarkElement.sendKeys(remark+"——修改了");

  79.          //点击修改

  80.          driver.findElement(By.xpath("/html/body/div[2]/div/div[3]/div/button[1]")).click();

  81.  ​

  82.          Thread.sleep(300);

  83.          //检查是否修改成功

  84.          boolean result = AutoTestUtils.containsTag(tag+"——修改了");

  85.          Assertions.assertTrue(result);

  86.     }

  87.  ​

  88.  ​

  89.      @Order(3)

  90.      @ParameterizedTest

  91.      @CsvSource({"测试tag1——修改了"})

  92.      void searchTagByTagNameTest(String tag) throws IOException, InterruptedException {

  93.          driver.findElement(By.xpath("//*[@id="app"]/div/div[2]/section/div/form/div[1]/div/div/input")).clear();

  94.          driver.findElement(By.xpath("//*[@id="app"]/div/div[2]/section/div/form/div[1]/div/div/input")).sendKeys(tag);

  95.          driver.findElement(By.xpath("//*[@id="app"]/div/div[2]/section/div/form/div[2]/div/button")).click();

  96.  ​

  97.          //等搜索界面加载一会

  98.          Thread.sleep(300);

  99.          // 进行截图,看当前是否搜索到了名字为tag的标签记录

  100.          File srcFile =  driver.getScreenshotAs(OutputType.FILE);

  101.          String fileName = "searchTagTest.png";

  102.          FileUtils.copyFile(srcFile, new File(fileName));

  103.     }

  104.  ​

  105.      @Order(4)

  106.      @ParameterizedTest

  107.      @CsvSource({"测试tag1——修改了"})

  108.      void deleteTagTest(String tag) throws InterruptedException {

  109.          //查找新增的标签对应的行数

  110.          int count = AutoTestUtils.getTagCount(tag);

  111.          //点击删除

  112.          driver.findElement(By.xpath("//tbody/tr["+count+"]/td[5]/div/button[2]")).click();

  113.  ​

  114.          JavascriptExecutor js = driver;

  115.          WebElement confirmButton = (WebElement) js.executeScript("return document.querySelector("body > div.el-message-box__wrapper > div > div.el-message-box__btns > button.el-button.el-button--default.el-button--small.el-button--primary")");

  116.          confirmButton.click();

  117.  ​

  118.          Thread.sleep(1000);

  119.          boolean result = AutoTestUtils.containsTag(tag);

  120.          Assertions.assertFalse(result);

  121.     }

  122.  ​

  123.  ​

  124.      @Test

  125.      @AfterAll

  126.      static void exit() {

  127.          driver.quit();

  128.     }

  129.  }

  130.  ​

前台系统

前台指的是:展示个人博客的前台系统

注册界面测试
1、编写测试用例

image-20240401114023730

2、测试页面显示是否正常

image-20240401151155110

3、测试注册成功的情况

image-20240401151304191

  • 注册成功后会跳转到登录页面,这里添加一个断言。
  • 跳转到登录页面后,为继续测试注册失败的情况,我们需要再跳转回注册页面。driver.navigate.back()类似于浏览器中点击浏览器的“后退”按钮。
4、测试注册失败的情况

image-20240401151627242

  • 因为注册失败的测试用例较多,我们将测试用例写在csv文件下,采用@CsvFileSource注解把它们拿过来。

    image-20240401151822672

  • 这里测试出来我的前台注册功能的两个bug

    • 两次输入不一致的密码,仍可注册成功!大漏洞!
    • 邮箱后缀不加.仍可以注册成功
  • 这里遇到了一个小问题:当我测试密码为空的用例时,浏览器好像会复用上一个用例的123123密码,导致注册成功

5、测试代码和视频

  1.  import common.AutoTestUtils;

  2.  import org.apache.commons.io.FileUtils;

  3.  import org.junit.jupiter.api.*;

  4.  import org.junit.jupiter.params.ParameterizedTest;

  5.  import org.junit.jupiter.params.provider.CsvFileSource;

  6.  import org.junit.jupiter.params.provider.CsvSource;

  7.  import org.openqa.selenium.By;

  8.  import org.openqa.selenium.OutputType;

  9.  import org.openqa.selenium.chrome.ChromeDriver;

  10.  ​

  11.  import java.io.File;

  12.  import java.io.IOException;

  13.  ​

  14.  /**

  15.   * 注册界面的自动化测试

  16.   */

  17.  @TestMethodOrder(MethodOrderer.OrderAnnotation.class)

  18.  public class regTest extends AutoTestUtils {

  19.      public static ChromeDriver driver = new ChromeDriver();

  20.      @Test

  21.      @BeforeAll // 带有BeforeAll注解的方法会在当前类下的所有测试用例之前(方法)执行一次,注意只是执行一次

  22.      public static void init() {

  23.          // 既然是对注册界面的测试,自然要先跳转到该界面

  24.          driver.get("http://www.hello3a87.com/#/Login?login=0");

  25.     }

  26.  ​

  27.      /**

  28.       * 对页面内容的完整性进行测试

  29.       */

  30.      @Test

  31.      @Order(1)

  32.      public void regPageTest() {

  33.          // 利用断言验证页面显示的文本是否正确

  34.          String expect = "注册";

  35.          String actual = driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[1]/h1")).getText();

  36.          Assertions.assertEquals(expect, actual);

  37.  ​

  38.          // 检查博客登录页的主页超链接是否存在

  39.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[1]/p/a"));

  40.          // 检查提交按钮是否存在

  41.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[12]"));

  42.     }

  43.  ​

  44.      /**

  45.       * 正常注册

  46.       */

  47.      @Order(2)

  48.      @ParameterizedTest

  49.      @CsvSource({"懒羊羊,lyy,3390997000@qq.com,123123,123123"})

  50.      public void regRightTest(String username,String nickname,String email,String password1, String password2) throws InterruptedException, IOException {

  51.          // 每次都要提前把之前输入框的内容给清除(不管有没有内容)

  52.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[3]/input")).clear(); //用户名

  53.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[5]/input")).clear(); //昵称

  54.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[6]/input")).clear(); //邮箱

  55.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[8]/input")).clear(); //用户密码

  56.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[10]/input")).clear(); //确认密码

  57.  ​

  58.          // 将信息填入输入框

  59.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[3]/input")).sendKeys(username); //用户名

  60.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[5]/input")).sendKeys(nickname); //昵称

  61.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[6]/input")).sendKeys(email); //邮箱

  62.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[8]/input")).sendKeys(password1); //用户密码

  63.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[10]/input")).sendKeys(password2); //确认密码

  64.          // 找到提交按钮,并点击提交

  65.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[12]")).click();

  66.          // 注册成功后,应该会跳转到登录页面

  67.          Thread.sleep(100);

  68.          String expectURL = "http://www.hello3a87.com/#/Login?login=1";

  69.          String actualURL = driver.getCurrentUrl(); // 获取当前页面的URL

  70.          Assertions.assertEquals(expectURL, actualURL);

  71.          // 获取此时的屏幕截图,此时应该以及跳转到了登录页面

  72.          File srcFile = driver.getScreenshotAs(OutputType.FILE);

  73.          String fileName = "regRightTest.png";

  74.          FileUtils.copyFile(srcFile, new File(fileName));

  75.          // 因为注册成功会跳转到登录界面,所以但接下来我们还有在注册界面测试,所以要回退到注册界面

  76.          driver.navigate().back();

  77.     }

  78.      /**

  79.       * 测试注册失败的情况

  80.       */

  81.      @ParameterizedTest

  82.      @Order(3)

  83.      @CsvFileSource(resources = "/regUsers.csv")

  84.      public void regFailTest(String username,String nickname,String email,String password1, String password2) throws InterruptedException {

  85.          // 每次都要提前把之前输入框的内容给清除(不管有没有内容)

  86.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[3]/input")).clear(); //用户名

  87.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[5]/input")).clear(); //昵称

  88.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[6]/input")).clear(); //邮箱

  89.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[8]/input")).clear(); //用户密码

  90.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[10]/input")).clear(); //确认密码

  91.  ​

  92.          // 将信息填入输入框

  93.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[3]/input")).sendKeys(username); //用户名

  94.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[5]/input")).sendKeys(nickname); //昵称

  95.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[6]/input")).sendKeys(email); //邮箱

  96.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[8]/input")).sendKeys(password1); //用户密码

  97.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[10]/input")).sendKeys(password2); //确认密码

  98.          // 找到提交按钮,并点击提交

  99.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[12]")).click();

  100.          Thread.sleep(100);

  101.     }

  102.  ​

  103.      /**

  104.       * 关闭注册弹窗

  105.       */

  106.      @Test

  107.      @AfterAll  // 带有AfterAll注解的方法会在当前类下的所有测试用例(方法)执行之后 执行一次,注意只是执行一次

  108.      public static void close() {

  109.          driver.quit();

  110.     }

  111.  ​

  112.  }

  113.  ​

博客首页
1、编写测试用例

image-20240402215314586

2、测试首页页面的完整性

image-20240403143119407

3、测试代码和视频

  1.  import common.AutoTestUtils;

  2.  import org.junit.jupiter.api.AfterAll;

  3.  import org.junit.jupiter.api.Assertions;

  4.  import org.junit.jupiter.api.BeforeAll;

  5.  import org.junit.jupiter.api.Test;

  6.  import org.openqa.selenium.By;

  7.  import org.openqa.selenium.chrome.ChromeDriver;

  8.  ​

  9.  import java.time.Duration;

  10.  ​

  11.  /**

  12.   * @author 3A87

  13.   * @description 测试博客首页

  14.   */

  15.  public class PageTest extends AutoTestUtils {

  16.      public static ChromeDriver driver = new ChromeDriver();

  17.      @Test

  18.      @BeforeAll

  19.      static void init() {

  20.          driver.get("http://www.hello3a87.com/#/Home");

  21.     }

  22.      @Test

  23.      void pageTest() {

  24.          // 隐式等待--// 隐式等待,更加丝滑——》作用于下面的整个作用领域,这个方法中的所有元素,在这3秒内不断轮询

  25.          driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));

  26.          // 测试右侧的用户名是否能正常显示

  27.          String expect = "3A87";

  28.          String actual = driver.findElement(By.cssSelector("#app > div > div.container > div > div.el-col.el-col-24.el-col-sm-24.el-col-md-8 > div > section:nth-child(1) > div.r1-body > p")).getText();

  29.          Assertions.assertEquals(expect,actual);

  30.          // 测试博客主页格言是否能正常显示

  31.          String expect1 = "人生人山人海人来人往,自己自尊自爱自由自在";

  32.          String actual1 = driver.findElement(By.cssSelector("#app > div > div:nth-child(1) > div.headImgBox > div.h-information > h2")).getText();

  33.          Assertions.assertEquals(expect1, actual1);

  34.          // 测试菜单栏的首页、分类、赞赏、友链是否能正常显示

  35.          String expect2 = "首页";

  36.          String actual2 = driver.findElement(By.xpath("//*[@id="app"]/div/div[1]/div[1]/div/div/div/ul/li[1]")).getText();

  37.          Assertions.assertEquals(expect2, actual2);

  38.          String expect3 = "分类";

  39.          String actual3 = driver.findElement(By.xpath("//*[@id="app"]/div/div[1]/div[1]/div/div/div/ul/li[2]/div")).getText();

  40.          Assertions.assertEquals(expect3, actual3);

  41.          String expect4 = "赞赏";

  42.          String actual4 = driver.findElement(By.xpath("//*[@id="app"]/div/div[1]/div[1]/div/div/div/ul/li[3]")).getText();

  43.          Assertions.assertEquals(expect4, actual4);

  44.          String expect5 = "友链";

  45.          String actual5 = driver.findElement(By.xpath("//*[@id="app"]/div/div[1]/div[1]/div/div/div/ul/li[4]")).getText();

  46.          Assertions.assertEquals(expect5, actual5);

  47.          // 测试热门文章是否能正常显示

  48.          String expect6 = "热门文章";

  49.          String actual6 = driver.findElement(By.xpath("//*[@id="app"]/div/div[2]/div/div[2]/div/section[2]/h2")).getText();

  50.          Assertions.assertEquals(expect6,actual6);

  51.     }

  52.      @Test

  53.      @AfterAll

  54.      static void exit() {

  55.          driver.quit();

  56.     }

  57.  }

文章详情页
1、编写测试用例

image-20240403145601360

2、测试文章详情页面的完整性

image-20240403144825520

3、测试代码和视频

  1.  import common.AutoTestUtils;

  2.  import org.junit.jupiter.api.Assertions;

  3.  import org.junit.jupiter.api.BeforeAll;

  4.  import org.junit.jupiter.api.Test;

  5.  import org.openqa.selenium.By;

  6.  import org.openqa.selenium.chrome.ChromeDriver;

  7.  ​

  8.  /**

  9.   * @author 3A87

  10.   * @description 测试博客文章详情页

  11.   */

  12.  public class ArticleTest extends AutoTestUtils {

  13.      public static ChromeDriver driver = new ChromeDriver();

  14.      @Test

  15.      @BeforeAll

  16.      static void init() {

  17.          driver.get("http://www.hello3a87.com/#/DetailArticle?aid=16");

  18.     }

  19.  ​

  20.      @Test

  21.      void pageTest(){

  22.          //查看文章标题显示内容是否和期待的一致

  23.          String expect = "Jmeter实战——编写博客标签模块增删改查自动化脚本和压测";

  24.          String actual = driver.findElement(By.xpath("//*[@id="detail"]/div/div[1]/div[1]/header/h1/a")).getText();

  25.          Assertions.assertEquals(expect,actual);

  26.  ​

  27.          //评论区是否正常显示

  28.          String expect1 = "发表评论";

  29.          String actual1 = driver.findElement(By.xpath("//*[@id="detail"]/div/div[1]/div[2]/div[1]/h3")).getText();

  30.          Assertions.assertEquals(expect1,actual1);

  31.     }

  32.  }

个人中心页面
1、编写测试用例

image-20240403193208826

2、准备工作 - 测试前台登录功能

image-20240403193259798

  • 若无等待,程序会找不到页面元素
3、测试个人中心页面的完整性

image-20240403201350316

  • 进入个人中心时鼠标悬浮,选择个人中心

    image-20240403150757604

4、测试编辑功能

image-20240403201651368

  • 强制等待,等upload.exe文件执行完,要不然程序太快,exe还没执行完就点击提交了

  • 这里上传头像采用的方式是AutoIt3工具,此外

    • 如果上传图片的前端代码是用input时,可以使用sendKeys的方式,注意路径名必须是绝对路径! (亲测)

       driver.findElement(By.xpath("定位index的xpath的路径")).sendKeys("图片的绝对路径"); 

      AI写代码

    • 还有使用Robot类的方式,但是我觉得过于繁琐。

    • 其他方式的具体使用可以参考我放在文末的文章链接。

利用AutoIt3工具上传头像使用步骤:

(1)官网下载AutoIt3工具AutoIt Downloads - AutoIt (autoitscript.com)

image-20240403201852673

(2)下载完成后,解压,双击exe文件,安装

image-20240403201934433

(3)打开SciTE Script Editor编写脚本

image-20240403202318293

(4)设置字符集

File→Encoding→UTF-8

防止中文乱码问题

image-20240403202440530

(5)编写脚本

;是注释


  1.  ; 等待 "打开" 对话框出现

  2.  WinWait("打开", "")

  3.  ​

  4.  ; 将焦点设置在 "打开" 对话框中的 "Edit1" 控件上

  5.  ControlFocus("打开","","Edit1")

  6.  ​

  7.  ; 在 "打开" 对话框中的 "Edit1" 控件中设置文本,即文件路径

  8.  ControlSetText("打开", "", "Edit1", "D:\picture\美羊羊.png")

  9.  ​

  10.  ; 在 "打开" 对话框中点击 "Button1" 控件,即确定按钮

  11.  ControlClick("打开", "", "Button1")

我编写脚本的办法有两种:1、和chatgpt对话 2、查阅api文档

打开AutoIt Help File,用好查找的功能

image-20240403203123995

(6)保存为au3格式的脚本

File -> save

(7)将au3格式转为exe文件

打开Compile Script to .exe

image-20240403203419984

(8)在测试代码中用引入

 Runtime.getRuntime().exec("C:\Users\liuxi\Desktop\upload.exe"); 

AI写代码

5、测试代码和视频

  1.  import common.AutoTestUtils;

  2.  import org.apache.commons.io.FileUtils;

  3.  import org.junit.jupiter.api.*;

  4.  import org.junit.jupiter.params.ParameterizedTest;

  5.  import org.junit.jupiter.params.provider.CsvSource;

  6.  import org.openqa.selenium.By;

  7.  import org.openqa.selenium.OutputType;

  8.  import org.openqa.selenium.chrome.ChromeDriver;

  9.  import org.openqa.selenium.interactions.Actions;

  10.  ​

  11.  import java.io.File;

  12.  import java.io.IOException;

  13.  import java.time.Duration;

  14.  ​

  15.  /**

  16.   * @author 3A87

  17.   * @description 测试个人中心功能

  18.   */

  19.  @TestMethodOrder(MethodOrderer.OrderAnnotation.class)

  20.  public class MyPageTest extends AutoTestUtils {

  21.      public static ChromeDriver driver = new ChromeDriver();

  22.      @Test

  23.      @BeforeAll

  24.      static void init() {

  25.          driver.get("http://www.hello3a87.com/#/Home");

  26.     }

  27.  ​

  28.      @Order(1)

  29.      @ParameterizedTest

  30.      @CsvSource({"美羊羊,123123"})

  31.      void LoginTest(String username,String pwd){

  32.          driver.findElement(By.xpath("//*[@id="app"]/div/div[1]/div[1]/div/div/div/ul/div/div[1]/a[1]")).click();

  33.          // 隐式等待--// 隐式等待,更加丝滑——》作用于下面的整个作用领域,这个方法中的所有元素,在这3秒内不断轮询

  34.          driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));

  35.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[3]/input")).sendKeys(username);

  36.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[4]/input")).sendKeys(pwd);

  37.  ​

  38.          driver.findElement(By.xpath("//*[@id="app"]/div/div/div/div/div[5]")).click();

  39.     }

  40.  ​

  41.      @Order(2)

  42.      @Test

  43.      void MyPageTest() throws InterruptedException {

  44.          // 隐式等待--// 隐式等待,更加丝滑——》作用于下面的整个作用领域,这个方法中的所有元素,在这3秒内不断轮询

  45.          driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));

  46.          //鼠标悬浮在 小头像 元素上面

  47.          Actions action = new Actions(driver);

  48.          action.moveToElement(driver.findElement(By.xpath("//div[@class='haslogin']"))).perform();

  49.          Thread.sleep(100);

  50.          //进入个人中心

  51.          driver.findElement(By.linkText("个人中心")).click();

  52.  ​

  53.          Thread.sleep(300);

  54.          //检查页面显示是否完整

  55.          String expect1 = "编辑";

  56.          String button = driver.findElement(By.xpath("//*[@id="app"]/div/div[2]/div[2]/header/h1/span")).getText();

  57.          Assertions.assertEquals(expect1,button);

  58.          String expect2 = "电子邮件";

  59.          String email = driver.findElement(By.xpath("//*[@id="app"]/div/div[2]/div[2]/section/ul/li[3]/span[1]")).getText();

  60.          Assertions.assertEquals(expect2,email);

  61.     }

  62.  ​

  63.  ​

  64.      @Order(3)

  65.      @Test

  66.      void editMyPageTest() throws IOException, InterruptedException {

  67.          // 隐式等待--// 隐式等待,更加丝滑——》作用于下面的整个作用领域,这个方法中的所有元素,在这3秒内不断轮询

  68.          driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));

  69.          //点击编辑按钮

  70.          driver.findElement(By.xpath("//*[@id="app"]/div/div[2]/div[2]/header/h1/span")).click();

  71.          //上传图片

  72.          driver.findElement(By.xpath("//*[@id="app"]/div/div[2]/div[1]/section/ul/li[1]/div/div[1]")).click();

  73.          Runtime.getRuntime().exec("C:\Users\liuxi\Desktop\upload.exe");

  74.          Thread.sleep(3000);

  75.          //修改昵称

  76.          driver.findElement(By.xpath("//*[@id="app"]/div/div[2]/div[1]/section/ul/li[2]/div/input")).clear();

  77.          driver.findElement(By.xpath("//*[@id="app"]/div/div[2]/div[1]/section/ul/li[2]/div/input")).sendKeys("美羊羊");

  78.          //点击提交

  79.          driver.findElement(By.xpath("//*[@id="app"]/div/div[2]/div[1]/section/div/a[2]")).click();

  80.  ​

  81.          Thread.sleep(300);

  82.          // 进行截图,看是否更新了头像和昵称

  83.          File srcFile =  driver.getScreenshotAs(OutputType.FILE);

  84.          String fileName = "MyPageEditTest.png";

  85.          FileUtils.copyFile(srcFile, new File(fileName));

  86.  ​

  87.     }

  88.  ​

  89.      @Test

  90.      @AfterAll

  91.      static void exit() {

  92.          driver.quit();

  93.     }

  94.  ​

  95.  }

  96.  ​

总结

通过这次对我的个人博客系统进行自动化测试,

  • 我上手了Junit5测试框架

    • 套件的使用
    • 执行顺序的控制
    • 参数化(单值,批量,多个测试用例)
  • 我熟悉了selenium的使用

    • 对等待的理解更深,隐式等待、显示等待、强制等待
    • 元素定位的方法,xpath,css
    • 隐藏元素的定位,弹窗的定位
    • 断言的使用
    • 截图的使用
    • 鼠标悬浮
    • 刷新、回退页面
  • 我接触了新工具AutoIt3

感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取   

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐