博客笔记项目的自动化测试

作者简介:大家好,我是未央;

博客首页:未央.303

系列专栏:测试开发项目

每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!!!!


文章目录

前言

一、项目自动化测试用例设计

1.1 脑图的编写

二、自动化测试代码编写

2.1 登录页面的自动化测试

2.1.1 准备工作

2.1.2 创建公共类AutoTestUtils

2.1.3 测试登录页面是否正常打开

2.1.3 测试正常登录(多参数测试)

2.1.5 测试登录失败

2.1.6 登录页面总代码

2.2 列表页面的自动化测试

2.2.1 测试列表页是否可以正常打开

2.2.2 测试列表页的“查看全文”按钮是否可以正常跳转

2.2.3 测试未登录的状态是否会跳转到登录页面

2.2.4 列表页面总代码

2.3 编辑页面的自动化测试

2.3.1 测试编辑页是否可以正确打开

2.3.2 测试博客是否可以正常编辑和发布 

2.3.3 测试“写博客”按钮是否可以正常使用

2.3.4 编辑页面总代码

2.4 详情页面的自动化测试

2.4.1 测试详情页是否可以正确打开

2.4.2 测试“删除”按钮是否可用

2.4.3 详情页面总代码

三、自动化测试总结

3.1 测试小结

3.2 自动化测试过程的亮点



前言

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:使用测试套件

降低了测试人员的工作量,通过套件一次执行所有要运行的测试用例。

具体表现:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/424557.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mongodb基础(node.js版)

一、Mongodb 介绍 Mongodb 是一个文档数据库&#xff0c;以文档形式存储数据&#xff0c;格式类似于 JSON 与 Mysql 的特点及选型对照 MongodbMysql关系类型非关系型关系型存储类型文档存储&#xff08;类似于写 Word &#xff09;表格存储 &#xff08;类似于写 Excle&…

浙江大学主办!2024年第7届信息通信与信号处理国际会议( ICICSP2024)征稿开启!

会议官网 IEEE | ICICSP 2024 学术会议查询-学术会议交流服务平台-爱科会易 (uconf.com)​www.uconf.com/

Day12:信息打点-Web应用源码泄漏开源闭源指纹识别GITSVNDS备份

目录 开源-CMS指纹识别源码获取方式 闭源-习惯&配置&特性等获取方式 闭源-托管资产平台资源搜索监控 思维导图 章节点 Web&#xff1a;语言/CMS/中间件/数据库/系统/WAF等 系统&#xff1a;操作系统/端口服务/网络环境/防火墙等 应用&#xff1a;APP对象/API接口/微…

17.来自Sora的夺舍妄想——享元模式详解

OpenAI 的 Sora 模型面世之后&#xff0c;可以说人类抵御AI的最后阵地也沦陷了。 在此之前&#xff0c;人们面对AI交互式对话&#xff0c;AI制图&#xff0c;AI建模之类的奇迹时&#xff0c;还可以略微放肆的说&#xff1a;“的确很神奇&#xff0c;这毕竟还是比人类世界低了一…

【go从入门到精通】go包,内置类型和初始化顺序

大家好&#xff0c;这是我给大家准备的新的一期专栏&#xff0c;专门讲golang&#xff0c;从入门到精通各种框架和中间件&#xff0c;工具类库&#xff0c;希望对go有兴趣的同学可以订阅此专栏。 go基础 。 Go文件名&#xff1a; 所有的go源码都是以 ".go" 结尾&…

pytorch -- torch.nn网络结构

1. 官网描述 官网 使用 torch.nn 模块&#xff0c;我们可以创建自定义的神经网络模型&#xff0c;并使用 PyTorch 提供的优化器&#xff08;如 torch.optim&#xff09;和损失函数来训练和优化模型。 2. 常见结构 1.卷积操作 定义&#xff1a; 二维卷积 1.1版本 nn.functio…

香港大学发布思维扩散DoT,让思维在时间上扩散,提效保质!

引言&#xff1a;探索结合扩散模型与思维链来提升大模型推理能力 在人工智能领域&#xff0c;大语言模型&#xff08;LLMs&#xff09;已经引起了广泛的关注&#xff0c;它们在自然语言处理和机器学习的经典问题上展现出了显著的推理能力。特别是&#xff0c;思维链&#xff0…

Vue开发实例(九)动态路由实现左侧菜单导航

之前在【Vue开发实例&#xff08;六&#xff09;实现左侧菜单导航】文中实现了菜单的导航&#xff0c;本篇是在那个基础上改造的。 动态路由实现左侧菜单导航 一、动态菜单创建二、根据菜单数据来创建路由三、添加路由已加载标记&#xff0c;省的每次点击菜单都要加载 一、动态…

开源项目:图像分类算法在保险行业的创新应用与实践

一、引言 在当今数字化时代&#xff0c;保险行业正经历着前所未有的变革。传统保险公司面临着新兴科技的挑战&#xff0c;被迫重新思考其业务模式和营销策略。在这种背景下&#xff0c;我有幸参与了一个项目&#xff0c;该项目旨在通过整合多种销售渠道和技术手段&#xff0c;提…

【蓝桥杯】错误票据

今天是2024年3月1号&#xff0c;蓝桥杯比赛还有一个月的时间&#xff0c;虽说自己不指望拿奖吧&#xff0c;但是还是有些莫i名的焦虑&#xff0c;这道题目都做不出来&#xff0c;感觉自己真的有点菜啊&#xff01;但是还好啦&#xff0c;我觉得是因为我没有题感&#xff0c;慢慢…

DDS数据分发服务——提升汽车领域数据传输效率

1.引言 随着智能化技术的快速发展&#xff0c;汽车行业正经历着一场革命性的变革。如今的分布式系统变得越来越复杂且庞大&#xff0c;对网络通信基数要求在功能和性能层面越来越高。数据分发服务&#xff08;DDS&#xff09;作为一项先进的数据传输解决方案&#xff0c;在汽车…

Redis-基础篇

Redis是一个开源、高性能、内存键值存储数据库&#xff0c;由 Salvatore Sanfilippo&#xff08;网名antirez&#xff09;创建&#xff0c;并在BSD许可下发布。它不仅可以用作缓存系统来加速数据访问&#xff0c;还可以作为持久化的主数据存储系统或消息中间件使用。Redis因其数…

【大数据架构(3)】Lambda vs. Kappa Architecture-选择你需要的架构

文章目录 一. Data Processing Architectures1. Lambda Architecture1.1. 架构说明a. Data Ingestion Layerb. Batch Layer (Batch processing)c. Speed Layer (Real-Time Data Processing)d. Serving Layer 1.2. Lambda Architecture的优缺点1.3. 使用案例 2. Kappa Architect…

数据分析-Pandas数据的探查面积图

数据分析-Pandas数据的探查面积图 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表&…

MyBatis 面试题

什么是MyBatis&#xff1f; MyBatis 是一个开源、轻量级的数据持久化框架&#xff0c;是 JDBC 和 Hibernate 的替代方案。MyBatis 内部封装了 JDBC&#xff0c;简化了加载驱动、创建连接、创建 statement 等繁杂的过程&#xff0c;开发者只需要关注 SQL 语句本身。 MyBatis 支…

静态时序分析:SDC约束命令set_case_analysis详解

相关阅读 静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html?spm1001.2014.3001.5482 目录 指定值 指定端口/引脚列表 简单使用 set_case_analysis命令用于对电路进行特定模式的设定&#xff0c;例如对于一个工作在正常模式下的芯片&#xff0c;…

08 yum和git

什么是软件包 安装软件&#xff0c;一个通常的办法就是下载程序的源代码进行编译。这种太麻烦&#xff0c;于是一些人把常用软件编译好&#xff0c;做成软件包放在服务器上&#xff0c;通过包管理器可以很方便的得到这个软件包安装&#xff0c;就好比手机上的应用商店 yum&am…

美梦从舒适开始,康姿百德床垫为睡眠健康护航

在当今社会&#xff0c;高质量的睡眠已成为人们对生活品质的追求&#xff0c;对床垫的选择也变得越来越讲究。在我们繁忙的生活中&#xff0c;一张优质的床垫不仅是我们舒适休息的保障&#xff0c;更是保持健康生活方式的重要部分。康姿百德床垫&#xff0c;作为市场上的佼佼者…

14-Linux部署Hadoop集群

Linux部署Hadoop集群 简介 1&#xff09;Hadoop是一个由Apache基金会所开发的分布式系统基础架构。 2&#xff09;主要解决&#xff0c;海量数据的存储和海量数据的分析计算问题。 Hadoop HDFS 提供分布式海量数据存储能力 Hadoop YARN 提供分布式集群资源管理能力 Hadoop…

R语言使用dietaryindex包计算NHANES数据多种健康饮食指数 (HEI等)(1)

健康饮食指数 (HEI) 是评估一组食物是否符合美国人膳食指南 (DGA) 的指标。Dietindex包提供用户友好的简化方法&#xff0c;将饮食摄入数据标准化为基于指数的饮食模式&#xff0c;从而能够评估流行病学和临床研究中对这些模式的遵守情况&#xff0c;从而促进精准营养。 该软件…