作者简介:大家好,我是未央;
博客首页:未央.303
系列专栏:测试开发项目
每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!!!!
前言
1.针对博客笔记项目进行测试,博客笔记主要由四个页面构成:博客登录页、博客列表页、博客详情页和博客编辑页;
2.主要功能包括:登录、编辑发布博客、查看博客详情页、删除博客以及注销用户等功能。对于个人博客的测试主要就是针对主要功能进行测试,然后按照页面书写测试类。
3.博客笔记项目CSDN博客链接:
4.自动化测试一般步骤:
- step1)使用脑图编写web自动化测试用例
- step2)创建自动化项目,根据用例来实现脚本
一、项目自动化测试用例设计
1.1 脑图的编写
通过页面书写测试类,然后针对主要的功能进行自动化测试。
二、自动化测试代码编写
代码编写整体思路步骤:
- step1:根据脑图进行测试用例的编写:每个页面一个测试类,然后再各个测试类中进行测试用例的编写。
- step2:使用测试套件便于运行以及修改。
- step3:创建启动以及现场截图就是会频繁进行复用,所以单独创建一个类进行存储。
- step4:注意公共属性需要单独放一个类,方便进行代码复用。
- step5:注意添加隐式等待,为了确保页面正确加载显示。
2.1 登录页面的自动化测试
2.1.1 准备工作
1. 新建包并在包下创建测试类以及公共类
以下是所建立的是common公共包和Tests测试包:
2.添加相关依赖pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>BlogAutoTest</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <!-- 添加相关依赖pom.xml--> <dependencies> <!-- 添加selenium依赖--> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>4.0.0</version> </dependency> <!-- 保存屏幕截图需要用到的包--> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency> <!-- 添加junit5依赖--> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.8.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-suite</artifactId> <version>1.8.2</version> <scope>test</scope> </dependency> </dependencies> </project>
3.因为我们每个测试用例都要 创建驱动实例,进入到用户登录页面、所以我们不妨这样做:
这样再将我们的测试用例按一定的顺序来执行,就会使得我们的整个测试过程很流程、自然。
2.1.2 创建公共类AutoTestUtils
创建公共类AutoTestUtils的作用:创建驱动、保存现场截图.
注意点(1):
在保存现场截图的时候命名是按时间来进行文件夹的划分,然后图片的名称要体现出测试类的类名,方便进行问题的追溯。
注意点(2):
可以在创建驱动的时候修改默认的有头模式or无头模式
注意点(3):
注意文件名的动态获取,注意时间格式的设置。
公共类AutoTestUtils的测试总代码:
package com.blogWebAutoTest.common; import org.apache.commons.io.FileUtils; import org.openqa.selenium.OutputType; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.time.Duration; import java.util.ArrayList; import java.util.List; public class ceshi { // 为了能够降低代码冗余度,直接在这里实现代码的复用 public static ChromeDriver driver; // 创建驱动对象createDriver public static ChromeDriver createDriver() { // 创建无头模式 ChromeOptions options = new ChromeOptions(); options.addArguments("-headless"); // 判断驱动对象是否已经创建完成 if(driver == null) { // 如果没有创建完成,则创建一个驱动对象 driver = new ChromeDriver(options); // 默认的有头模式 // driver = new ChromeDriver(); // 创建隐式等待,为了确保页面渲染出来 driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(6)); } return driver; } /* * 获取屏幕截图 * 把所有用例的执行结果进行截图保存下来 * 注意:保存的屏幕截图的名字是动态生成的,否则会导致图片的覆盖 */ // 获取动态文件名 public List<String> getTime() { // 期望的时间格式:20230215-时间(到毫秒) // 进行时间的格式化 SimpleDateFormat sim1 = new SimpleDateFormat("yyyyMMdd-HHmmssSS"); String fileName = sim1.format(System.currentTimeMillis()); // 希望文件按每日的维度进行文件夹的分类保存 SimpleDateFormat sim2 = new SimpleDateFormat("yyyyMMdd"); String dirName = sim2.format(System.currentTimeMillis()); // 文件夹的名字 // 最后使用链表进行保存 List<String> list = new ArrayList<>(); list.add(dirName); list.add(fileName); return list; } // 为了区分是哪个用例返回的,需要加上用例的名称进行保存 public void getScreenShot(String str) throws IOException { List<String> list = getTime(); // 文件保存路径:dirName+fileName // ./指的是当前路径,这里来说就是BlogAutoTest目录下 // 如果目前期望的路径:./src/test/java/com/blogWebAutoTest/dirName/fileName // 注意:图片保存的路径和名称!!!!! String fileName = "./src/test/java/com/blogWebAutoTest/" + list.get(0) + "/" + str + "_" + list.get(1) + ".png"; // 保存的路径+名称 File scrFile = driver.getScreenshotAs(OutputType.FILE); // 获取到的屏幕截图 // 把屏幕截图生成的图片文件放到指定路径下 FileUtils.copyFile(scrFile,new File(fileName)); } }
2.1.3 测试登录页面是否正常打开
测试代码:
/* * 检查登录页面打开是否正确: * 检查点:右上角的显示、左上角的显示以及登录框等 */ @Test @Order(1) void loginPageLoadRight() throws IOException { //隐式等待:更加丝滑,作用于下面的整个作用领域.这个方法的所有元素,在这3秒内进行不断轮询 driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); //通过检查页面上是否存在对应元素检查页面的正确打开。 driver.findElement(By.cssSelector("body > div.nav > a:nth-child(4)")); driver.findElement(By.xpath("/html/body/div[1]/span")); driver.findElement(By.cssSelector("body > div.login-container > form > div")); //进行屏幕截图操作,保存现场操作 getScreenShot(getClass().getName()); }
测试登录页面是否正常打开
测试结果展示:
测试登录页面是否正常打开
2.1.3 测试正常登录(多参数测试)
测试代码:
/* * 检查登录正常情况(使用多参数测试) */ @Order(3) @ParameterizedTest @CsvSource({"未央,123","川屿,12138"})//使用多参数测试. void loginSuc(String name, String passwd) throws InterruptedException, IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); // 多个账号登录,在重新输入账号时,需要把之前的输入的内容清空 driver.findElement(By.cssSelector("#username")).clear(); driver.findElement(By.cssSelector("#password")).clear(); driver.findElement(By.cssSelector("#username")).sendKeys(name); driver.findElement(By.cssSelector("#password")).sendKeys(passwd); //点击登录按钮login-button,验证正常登录。 driver.findElement(By.cssSelector("#login-button")).click(); //进行屏幕截图操作,保存现场操作 getScreenShot(getClass().getName()); // 以上是登录步骤,但是并不能确保就是登录成功的 // 所以必须对登录成功以后的页面进行查找:即通过查找“全文”按钮,判断是否进入到了博客列表页 driver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > a")); // 因为要多参数,所以在执行完一遍执行下一遍的时候需要进行页面的回退,否则根本找不到登录框 driver.navigate().back(); // Thread.sleep(5000); // 强制等待,看看效果 }
测试正常登录(多参数测试)
测试结果展示:
测试正常登录(多参数测试)
2.1.5 测试登录失败
测试代码:
/* * 检查异常登录情况: * 多参数测试:参数为空 或者 参数非空两种情况。 * 注意:登录失败的检查元素和成功是不一样的 */ @Order(2) @ParameterizedTest @CsvSource({"未央,123","川屿,12138"}) void loginFail(String name, String passwd) throws InterruptedException, IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); // 在每次登录之后都要进行清空,然后才能重新输入 driver.findElement(By.cssSelector("#username")).clear(); driver.findElement(By.cssSelector("#password")).clear(); driver.findElement(By.cssSelector("#username")).sendKeys(name); driver.findElement(By.cssSelector("#password")).sendKeys(passwd); driver.findElement(By.cssSelector("#login-button")).click(); getScreenShot(getClass().getName()); // 登录失败的检测,不能仅通过body来进行校验判断,因为body在登录成功的页面也能够获取 // 所以使用获取文本进行比对,对参数为空和参数错误要区别。 /* String expectNull = "用户名或密码为空!!登陆失败!!"; // 参数为空情况 String expectNotNull = "用户名或密码错误!! 登陆失败!!"; // 参数错误情况 String actual = driver.findElement(By.cssSelector("body")).getText(); if(name.equals(null) || passwd.equals(null)) { Assertions.assertEquals(expectNull,actual); } else { Assertions.assertEquals(expectNotNull,actual); } */ // 这里注意一下:为空直接会报错,所以此时不检验 String expect = "用户名或密码错误!! 登陆失败!!"; String actual = driver.findElement(By.cssSelector("body")).getText(); Assertions.assertEquals(expect,actual); // 如果登录失败,则会跳转导航回到登录页 driver.navigate().back(); // Thread.sleep(5000); }
测试登录失败
测试结果展示:
测试登录失败
2.1.6 登录页面总代码
登录页面测试总代码:
package com.blogWebAutoTest.Tests; import com.blogWebAutoTest.common.AutoTestUtils; import org.junit.jupiter.api.*; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.openqa.selenium.By; import org.openqa.selenium.chrome.ChromeDriver; import java.io.IOException; import java.time.Duration; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class BlogLoginTest extends AutoTestUtils { // 注意:一定要关注测试用例执行顺序的问题 public static ChromeDriver driver = createDriver(); // 如果要测试登录页面,则所有的用来都会用到 创建驱动+打开浏览器 @BeforeAll static void baseControl() { driver.get("http://140.210.201.164:8080/blog_system/login.html"); } } /* * 检查登录页面打开是否正确: * 检查点:右上角的显示、左上角的显示以及登录框等 */ @Test @Order(1) void loginPageLoadRight() throws IOException { //隐式等待:更加丝滑,作用于下面的整个作用领域.这个方法的所有元素,在这3秒内进行不断轮询 driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); //通过检查页面上是否存在对应元素检查页面的正确打开。 driver.findElement(By.cssSelector("body > div.nav > a:nth-child(4)")); driver.findElement(By.xpath("/html/body/div[1]/span")); driver.findElement(By.cssSelector("body > div.login-container > form > div")); //进行屏幕截图操作,保存现场操作 getScreenShot(getClass().getName()); } /* * 检查登录正常情况(使用多参数测试) */ @Order(3) @ParameterizedTest @CsvSource({"未央,123","川屿,12138"})//使用多参数测试. void loginSuc(String name, String passwd) throws InterruptedException, IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); // 多个账号登录,在重新输入账号时,需要把之前的输入的内容清空 driver.findElement(By.cssSelector("#username")).clear(); driver.findElement(By.cssSelector("#password")).clear(); driver.findElement(By.cssSelector("#username")).sendKeys(name); driver.findElement(By.cssSelector("#password")).sendKeys(passwd); //点击登录按钮login-button,验证正常登录。 driver.findElement(By.cssSelector("#login-button")).click(); //进行屏幕截图操作,保存现场操作 getScreenShot(getClass().getName()); // 以上是登录步骤,但是并不能确保就是登录成功的 // 所以必须对登录成功以后的页面进行查找:即通过查找“全文”按钮,判断是否进入到了博客列表页 driver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > a")); // 因为要多参数,所以在执行完一遍执行下一遍的时候需要进行页面的回退,否则根本找不到登录框 driver.navigate().back(); // Thread.sleep(5000); // 强制等待,看看效果 } /* * 检查异常登录情况: * 多参数测试:参数为空 或者 参数非空两种情况。 * 注意:登录失败的检查元素和成功是不一样的 */ @Order(2) @ParameterizedTest @CsvSource({"未央,123","川屿,12138"}) void loginFail(String name, String passwd) throws InterruptedException, IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); // 在每次登录之后都要进行清空,然后才能重新输入 driver.findElement(By.cssSelector("#username")).clear(); driver.findElement(By.cssSelector("#password")).clear(); driver.findElement(By.cssSelector("#username")).sendKeys(name); driver.findElement(By.cssSelector("#password")).sendKeys(passwd); driver.findElement(By.cssSelector("#login-button")).click(); getScreenShot(getClass().getName()); // 登录失败的检测,不能仅通过body来进行校验判断,因为body在登录成功的页面也能够获取 // 所以使用获取文本进行比对,对参数为空和参数错误要区别。 /* String expectNull = "用户名或密码为空!!登陆失败!!"; // 参数为空情况 String expectNotNull = "用户名或密码错误!! 登陆失败!!"; // 参数错误情况 String actual = driver.findElement(By.cssSelector("body")).getText(); if(name.equals(null) || passwd.equals(null)) { Assertions.assertEquals(expectNull,actual); } else { Assertions.assertEquals(expectNotNull,actual); } */ // 这里注意一下:为空直接会报错,所以此时不检验 String expect = "用户名或密码错误!! 登陆失败!!"; String actual = driver.findElement(By.cssSelector("body")).getText(); Assertions.assertEquals(expect,actual); // 如果登录失败,则会跳转导航回到登录页 driver.navigate().back(); // Thread.sleep(5000); }
2.2 列表页面的自动化测试
每个测试用例都要 创建驱动实例,进入到用户登录页面、所以我们不妨这样做:
这样将我们的测试用例按一定的顺序来执行,就会使得我们的整个测试过程很流程、自然。
2.2.1 测试列表页是否可以正常打开
测试代码:
/* * 博客列表页可以正常显示的测试 * 检查:个人的信息、菜单栏、列表以及右上角的模块内容 */ @Test @Order(1) void listPageLoadRight() throws IOException { // 可以多检查几个,确保正确 driver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > div.title")); driver.findElement(By.cssSelector("body > div.container > div.container-left > div > img")); driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)")); //进行屏幕截图操作,保存现场操作 getScreenShot(getClass().getName()); }
测试列表页是否可以正常打开
测试结果展示:
测试列表页是否可以正常打开
2.2.2 测试列表页的“查看全文”按钮是否可以正常跳转
测试代码:
/* * 测试“查看全文“的按钮是否能点击 * 在点击之后,跳转到博客详情页 */ @Test @Order(2) void listInspectAll() throws IOException { driver.findElement(By.xpath("/html/body/div[2]/div[2]/div[1]/a")).click(); driver.findElement(By.cssSelector("body > div.container > div.container-right")); driver.findElement(By.cssSelector("#delete_button")); //进行屏幕截图操作,保存现场操作 getScreenShot(getClass().getName()); }
2.2.3 测试未登录的状态是否会跳转到登录页面
测试代码:
/* * 未登录:则直接回到登录页面 * 点击注销后,直接输入链接打开,然后进行元素的检查。 * 最后要登录保持登录的状态,方便后续使用。 */ @Test @Order(3) void listFail() throws IOException { //点击注销后直接输入链接打开,然后进行元素的检查 driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)")).click(); driver.get("http://140.210.201.164:8080/blog_system/blog_list.html"); driver.findElement(By.cssSelector("body > div.login-container > form > div")); //进行屏幕截图操作,保存现场操作 getScreenShot(getClass().getName()); //最后要进行登录方便后续使用 driver.findElement(By.cssSelector("#username")).sendKeys("未央"); driver.findElement(By.cssSelector("#password")).sendKeys("123"); driver.findElement(By.cssSelector("#login-button")).click(); //进行屏幕截图操作,保存现场操作 getScreenShot(getClass().getName()); }
2.2.4 列表页面总代码
列表页面测试总代码:
package com.blogWebAutoTest.Tests; import com.blogWebAutoTest.common.AutotestUtils; import org.junit.jupiter.api.*; import org.openqa.selenium.By; import org.openqa.selenium.chrome.ChromeDriver; import java.io.IOException; import java.time.Duration; // 博客列表页测试 //上一步是在登录成功 @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class blogListTest extends AutotestUtils { public static ChromeDriver driver = createDriver(); @BeforeAll static void baseControl() { driver.get("http://140.210.201.164:8080/blog_system/blog_list.html"); } /* * 博客列表页可以正常显示的测试 * 检查:个人的信息、菜单栏、列表以及右上角的模块内容 */ @Test @Order(1) void listPageLoadRight() throws IOException { // 可以多检查几个,确保正确 driver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > div.title")); driver.findElement(By.cssSelector("body > div.container > div.container-left > div > img")); driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)")); //进行屏幕截图操作,保存现场操作 getScreenShot(getClass().getName()); } /* * 测试“查看全文“的按钮是否能点击 * 在点击之后,跳转到博客详情页 */ @Test @Order(2) void listInspectAll() throws IOException { driver.findElement(By.xpath("/html/body/div[2]/div[2]/div[1]/a")).click(); driver.findElement(By.cssSelector("body > div.container > div.container-right")); driver.findElement(By.cssSelector("#delete_button")); //进行屏幕截图操作,保存现场操作 getScreenShot(getClass().getName()); } /* * 未登录:则直接回到登录页面 * 点击注销后,直接输入链接打开,然后进行元素的检查。 * 最后要登录保持登录的状态,方便后续使用。 */ @Test @Order(3) void listFail() throws IOException { //点击注销后直接输入链接打开,然后进行元素的检查 driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)")).click(); driver.get("http://140.210.201.164:8080/blog_system/blog_list.html"); driver.findElement(By.cssSelector("body > div.login-container > form > div")); //进行屏幕截图操作,保存现场操作 getScreenShot(getClass().getName()); //最后要进行登录方便后续使用 driver.findElement(By.cssSelector("#username")).sendKeys("未央"); driver.findElement(By.cssSelector("#password")).sendKeys("123"); driver.findElement(By.cssSelector("#login-button")).click(); //进行屏幕截图操作,保存现场操作 getScreenShot(getClass().getName()); } }
测试成功结果:
2.3 编辑页面的自动化测试
2.3.1 测试编辑页是否可以正确打开
测试代码:
/* * 测试编辑页可以正常打开 */ @Test @Order(1) void editPageLoadRight() throws IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); // 可以多检查几个,确保正确 driver.findElement(By.cssSelector("#blog-title")); driver.findElement(By.cssSelector("#editor > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll")); driver.findElement(By.xpath("//*[@id=\"submit\"]")); getScreenShot(getClass().getName()); }
2.3.2 测试博客是否可以正常编辑和发布
测试代码:
/* * 正确编辑并发布博客测试 * 目前是在列表页,直接点击列表页的按钮跳转到博客编辑页 * 然后检查元素就行,注意要验证博客编辑页的特定的元素 */ @Test @Order(3) void editAndSubmitBlog() throws IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); driver.findElement(By.xpath("/html/body/div[1]/a[2]")).click(); editPageLoadRight(); String expect = "autoTest耶耶"; // 编辑页的md是第三方插件,所以不可以直接使用sendKeys向编辑模块写入内容 // 但是可以通过点击上方按钮进行内容的插入 driver.findElement(By.cssSelector("#blog-title")).sendKeys(expect); driver.findElement(By.cssSelector("#editor > div.editormd-toolbar > div > ul > li:nth-child(5) > a")).click(); driver.findElement(By.xpath("//*[@id=\"editor\"]/div[5]/div/ul/li[20]/a")).click(); driver.findElement(By.cssSelector("#editor > div.editormd-toolbar > div > ul > li:nth-child(6) > a")).click(); driver.findElement(By.xpath("//*[@id=\"submit\"]")).click(); getScreenShot(getClass().getName()); // 判断是否发布成功: // 获取第一篇博客的title的文本,检查是否相符即可(一定要找特定元素) String actual = driver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > div.title")).getText(); Assertions.assertEquals(expect,actual); }
2.3.3 测试“写博客”按钮是否可以正常使用
测试代码:
/* * 测试“写博客”的按钮是否可以点击 * 发布测试(无标题) */ @Test @Order(2) void editWriteAndNoTitle() throws IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); // 进行无标题测试 driver.findElement(By.cssSelector("#editor > div.editormd-toolbar > div > ul > li:nth-child(5) > a")).click(); driver.findElement(By.xpath("//*[@id=\"editor\"]/div[5]/div/ul/li[20]/a")).click(); driver.findElement(By.cssSelector("#editor > div.editormd-toolbar > div > ul > li:nth-child(6) > a")).click(); driver.findElement(By.cssSelector("#submit")).click(); getScreenShot(getClass().getName()); // 进行博客列表页的元素检查 driver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > a")); }
2.3.4 编辑页面总代码
编辑页面测试总代码:
package com.blogWebAutoTest.Tests; import com.blogWebAutoTest.common.AutotestUtils; import org.junit.jupiter.api.*; import org.openqa.selenium.By; import org.openqa.selenium.chrome.ChromeDriver; import java.io.IOException; import java.time.Duration; import static com.blogWebAutoTest.common.AutotestUtils.driver; // 博客编辑页测试 @TestMethodOrder(MethodOrderer.OrderAnnotation.class) static class blogEditTest extends AutotestUtils { //在这创建驱动对象,保证后面都能用到他 public static ChromeDriver driver = createDriver(); @BeforeAll static void baseControl() { driver.get("http://140.210.201.164:8080/blog_system/blog_edit.html"); } } /* * 测试编辑页可以正常打开 */ @Test @Order(1) void editPageLoadRight() throws IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); // 可以多检查几个,确保正确 driver.findElement(By.cssSelector("#blog-title")); driver.findElement(By.cssSelector("#editor > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll")); driver.findElement(By.xpath("//*[@id=\"submit\"]")); getScreenShot(getClass().getName()); } /* * 正确编辑并发布博客测试 * 目前是在列表页,直接点击列表页的按钮跳转到博客编辑页 * 然后检查元素就行,注意要验证博客编辑页的特定的元素 */ @Test @Order(3) void editAndSubmitBlog() throws IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); driver.findElement(By.xpath("/html/body/div[1]/a[2]")).click(); editPageLoadRight(); String expect = "autoTest耶耶"; // 编辑页的md是第三方插件,所以不可以直接使用sendKeys向编辑模块写入内容 // 但是可以通过点击上方按钮进行内容的插入 driver.findElement(By.cssSelector("#blog-title")).sendKeys(expect); driver.findElement(By.cssSelector("#editor > div.editormd-toolbar > div > ul > li:nth-child(5) > a")).click(); driver.findElement(By.xpath("//*[@id=\"editor\"]/div[5]/div/ul/li[20]/a")).click(); driver.findElement(By.cssSelector("#editor > div.editormd-toolbar > div > ul > li:nth-child(6) > a")).click(); driver.findElement(By.xpath("//*[@id=\"submit\"]")).click(); getScreenShot(getClass().getName()); // 判断是否发布成功: // 获取第一篇博客的title的文本,检查是否相符即可(一定要找特定元素) String actual = driver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > div.title")).getText(); Assertions.assertEquals(expect,actual); } /* * 测试“写博客”的按钮是否可以点击 * 发布测试(无标题) */ @Test @Order(2) void editWriteAndNoTitle() throws IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); // 进行无标题测试 driver.findElement(By.cssSelector("#editor > div.editormd-toolbar > div > ul > li:nth-child(5) > a")).click(); driver.findElement(By.xpath("//*[@id=\"editor\"]/div[5]/div/ul/li[20]/a")).click(); driver.findElement(By.cssSelector("#editor > div.editormd-toolbar > div > ul > li:nth-child(6) > a")).click(); driver.findElement(By.cssSelector("#submit")).click(); getScreenShot(getClass().getName()); // 进行博客列表页的元素检查 driver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > a")); }
2.4 详情页面的自动化测试
2.4.1 测试详情页是否可以正确打开
测试代码:
/* * 详情页是否可以正常打开 * 第一种情况:没有blogId * 因为没有blogId会出现Undefined */ @Test @Order(1) void undefindedTest() throws IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); String expect = ""; String fact = driver.findElement(By.xpath("/html/body/div[2]/div[2]/div/div[1]")).getText(); getScreenShot(getClass().getName()); Assertions.assertEquals(expect,fact); } /* * 测试详情页正确打开 * 第二种情况:有blogId * 通过验证测试标题以及时间一定有来判断是否正确打开 */ @Test @Order(2) void blogDetailPageRight() throws IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); driver.get("http://140.210.201.164:8080/blog_system/blog_detail.html?blogId=17"); driver.findElement(By.cssSelector("body > div.container > div.container-right > div > h3")); driver.findElement(By.cssSelector("body > div.container > div.container-right > div > div.date")); driver.findElement(By.xpath("//*[@id=\"content\"]/p")); getScreenShot(getClass().getName()); driver.findElement(By.xpath("/html/body/div[1]/a[1]")).click(); // 回到列表页 }
2.4.2 测试“删除”按钮是否可用
测试代码:
/* * 测试“删除”按钮是否可点击 * 删除后会跳回到博客列表页,然后与最上面的时间进行比对即可 * 注意:不能比较标题,因为标题可能为空,无法比较。 * 这里是要删除第一篇博客 */ @Test @Order(3) void deleteTest() throws IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); getScreenShot(getClass().getName()); driver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > a")).click(); String before = driver.findElement(By.xpath("/html/body/div[2]/div[2]/div/div[1]")).getText(); driver.findElement(By.cssSelector("#delete_button")).click(); String after = driver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > div.date")).getText(); getScreenShot(getClass().getName()); Assertions.assertNotEquals(before,after); // 比对不同 }
2.4.3 详情页面总代码
package com.blogWebAutoTest.Tests; import org.junit.jupiter.api.*; import org.openqa.selenium.By; import org.openqa.selenium.chrome.ChromeDriver; import java.io.IOException; import java.time.Duration; import static com.blogWebAutoTest.common.AutotestUtils.createDriver; import static com.blogWebAutoTest.common.AutotestUtils.driver; // 博客详情页测试 @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class blogDetailTest { public static ChromeDriver driver = createDriver(); @BeforeAll static void baseControl() { driver.get("http://140.210.201.164:8080/blog_system/blog_detail.html"); } } /* * 详情页是否可以正常打开 * 第一种情况:没有blogId * 因为没有blogId会出现Undefined */ @Test @Order(1) void undefindedTest() throws IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); String expect = ""; String fact = driver.findElement(By.xpath("/html/body/div[2]/div[2]/div/div[1]")).getText(); getScreenShot(getClass().getName()); Assertions.assertEquals(expect,fact); } /* * 测试详情页正确打开 * 第二种情况:有blogId * 通过验证测试标题以及时间一定有来判断是否正确打开 */ @Test @Order(2) void blogDetailPageRight() throws IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); driver.get("http://140.210.201.164:8080/blog_system/blog_detail.html?blogId=17"); driver.findElement(By.cssSelector("body > div.container > div.container-right > div > h3")); driver.findElement(By.cssSelector("body > div.container > div.container-right > div > div.date")); driver.findElement(By.xpath("//*[@id=\"content\"]/p")); getScreenShot(getClass().getName()); driver.findElement(By.xpath("/html/body/div[1]/a[1]")).click(); // 回到列表页 } /* * 测试“删除”按钮是否可点击 * 删除后会跳回到博客列表页,然后与最上面的时间进行比对即可 * 注意:不能比较标题,因为标题可能为空,无法比较。 * 这里是要删除第一篇博客 */ @Test @Order(3) void deleteTest() throws IOException { driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3)); getScreenShot(getClass().getName()); driver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > a")).click(); String before = driver.findElement(By.xpath("/html/body/div[2]/div[2]/div/div[1]")).getText(); driver.findElement(By.cssSelector("#delete_button")).click(); String after = driver.findElement(By.cssSelector("body > div.container > div.container-right > div:nth-child(1) > div.date")).getText(); getScreenShot(getClass().getName()); Assertions.assertNotEquals(before,after); // 比对不同 } // // 注意更改驱动释放位置(在最后一个类/测试用例之后) // @AfterAll // static void driverQuite() { // driver.quit(); // }
三、自动化测试总结
3.1 测试小结
测试要点总结:
要点1:页面元素的检查
对于页面的检查一定要到位,如检查元素是否存在来确保页面的正确性。
要点2:测试用例的执行顺序
一定要关注测试用例的执行顺序问题,防止出现逻辑错误导致自动化测试失败。
要点3:多参数测试
在进行登录页面多参数测试时候,注意之前账号的清空和页面的回退。
要点4:适当关注用例执行时间
如果时间过长就需要考虑是我们自己写的测试用例的问题还是程序真的有性能问题呢
要点5:获取元素的时候建议获取固定的元素
建议获取如时间、标题等;内容不建议获取,因为是动态的。
元素的路径是不可变的,所以可以使用xpath来进行元素的定位。
要点6:单独写一个类来存放驱动释放
为了避免遗漏or遗忘驱动释放的位置,然后直接放到套件测试类的最后就行。
要点7:注意屏幕截图保存的方式
动态时间戳并进行时间格式化,然后期望按照某种维度(天)以文件夹的方式进行保存。
要点8:驱动关闭的位置
驱动关闭的位置要注意,只有最后一个用例结束之后才进行关闭。
3.2 自动化测试过程的亮点
亮点1:无头模式
使用了无头模式:只注重结果,可以留出屏幕。
具体表现:
亮点2:使用JUnit5种的注解
避免生成过多的对象,造成资源和时间的浪费,提高了自动化的执行效率。
亮点3:使用了隐式等待
提高了自动化的运行效率,提高了自动化的稳定性,减小误报的可能性。
具体表现:
亮点4:使用了屏幕截图
方便对问题的追溯以及问题的解决,能够在出现问题时候快速检索问题,找到依据。
具体表现:
亮点5:使用测试套件
降低了测试人员的工作量,通过套件一次执行所有要运行的测试用例。
具体表现: