【项目测试报告】网页版博客系统 + online 好友聊天室

news2024/12/24 20:44:50

网页版博客系统 + online 好友聊天室(项目介绍)

实现一个网页版的博客系统,提供了一个技术文章论坛,同时也提供了用户之间在线交流的平台。

网页博客系统支持以下核心功能:

  • 用户注册、登录、注销功能(登录成功后 session 信息持久化到 Redis 上).
  • 已登录用户可对自己的文章进行发布、修改、查看详情、修改功能,同时也可以进入其他用户的主页,查看其发表文章.
  • 未登录用户可以使用博客列表页,查看所有用户的文章详情.

online 好友聊天室支持以下核心功能:

  • 已登录用户可以进入 online 好友聊天室,通过 WebSocket 实现查看好友(相互关注)列表的实时在线情况.
  • 已登录用户可以在 online 好友聊天室与好友进行聊天,使用 WebSocket 实现消息推送.

额外扩展功能:

1. 文章评论功能.
2. 文章点赞功能.
3. 文章保存草稿功能.
4. 用户找回密码功能.
5. 用户相互关注功能,附带粉丝列表,关注列表.
6. 用户个⼈中心:支持修改密码、修改昵称(非登录名)、修改密保等功能.
7. 使用多线程(使用线程池优化)实现文章定时发布功能.
8. 使用多线程(使用线程池优化)实现用户多次登录,账号冻结的业务.
9. 使用 MultipartFile 实现上传头像,使用 WebMvcConfigurer 配置自定义资源映射实现头像展示.
10. 手动实现密码加盐算法,提高用户信息安全性.
11. 使用 hutool 工具,实现了登录时图片验证码功能,增加了系统的安全性.
12. 使用 HandlerInterceptor 实现了统一登录拦截器.
13. 使用 ResponseBodyAdvice 实现了统一数据格式返回.

核心技术

  • Spring Boot / Spring MVC / MyBatis
  • WebSocket
  • MySQL
  • Redis
  • HTML / CSS / JS / AJAX

项目页面设计

整个 Web 项目分为以下页面

  • 登录页面(任意用户可使用)
  • 注册页面(任意用户可使用)
  • 找回密码页面(任意用户可使用)
  • 博客列表页(任意用户可使用)
  • 博客详情页(任意用户可使用)
  • 个人博客发布页(面向已登录用户)
  • 个人博客修改页(面向已登录用户)
  • 个人博客草稿页(面向已登录用户)
  • 个人资料页(面向已登录用户)
  • 个人 / 他人 博客列表页(面向已登录用户)
  • 好友聊天室(面向已登录用户)

登录页面

在这里插入图片描述

客户端输入用户名、密码、验证码,后端首先检验验证码受否正确,然后通过 Spring + MyBatis 进行数据库的查询操作校验用户的账号和秘密是否正确,若正确则将 Session 信息持久化到 Redis 上并跳转到博客列表页,若错误则用弹窗进行提示。

注册页面

在这里插入图片描述

客户端输入账号和密码,增加用户,通过 MySQL 将用户信息进行持久化保存.

服务器通过 Spring + MyBatis 进行信息的增添.

找回密码页面

在这里插入图片描述

客户端输入找回账号、真实密码、身份证号作为修改密码的前置步骤,前置步骤验证通过后,服务器通过 Spring + MyBatis 进行数据库的修改密码.

博客列表页

在这里插入图片描述

面向任意用户,博客列表支持分页功能,可以在该界面访问所有用发布的博客以及访问量点赞信息,并且可以点击 “查看全文” 查看博客详细信息.

服务器通过 Spring + MyBatis 进行数据库的查询操作,查询所有文章.

博客详情页

在这里插入图片描述

博客详情页可以,查看博客的详细信息以及作者信息,已登录用户可以进行关注,发表评论,删除评论.

服务器通过 Spring + MyBatis 进行数据库的查询操作,查询当前访问的文章和作者信息.

个人博客发布页

在这里插入图片描述

面向当前登录用户. 该页面用户可以填写标题和正文,最后点击发布按钮进行发布博客; 用户也可以选择定时发布博客,让博客在指定时间发布; 用户也可以选择先不发布博客,保存草稿.

服务器通过 MySQL 保存用户发布的博客信息.

个人博客修改页

在这里插入图片描述

面向当前登录用户. 该页面可以进行对之前发布的页面进行编辑操作,修改完后点击发布博客即可修改文章; 用户也可以选择定时发布博客,让博客在指定时间发布; 用户也可以选择先不发布博客,保存草稿.

服务器通过 Spring + MyBatis 对数据库进行相应的修改操作。

个人博客草稿页(面向已登录用户)

在这里插入图片描述

面向当前登录用户. 该页面可以对之前保存的文章草稿进行修改和删除.

服务器针对用户的以上操通过 Spring + MyBatis 进行相应的增删改查操作.

个人资料页

在这里插入图片描述

面向当前登录用户. 可以对当前登录用户进行信息修改.

服务器针对用户的以上操通过 Spring + MyBatis 进行相应的增删改查操作.

个人 / 他人博客列表页

个人:
在这里插入图片描述

他人:
在这里插入图片描述

面向当前登录用户. 该页面会展示用户的个人发布的所有博客,若是自己的主页可以对这些博客进行查看详情,修改,删除等功能,若是他人主页可以查看文章详情,并且可以查看粉丝列表和关注列表.

服务器针对用户的以上操通过 Spring + MyBatis 进行相应的增删改查操作.

好友聊天室

在这里插入图片描述
在这里插入图片描述

面向已登录的用户. 该页面通过消息推送机制,展现在线与离线好友(相互关注),可以与好友进行聊天,消息通过 WebSocket 进行实时推送.

测试计划

功能测试

编写测试用例

在这里插入图片描述

登录页面

a)输入异常的账号、密码、验证码
预期结果:弹窗对应提示
实际结果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

b)输入正常的账号和密码
预期结果:跳转到用户主页
实际结果如下:
在这里插入图片描述

注册页面

a)输入异常的账号、密码、确认密码
预期结果:对应的弹窗提示
实际结果如下:
在这里插入图片描述在这里插入图片描述

b)输入正常的账号、密码、确认密码
预期结果:弹窗提示是否跳转到登录页面

实际结果如下:
在这里插入图片描述

找回密码页面

a)异常填写账号、密保信息、密码
预期结果:对应的弹窗提示
实际结果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
b)输入正确的信息
预期结果:弹窗提示密码找回成功,是否跳转到登录页.
实际结果如下:
在这里插入图片描述

博客列表页

a)点击对应博客弹出对应的博客详情
在这里插入图片描述

b)分页功能测试
预期结果:上一页,下一页,首页,末页均有边界处理
实际结果如下:
在这里插入图片描述
在这里插入图片描述

博客详情页

a)预期结果:点击点赞按钮,标识已点赞,再点一次取消点赞
实际结果如下:
在这里插入图片描述
在这里插入图片描述

b)预期结果:评论后在评论区显示评论气泡
实际结果如下:在这里插入图片描述

c)预期结果:点击删除评论,进行信息删除,若不是自己的评论(作者除外),无法删除
实际结果如下:
在这里插入图片描述

个人博客列表页

a)博客列表显示
在这里插入图片描述

b)粉丝列表显示
在这里插入图片描述
c)关注列表显示
在这里插入图片描述

个人博客草稿页

a)预期结果:点击删除草稿,进行弹窗提示,若确认,则删除草稿。
实际结果如下:
在这里插入图片描述

博客编辑页

a)预期结果:点击发布博客,进行对应弹窗提示,确认后,回到主页即可看到博客 .
实际结果如下:
在这里插入图片描述
在这里插入图片描述
b)预期结果:点击定时发布功能,设置时间为1分钟后发布(若没设置时间,则进行弹窗提示),并在1分钟后在主页观察到博客。
实际结果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

c)预期结果:点击保存草稿后,在草稿列表显示
实际结果如下:
在这里插入图片描述
在这里插入图片描述

个人资料页

a)修改头像后,在界面实时展示
在这里插入图片描述
在这里插入图片描述

好友聊天室

a)预期结果:登录后相互关注的好友可以观察到对方上线消息.
实际结果如下:
在这里插入图片描述
在这里插入图片描述
b)预期结果:双方互发消息,消息气泡在对应一侧,并且消息及时推送
在这里插入图片描述
在这里插入图片描述
c)预期结果:输入框输入消息,点击发送按钮后,清空消息框
实际结果如下:
在这里插入图片描述
在这里插入图片描述

自动化测试

单例驱动,获取屏幕截图

单例驱动: 自动化程序中会很频繁的使用驱动,如果频繁的创建和销毁开销还是比较大的,因此我们可以使用一种懒汉模式的加载方式去加载驱动,这样既能保证驱动不会频繁创建(程序运行过程保持单例),又能减轻程序刚开始启动时的系统开销(只有用到驱动的时候才去加载他),其他类如果需要驱动直接继承该类即可.
屏幕截图: 有的时候我们测试用例执行出错了,我们需要查看当时网页出现的情况,那么就需要使用屏幕截图来排查问题.
具体的,可以使用驱动的 getScreenshotAs 方法去保存屏幕截图,在每一个测试case执行完后进行一次屏幕截图,并将截图保存到一个路径下,文件名以当时时间进行组织(防止保存屏幕截图出现覆盖情况),那么就可以在AutoTestUtils类下加上屏幕截图的方法,方便其他类调用.

import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;

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 AutoTestUtils {

    private static EdgeDriver driver;

    public static EdgeDriver getDriver() {
        EdgeOptions options = new EdgeOptions();
        options.addArguments("--remote-allow-origins=*");
        if(driver == null) {
            driver = new EdgeDriver(options);
            //创建隐式等待,设置最长等待时间为 10s
            driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
        }
        return driver;
    }

    /**
     * 获取屏幕截图,将用例执行结果保存下来
     * @param str
     */
    public void getScreenShot(String str) throws IOException {
        List<String> list = getTime();
        String fileName = "./src/test/java/com/blogWebAutoTest/" + list.get(0) + "/" + str + "_" + list.get(1) + ".png";
        File srcFile = driver.getScreenshotAs(OutputType.FILE);
        //把生成的截图放入指定路径
        FileUtils.copyFile(srcFile, new File(fileName));
    }

    //获取当前时间(记录每个屏幕截图是什么时候拍摄的)
    private List<String> getTime() {
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMdd-HHmmssSS");
        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyyMMdd");
        //保存的文件夹名:当天的年月 +  具体时间
        String fileName = sdf1.format(System.currentTimeMillis());
        //保存的图片名:当天年月
        String dirName = sdf2.format(System.currentTimeMillis());
        List<String> list = new ArrayList<>();
        list.add(dirName);
        list.add(fileName);
        return list;
    }

}

注册页面自动化测试

创建一个类(自定义名为 RegTest)继承 AutoTestUtils,得到驱动,首先测试登录页面是否正常打开,接着找几个典型的 case 使用参数化注解进行测试注册功能即可,最后进行相应弹窗处理,最后进行屏幕截图.

import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.edge.EdgeDriver;

import java.io.IOException;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class RegTest extends AutoTestUtils {

    //获取到驱动
    private static EdgeDriver driver = AutoTestUtils.getDriver();

    /**
     * 打开网页
     */
    @BeforeAll
    public static void openWeb() {
        driver.get("http://43.139.193.116:8080/reg.html");
    }

    /**
     * 验证网页正常打开
     */
    @Test
    @Order(1)
    public void webAppear() throws IOException {
        driver.findElement(By.cssSelector("#username"));
        driver.findElement(By.cssSelector("body > div.login-container > div > div:nth-child(4)"));
        driver.findElement(By.xpath("//*[@id=\"submit\"]"));
        driver.findElement(By.xpath("/html/body/div[2]/div"));
        //屏幕截图
        getScreenShot(getClass().getName());
    }

    /**
     * 异常测试注册功能
     * @param username
     * @param password
     */
    @ParameterizedTest
    @CsvSource({"zhangsan, 123, 123", "aaa, 123, 1233"})
    @Order(2)
    public void regFunTest(String username, String password, String confirmPassword) throws InterruptedException, IOException {
        //拿到元素
        WebElement inputUsername = driver.findElement(By.cssSelector("#username"));
        WebElement inputPassword = driver.findElement(By.cssSelector("#password"));
        WebElement inputconfirmPassword = driver.findElement(By.cssSelector("#password2"));
        WebElement button = driver.findElement(By.cssSelector("#submit"));
        //先清除输入框
        inputUsername.clear();
        inputPassword.clear();
        inputconfirmPassword.clear();
        //输入用例
        inputUsername.sendKeys(username);
        inputPassword.sendKeys(password);
        inputconfirmPassword.sendKeys(confirmPassword);
        //提交
        button.click();
        //处理弹窗
        Thread.sleep(100);
        driver.switchTo().alert().accept();
        //屏幕截图
        getScreenShot(getClass().getName());
    }

}

登录页面自动化测试

创建一个类(自定义名为 LoginTest)继承 AutoTestUtils 类,获取驱动,之后选取典型 case 使用参数化注解对异常、正常登录分别进行测试,进行相应弹窗处理,最后进行屏幕截图.

import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.edge.EdgeDriver;

import java.io.IOException;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class LoginTest extends AutoTestUtils {

    //获取驱动
    private static EdgeDriver driver = AutoTestUtils.getDriver();

    /**
     * 打开网页
     * @BeforeAll 标识在所有测试用例执行之前执行依次。
     */
    @BeforeAll
    private static void openWeb() {
        driver.get("http://43.139.193.116:8080/login.html");
    }

    /**
     * 检测登录页面能否正常打开
     */
    @Test
    @Order(1)
    public void elementsAppear() throws IOException {
        driver.findElement(By.cssSelector("#username"));
        driver.findElement(By.cssSelector("#checkpassword-img"));
        driver.findElement(By.cssSelector("body > div.login-container > div"));
        driver.findElement(By.cssSelector("body > div.login-container > div > div.assist > a:nth-child(2)"));
        //屏幕截图
        getScreenShot(getClass().getName());
    }

    /**
     * 检测登录异常测试
     * @param username
     * @param password
     * @param checkPassword 验证码
     * Ps: 万能验证码为 chenyikang
     */
    @ParameterizedTest
    @CsvSource({"adm* , 1234, chenyikang", "daf, 11, chenyikang", "&*^% , 123, aaa"})
    @Order(2)
    public void loginAbnormalTest(String username, String password, String checkPassword) throws InterruptedException, IOException {
        //清除用户名、密码框、验证码
        driver.findElement(By.cssSelector("#username")).clear();
        driver.findElement(By.cssSelector("#password")).clear();
        driver.findElement(By.cssSelector("#checkpassword")).clear();
        //输入账号、密码、验证码
        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#password")).sendKeys(password);
        driver.findElement(By.cssSelector("#checkpassword")).sendKeys(checkPassword);
        driver.findElement(By.cssSelector("#submit")).click();
        //强制等待,等待弹窗
        Thread.sleep(100);
        //处理弹窗
        Alert alert = driver.switchTo().alert();
        //期望结果
        String expect = "用户名/密码/验证码错误";
        String actual = "用户名/密码/验证码错误";
        //实际结果(若未出现弹窗,说明不符合预期);
        if(alert == null) {
            actual = "当前页面异常";
        }
        alert.accept();
        //屏幕截图
        getScreenShot(getClass().getName());
        Assertions.assertEquals(expect, actual);
    }

    /**
     * 正常登录测试
     * @param username
     * @param password
     * @throws InterruptedException
     */
    @ParameterizedTest
    @CsvSource({"zhangsan, 123"})
    @Order(3)
    public void loginNormalTest(String username, String password) throws InterruptedException, IOException {
        //清除用户名、密码框、验证码
        driver.findElement(By.cssSelector("#username")).clear();
        driver.findElement(By.cssSelector("#password")).clear();
        driver.findElement(By.cssSelector("#checkpassword")).clear();
        //输入账号、密码、验证码
        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#password")).sendKeys(password);
        driver.findElement(By.cssSelector("#checkpassword")).sendKeys("chenyikang");
        driver.findElement(By.cssSelector("#submit")).click();
        //屏幕截图
        getScreenShot(getClass().getName());
        //登录成功需要返回登录界面
        //点击注销
        driver.findElement(By.cssSelector("body > div.nav > a:nth-child(9)")).click();
        //处理弹窗
        //强制等待,等待弹窗
        Thread.sleep(100);
        driver.switchTo().alert().accept();
        //等待处理完成
        Thread.sleep(100);
    }


}

找回密码页面自动化测试

创建一个类(自定义名为 fundPasswordTest )继承 AutoTestUtils 类,获取驱动,首先验证页面是否能正常显示,接着找几个典型的 case 使用参数化注解对其进行测试,进行相应弹窗处理,最后进行屏幕截图.

import com.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.edge.EdgeDriver;

import java.io.IOException;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class fundPasswordTest extends AutoTestUtils {

    //获取驱动
    private static EdgeDriver driver = AutoTestUtils.getDriver();

    /**
     * 打开页面
     */
    @BeforeAll
    public static void openWeb() {
        driver.get("http://43.139.193.116:8080/fund_password.html");
    }

    /**
     * 验证页面正常打开
     */
    @Test
    @Order(1)
    public void WebAppear() throws IOException {
        driver.findElement(By.cssSelector("#ps"));
        driver.findElement(By.cssSelector("#realname"));
        driver.findElement(By.cssSelector("#submit"));
        driver.findElement(By.xpath("/html/body/div[2]/div"));
        //屏幕截图
        getScreenShot(getClass().getName());
    }

    /**
     * 异常找回密码测试
     * @param username
     * @param realname
     * @param id
     * @param newPassword
     * @param password
     */
    @ParameterizedTest
    @CsvSource({"wu, zhangsan, 610, 123, 123", "zhangsan, lisi, 610, 123, 123",
    "zhangsan, zhangsan, 611, 123, 123", "zhangsan, zhangsan, 610, 123, 12234"})
    @Order(2)
    public void fundPasswordFunExceptionTest(String username, String realname,
                                    String id, String newPassword, String password) throws InterruptedException, IOException {
        //先清除输入框
        driver.findElement(By.cssSelector("#username")).clear();
        driver.findElement(By.cssSelector("#realname")).clear();
        driver.findElement(By.cssSelector("#idcard")).clear();
        driver.findElement(By.cssSelector("#password1")).clear();
        driver.findElement(By.cssSelector("#password2")).clear();
        //输入
        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#realname")).sendKeys(realname);
        driver.findElement(By.cssSelector("#idcard")).sendKeys(id);
        driver.findElement(By.cssSelector("#password1")).sendKeys(newPassword);
        driver.findElement(By.cssSelector("#password2")).sendKeys(password);
        //提交
        driver.findElement(By.cssSelector("#submit")).click();
        //处理弹窗
        Thread.sleep(100);
        driver.switchTo().alert().accept();
        //屏幕截图
        getScreenShot(getClass().getName());
        //处理完弹窗若还能发现页面元素,说明异常处理成功
        String expect = "找回密码";
        String actual = driver.findElement(By.cssSelector("body > div.login-container > div > h3")).getText();
        Assertions.assertEquals(expect, actual);
    }

    /**
     * 正常找回密码
     * @param username
     * @param realname
     * @param id
     * @param newPassword
     * @param password
     */
    @ParameterizedTest
    @CsvSource("zhangsan, zhangsan, 610, 123, 123")
    @Order(3)
    public void fundPasswordFunNormalTest(String username, String realname,
                                    String id, String newPassword, String password) throws InterruptedException, IOException {
        //先清除输入框
        driver.findElement(By.cssSelector("#username")).clear();
        driver.findElement(By.cssSelector("#realname")).clear();
        driver.findElement(By.cssSelector("#idcard")).clear();
        driver.findElement(By.cssSelector("#password1")).clear();
        driver.findElement(By.cssSelector("#password2")).clear();
        //输入
        driver.findElement(By.cssSelector("#username")).sendKeys(username);
        driver.findElement(By.cssSelector("#realname")).sendKeys(realname);
        driver.findElement(By.cssSelector("#idcard")).sendKeys(id);
        driver.findElement(By.cssSelector("#password1")).sendKeys(newPassword);
        driver.findElement(By.cssSelector("#password2")).sendKeys(password);
        //提交
        driver.findElement(By.cssSelector("#submit")).click();
        //处理弹窗
        Thread.sleep(100);
        driver.switchTo().alert().accept();
        //屏幕截图
        getScreenShot(getClass().getName());
        Thread.sleep(100);
        String expect = "登陆";
        String actual = driver.findElement(By.cssSelector("body > div.login-container > div > h3")).getText();
        Assertions.assertEquals(expect, actual);
    }

}

个人资料页自动化测试

创建一个类(自定义名为 PersonalInfoTest)继承 AutoTestUtils,得到驱动,对基础信息、密保信息、修改密码使用典型 case 进行测试,进行相应弹窗处理,最后进行屏幕截图.

import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.edge.EdgeDriver;

import java.io.IOException;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class PersonalInfoTest extends AutoTestUtils {

    private static EdgeDriver driver = AutoTestUtils.getDriver();

    @BeforeAll
    public static void openWeb() throws InterruptedException {
//        //方式一:单元测试
//        driver.get("http://43.139.193.116:8080/login.html");
//        driver.findElement(By.cssSelector("#username")).clear();
//        driver.findElement(By.cssSelector("#password")).clear();
//        driver.findElement(By.cssSelector("#checkpassword")).clear();
//        //输入账号、密码、验证码
//        driver.findElement(By.cssSelector("#username")).sendKeys(AppValues.USERNAME);
//        driver.findElement(By.cssSelector("#password")).sendKeys(AppValues.PASSWORD);
//        driver.findElement(By.cssSelector("#checkpassword")).sendKeys(AppValues.CODE);
//        driver.findElement(By.cssSelector("#submit")).click();
//        Thread.sleep(100);
//        driver.findElement(By.cssSelector("body > div.nav > a:nth-child(5)")).click();

        //整体测试
        //此时已经有 session 会话了,直接进入即可
        driver.get("http://43.139.193.116:8080/myinfo.html");
    }

    /**
     * 验证页面正常打开
     */
    @Test
    @Order(1)
    public void webAppear() throws IOException {
        driver.findElement(By.cssSelector("#photo"));
        driver.findElement(By.cssSelector("#gitee"));
        driver.findElement(By.cssSelector("#submit"));
        driver.findElement(By.cssSelector("body > div.login-container > div > div:nth-child(12)"));
        //屏幕截图
        getScreenShot(getClass().getName());
    }

    /**
     * 修改基础信息测试
     * @param nickname
     */
    @ParameterizedTest
    @ValueSource(strings = {"123", "xiaozhang", "&*^%$", "小张"})
    @Order(2)
    public void basicInfoTest(String nickname) throws InterruptedException, IOException {
        //清除输入框
        driver.findElement(By.cssSelector("#nickname")).clear();
        //输入信息
        driver.findElement(By.cssSelector("#nickname")).sendKeys(nickname);
        //提交
        driver.findElement(By.xpath("/html/body/div[2]/div/div[3]/button")).click();
        //处理弹窗
        Thread.sleep(100);
        driver.switchTo().alert().accept();
        //屏幕截图
        getScreenShot(getClass().getName());
    }

    /**
     * 修改密保信息测试
     */
    @ParameterizedTest
    @CsvSource({"132, &*^, 610", "$$#@^7., &$(#)+, 610", "123, zhangsan, 610"})
    @Order(3)
    public void SecurityInfoTest(String username, String realname, String id) throws InterruptedException, IOException {
        //清除输入框
        driver.findElement(By.cssSelector("#password")).clear();
        driver.findElement(By.cssSelector("#realname")).clear();
        driver.findElement(By.cssSelector("#idcard")).clear();
        //输入信息
        driver.findElement(By.cssSelector("#password")).sendKeys(username);
        driver.findElement(By.cssSelector("#realname")).sendKeys(realname);
        driver.findElement(By.cssSelector("#idcard")).sendKeys(id);
        //提交
        driver.findElement(By.xpath("/html/body/div[2]/div/div[6]/button")).click();
        //处理弹窗
        Thread.sleep(200);
        driver.switchTo().alert().accept();
        //屏幕截图
        getScreenShot(getClass().getName());
        //提交
        driver.findElement(By.xpath("/html/body/div[2]/div/div[7]/button")).click();
        //处理弹窗
        Thread.sleep(200);
        driver.switchTo().alert().accept();
    }

    /**
     * 异常密码测试1
     * @param oldPassword
     * @param newPassword
     * @param confirmPassword
     */
    @ParameterizedTest
    @CsvSource({"134358, 123, 123", "*#&2*, 123, 123"})
    @Order(4)
    public void updatePasswordTest1(String oldPassword, String newPassword, String confirmPassword) throws InterruptedException, IOException {
        //清除输入框
        driver.findElement(By.cssSelector("#password1")).clear();
        driver.findElement(By.cssSelector("#password2")).clear();
        driver.findElement(By.cssSelector("#password3")).clear();
        //输入
        driver.findElement(By.cssSelector("#password1")).sendKeys(oldPassword);
        driver.findElement(By.cssSelector("#password2")).sendKeys(newPassword);
        driver.findElement(By.cssSelector("#password3")).sendKeys(confirmPassword);
        //提交
        driver.findElement(By.xpath("/html/body/div[2]/div/div[12]/button")).click();
        //处理弹窗
        Thread.sleep(100);
        Alert alert = driver.switchTo().alert();
        String e1 = "旧密码错误,请重新输入!";
        String a1 = alert.getText();
        alert.accept();
        //屏幕截图
        getScreenShot(getClass().getName());
        //断言
        Assertions.assertEquals(e1, a1);
    }


    /**
     * 异常密码测试2
     * @param oldPassword
     * @param newPassword
     * @param confirmPassword
     */
    @ParameterizedTest
    @CsvSource({"123, adfa, 1551sf8", "123, 489, 123"})
    @Order(5)
    public void updatePasswordTest(String oldPassword, String newPassword, String confirmPassword) throws InterruptedException, IOException {
        //清除输入框
        driver.findElement(By.cssSelector("#password1")).clear();
        driver.findElement(By.cssSelector("#password2")).clear();
        driver.findElement(By.cssSelector("#password3")).clear();
        //输入
        driver.findElement(By.cssSelector("#password1")).sendKeys(oldPassword);
        driver.findElement(By.cssSelector("#password2")).sendKeys(newPassword);
        driver.findElement(By.cssSelector("#password3")).sendKeys(confirmPassword);
        //提交
        driver.findElement(By.xpath("/html/body/div[2]/div/div[12]/button")).click();
        //处理弹窗
        Thread.sleep(100);
        Alert alert = driver.switchTo().alert();
        String e1 = "新密码与确认密码不一致,请重试!";
        String a1 = alert.getText();
        alert.accept();
        //屏幕截图
        getScreenShot(getClass().getName());
        //断言
        Assertions.assertEquals(e1, a1);
    }


    /**
     * 正常修改密码测试
     * @param oldPassword
     * @param newPassword
     * @param confirmPassword
     */
    @ParameterizedTest
    @CsvSource({"123, 1234, 1234", "1234, 123, 123"})
    @Order(5)
    public void updatePasswordNorTest(String oldPassword, String newPassword, String confirmPassword) throws InterruptedException, IOException {
        //清除输入框
        driver.findElement(By.cssSelector("#password1")).clear();
        driver.findElement(By.cssSelector("#password2")).clear();
        driver.findElement(By.cssSelector("#password3")).clear();
        //输入
        driver.findElement(By.cssSelector("#password1")).sendKeys(oldPassword);
        driver.findElement(By.cssSelector("#password2")).sendKeys(newPassword);
        driver.findElement(By.cssSelector("#password3")).sendKeys(confirmPassword);
        //提交
        driver.findElement(By.xpath("/html/body/div[2]/div/div[12]/button")).click();
        //处理弹窗
        Thread.sleep(100);
        Alert alert = driver.switchTo().alert();
        String e1 = "密码修改成功!";
        String a1 = alert.getText();
        alert.accept();
        //屏幕截图
        getScreenShot(getClass().getName());
        //断言
        Assertions.assertEquals(e1, a1);
    }

}

个人主页自动化测试

创建一个类名为 PersonalHomeTest 继承 AutoTestUtils ,得到驱动,分别对博客列表信息、粉丝列表、关注列表进行检验,最后对关注功能进行检验,进行相应弹窗处理,最后进行屏幕截图.

import com.common.AppValues;
import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.edge.EdgeDriver;

import java.io.IOException;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class PersonalHomeTest extends AutoTestUtils {

    //获取驱动
    private static EdgeDriver driver = AutoTestUtils.getDriver();

    /**
     * 进入到个人主页中
     * Ps: 这里不能直接通过网址进入,需要先登录
     */
    @BeforeAll
    private static void intoWeb() {
//        //先检验拦截
//        driver.get("http://43.139.193.116:8080/myblog_list.html?id=2");
//        //检验拦截后是否跳转到登录页面
//        String expect = "登陆";
//        String actual = driver.findElement(By.cssSelector("body > div.login-container > div > h3")).getText();
//        Assertions.assertEquals(expect, actual);
        //登录
        driver.get("http://43.139.193.116:8080/login.html");
        driver.findElement(By.cssSelector("#username")).clear();
        driver.findElement(By.cssSelector("#password")).clear();
        driver.findElement(By.cssSelector("#checkpassword")).clear();
        //输入账号、密码、验证码
        driver.findElement(By.cssSelector("#username")).sendKeys(AppValues.USERNAME);
        driver.findElement(By.cssSelector("#password")).sendKeys(AppValues.PASSWORD);
        driver.findElement(By.cssSelector("#checkpassword")).sendKeys(AppValues.CODE);
        driver.findElement(By.cssSelector("#submit")).click();
    }

    /**
     * 检验网页是否正常打开
     */
    @Test
    @Order(1)
    public void webAppear() throws IOException {
        driver.findElement(By.cssSelector("#photo"));
        driver.findElement(By.cssSelector("body > div.container > div.container-left > div > div:nth-child(5) > div"));
        driver.findElement(By.cssSelector("body > div.container > div.container-right > div.selectInfo > span:nth-child(3)"));
        driver.findElement(By.cssSelector("#listInfo"));
        //屏幕截图
        getScreenShot(getClass().getName());
    }

    /**
     * 辅助栏检验
     * Ps:顺带检验关注功能
     */
    @Test
    @Order(2)
    public void assistColumnTest() throws InterruptedException, IOException {
        //1.进入关注列表
        driver.findElement(By.cssSelector("body > div.container > div.container-right > div.selectInfo > span:nth-child(3)")).click();
        //找到关注元素
        WebElement followButton = driver.findElement(By.cssSelector("#follow-msg"));
        //检验关注和取关
        String e1 = "已关注";
        String a1 = followButton.getText();
        Assertions.assertEquals(e1, a1);
        //进行取关
        followButton.click();
        //等待服务器响应一会
        Thread.sleep(100);
        //屏幕截图
        getScreenShot(getClass().getName());
        //2.进入粉丝列表
        driver.findElement(By.cssSelector("body > div.container > div.container-right > div.selectInfo > span:nth-child(2)")).click();
        //进行关注
        driver.findElement(By.cssSelector("#follow-msg")).click();
        //检验是否关注成功
        String e2 = "已关注";
        Thread.sleep(500);
        String a2 = driver.findElement(By.cssSelector("#follow-msg")).getText();
        //屏幕截图
        getScreenShot(getClass().getName());
        Assertions.assertEquals(e2, a2);

    }
    
}

博客草稿页自动化测试

创建一个类名为 DraftListTest 继承 AutoTestUtils 类,获取驱动,在对草稿的编辑,删除功能分别进行测试,进行相应弹窗处理,最后进行屏幕截图.

import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.edge.EdgeDriver;

import java.io.IOException;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class DraftListTest extends AutoTestUtils {

    //获取驱动
    private static EdgeDriver driver = AutoTestUtils.getDriver();

    /**
     * 进入到个人草稿页中
     */
    @BeforeAll
    public static void intoWeb() {
//        //方式一:单元测试
//        driver.get("http://43.139.193.116:8080/login.html");
//        driver.findElement(By.cssSelector("#username")).clear();
//        driver.findElement(By.cssSelector("#password")).clear();
//        driver.findElement(By.cssSelector("#checkpassword")).clear();
//        //输入账号、密码、验证码
//        driver.findElement(By.cssSelector("#username")).sendKeys(AppValues.USERNAME);
//        driver.findElement(By.cssSelector("#password")).sendKeys(AppValues.PASSWORD);
//        driver.findElement(By.cssSelector("#checkpassword")).sendKeys(AppValues.CODE);
//        driver.findElement(By.cssSelector("#submit")).click();
//        driver.findElement(By.cssSelector("body > div.nav > a:nth-child(7)")).click();

        //整体测试
        //此时已经有 session 会话了,直接进入即可
        driver.get("http://43.139.193.116:8080/draft_list.html?id=2");
    }

    /**
     * 检查当前页面元素出现
     */
    @Test
    @Order(1)
    public void webAppear() throws IOException {
        driver.findElement(By.cssSelector("#photo"));
        driver.findElement(By.cssSelector("body > div.container > div.container-left > div > div:nth-child(4) > div"));
        driver.findElement(By.cssSelector("#listInfo"));
        driver.findElement(By.cssSelector("#listInfo > div > a > div.title"));
        //屏幕截图
        getScreenShot(getClass().getName());
    }

    /**
     * 检验草稿功能
     */
    @Test
    @Order(2)
    public void draftTest() throws InterruptedException, IOException {
        //1.检验草稿是否能打开
        driver.findElement(By.cssSelector("#listInfo > div > a > div.title")).click();
        String e1 = "这是一篇草稿";
        String a1 = driver.findElement(By.cssSelector("#title")).getText();
        Assertions.assertEquals(e1, a1);
        //后退
        driver.navigate().back();
        //2.检验修改草稿按钮
        Assertions.assertNotNull(driver.findElement(By.cssSelector("#edit")));
        //3.检验删除草稿功能
        driver.findElement(By.cssSelector("#del")).click();
        Alert alert = driver.switchTo().alert();
        Thread.sleep(100);
        Assertions.assertEquals("确定删除该文章?", alert.getText());
        //处理弹窗(取消)
        alert.dismiss();
        //屏幕截图
        getScreenShot(getClass().getName());
    }
    
}

文章发布和文章保存自动化测试

创建一个类名为 BlogAndDraftPostTest 继承 AutoTestUtils 类,得到驱动,并对发布按钮和保存按钮进行自动化测试,进行相应弹窗处理,最后进行屏幕截图.

import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.openqa.selenium.By;
import org.openqa.selenium.edge.EdgeDriver;

import java.io.IOException;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BlogAndDraftPostTest extends AutoTestUtils {

    //获取驱动
    private static EdgeDriver driver = AutoTestUtils.getDriver();

    /**
     * 打开当前页面
     */
    @BeforeAll
    public static void openWeb() {

//        //方式一:单元测试
//        driver.get("http://43.139.193.116:8080/login.html");
//        driver.findElement(By.cssSelector("#username")).clear();
//        driver.findElement(By.cssSelector("#password")).clear();
//        driver.findElement(By.cssSelector("#checkpassword")).clear();
//        //输入账号、密码、验证码
//        driver.findElement(By.cssSelector("#username")).sendKeys(AppValues.USERNAME);
//        driver.findElement(By.cssSelector("#password")).sendKeys(AppValues.PASSWORD);
//        driver.findElement(By.cssSelector("#checkpassword")).sendKeys(AppValues.CODE);
//        driver.findElement(By.cssSelector("#submit")).click();
//        driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)")).click();

        //整体测试
        //此时已经有 session 会话了,直接进入即可
        driver.get("http://43.139.193.116:8080/blog_add.html");
    }

    /**
     * 检验当前页面
     */
    @Test
    @Order(1)
    public void webAppear() throws IOException {
        driver.findElement(By.cssSelector("#title"));
        driver.findElement(By.cssSelector("#pubdate"));
        driver.findElement(By.cssSelector("#editorDiv > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll"));
        driver.findElement(By.cssSelector("body > div.blog-edit-container > div.title > button:nth-child(4)"));
        //屏幕截图
        getScreenShot(getClass().getName());
    }

    /**
     * 测试博客发布功能
     */
    @Test
    @Order(2)
    public void postFun() throws InterruptedException, IOException {
        //输入标题和正文
        driver.findElement(By.cssSelector("#title")).sendKeys("test");
        driver.findElement(By.cssSelector("#editorDiv > div.editormd-toolbar > div > ul > li:nth-child(7) > a")).click();
        //点击发布博客
        driver.findElement(By.cssSelector("body > div.blog-edit-container > div.title > button:nth-child(4)")).click();
        Thread.sleep(100);
        driver.switchTo().alert().accept();
        Thread.sleep(100);
        driver.switchTo().alert().dismiss();
        //检查是否成功发布
        //屏幕截图
        getScreenShot(getClass().getName());
        String e1 = "test";
        String a1 = driver.findElement(By.cssSelector("#listInfo > div:nth-child(1) > a > div.title")).getText();
        Assertions.assertEquals(e1, a1);
        //删除博客
        driver.findElement(By.xpath("/html/body/div[2]/div[2]/div[2]/div[1]/div/a[3]")).click();
        Thread.sleep(100);
        driver.switchTo().alert().accept();
        //屏幕截图
        getScreenShot(getClass().getName());
        Thread.sleep(200);
        driver.switchTo().alert().accept();
    }

    /**
     * 检验保存草稿功能
     */
    @Test
    @Order(3)
    public void saveBlog() throws InterruptedException, IOException {
        //进入博客发布页面
        driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)")).click();
        //输入标题和正文
        driver.findElement(By.cssSelector("#title")).sendKeys("test");
        driver.findElement(By.cssSelector("#editorDiv > div.editormd-toolbar > div > ul > li:nth-child(7) > a")).click();
        //点击保存草稿
        driver.findElement(By.cssSelector("body > div.blog-edit-container > div.title > button:nth-child(5)")).click();
        //处理弹窗
        Thread.sleep(100);
        driver.switchTo().alert().accept();
        //屏幕截图
        getScreenShot(getClass().getName());
        //进入草稿页面
        driver.findElement(By.cssSelector("body > div.nav > a:nth-child(7)")).click();
        //检验是否保存成功
        String e1 = "test";
        String a1 = driver.findElement(By.cssSelector("#listInfo > div:nth-child(1) > a > div.title")).getText();
        Assertions.assertEquals(e1, a1);
        //删除草稿
        driver.findElement(By.xpath("/html/body/div[2]/div[2]/div[1]/div/a[3]")).click();
        //处理弹窗
        Thread.sleep(100);
        driver.switchTo().alert().accept();
        //屏幕截图
        getScreenShot(getClass().getName());
        Thread.sleep(100);
        driver.switchTo().alert().accept();
    }

}

online 在线聊天室

创建一个类名为 OnlineTalkTest 继承 AutoTestUtils ,得到驱动,对在线显示以及聊天功能进行测试,最后进行相应弹窗处理,最后进行屏幕截图.

import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.edge.EdgeDriver;

import java.io.IOException;

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class OnlineTalkTest extends AutoTestUtils {

    private static EdgeDriver driver = AutoTestUtils.getDriver();

    @BeforeAll
    public static void openWeb() throws InterruptedException {
//        //方式一:单元测试
//        driver.get("http://43.139.193.116:8080/login.html");
//        driver.findElement(By.cssSelector("#username")).clear();
//        driver.findElement(By.cssSelector("#password")).clear();
//        driver.findElement(By.cssSelector("#checkpassword")).clear();
//        //输入账号、密码、验证码
//        driver.findElement(By.cssSelector("#username")).sendKeys(AppValues.USERNAME);
//        driver.findElement(By.cssSelector("#password")).sendKeys(AppValues.PASSWORD);
//        driver.findElement(By.cssSelector("#checkpassword")).sendKeys(AppValues.CODE);
//        driver.findElement(By.cssSelector("#submit")).click();
//        Thread.sleep(100);
//        driver.findElement(By.cssSelector("body > div.nav > a:nth-child(8)")).click();

        //整体测试
        //此时已经有 session 会话了,直接进入即可
        driver.get("http://43.139.193.116:8080/private_letter.html");
    }

    /**
     * 测试页面正常打开
     */
    @Test
    @Order(1)
    public void webAppear() throws IOException {
        driver.findElement(By.cssSelector("body > div.container > div.container-left > div.mycard > img"));
        driver.findElement(By.cssSelector("body > div.container > div.container-left > div.mycard > span"));
        driver.findElement(By.cssSelector("body > div.container > div.container-left"));
        driver.findElement(By.cssSelector("body > div.container > div.container-right > div"));
        //屏幕截图
        getScreenShot(getClass().getName());
    }

    /**
     * 测试聊天功能
     */
    @ParameterizedTest
    @ValueSource(strings = {"你好呀~", "#*&*@%)"})
    @Order(2)
    public void TalkTest(String msg) throws InterruptedException, IOException {
        Thread.sleep(100);
        //点击头像开始聊天
        driver.findElement(By.xpath("/html/body/div[2]/div[1]/div[2]/div")).click();
        //选择输入框开始聊天
        driver.findElement(By.cssSelector("#messageText")).sendKeys(msg);
        //提交
        driver.findElement(By.cssSelector("#sendButton")).click();
        //处理弹窗
        Thread.sleep(100);
        Alert alert = driver.switchTo().alert();
        String e1 = "对方不在线!";
        String a1 = alert.getText();
        Thread.sleep(100);
        alert.accept();
        //屏幕截图
        getScreenShot(getClass().getName());
        //断言
        Assertions.assertEquals(e1, a1);
    }

}

退出驱动

在所有自动化测试用例执行完后,需要进行退出浏览器,那么我们可以创建一个退出驱动类,放在测试套件类的最后一个测试类。

import com.common.AutoTestUtils;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.edge.EdgeDriver;

/**
 * 退出驱动类
 */
public class DriverQuitTest extends AutoTestUtils {

    private static EdgeDriver driver = AutoTestUtils.getDriver();

    /**
     * 退出驱动
     */
    @Test
    public void quitWeb() {
        driver.quit();
    }

}

测试套件

创建一个类(自定义名为RunSuite),通过 @Suite 注解标识该类为测试套件类(不是测试类),然后使用 @SelectClasses 注解来声明我们要指定的类(通过这个类来运行测试用例),这样优点如下:

1.相比于一个个函数调用来对测试用例进行测试就大大减少了开销和时间;

2.同时指定了类的测试顺序,即在注解@SelectClasses参数中的类测试顺序为从左向右;

import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;

@Suite
@SelectClasses({LoginTest.class, RegTest.class, fundPasswordTest.class,
        PersonalHomeTest.class, DraftListTest.class, BlogAndDraftPostTest.class,
        PersonalInfoTest.class, OnlineTalkTest.class, DriverQuitTest.class})
public class RunSuite {}

自动化测试结果和亮点

在这里插入图片描述
在这里插入图片描述

  1. 使用 junit5 中提供的注解,避免生成过多的对象,造成资源和时间上的浪费,提高了自动化执行的效率。
  2. 只创建一次驱动,避免每个用例重复创建驱动造成时间和资源的浪费。
  3. 使用参数化,保持用例的整洁,提高代码的可读性。
  4. 使用隐式等待,提高了自动化运行效率,提高了自动化的稳定性。
  5. 使用屏幕截图,方便问题的追溯以及解决。

性能测试

使用工具说明

在这里插入图片描述

我们使用以上三个工具针对我们的项目进行性能测试。

a)Virtual User Generator(简称VUG): 用来生成性能测试脚本。

b)Controller: 创建测试场景,运行和监控场景。

c)Analysis: 生成测试报告,分析性能测试结果

UI新能测试步骤

a)访问博客登录页;

b)执行登录;

c)进入首页.

编写性能测试脚本

1、创建项目

由于我们测试的博客系统是一个Web项目,因此需要创建一个Web性能测试脚本,如下:
在这里插入图片描述

2.运行设置

在这里插入图片描述

代码编写(我们主要在Action里编写代码~)

主要的操作步骤为:1.打开网页,2.输入账号密码并登录;

在此期间,为了更好的让我们进行性能测试的数据收集,我们可以使用

  • 事务: 衡量性能的重要指标,通过观察每秒事务通过数来衡量性能;
  • 集合点: 让所有的虚拟用户执行到集合点时断在集合,满足条件后一起执行下一个步骤(保证每一个虚拟用户同时执行下一步);
  • 检查点: 可以用来检测当前页面的元素是否存在以及存在个数(检查点一般放在请求之前);
  • 参数化: 通过提供的数据源可以实现多个参数逐个执行;
Action()
{
	//开启事务1
	lr_start_transaction("index_trans");
	//1.访问登录页面
	web_url("web_url",
		"URL=http://43.139.193.116:8080/blog_system/login.html",
		"TargetFrame=",
		"Resource=0",
		"Referer=",
		LAST);
 
	//登录的集合点
	lr_rendezvous("login_rendezvous");
 
	//开始事务2
	lr_start_transaction("login_trans");
 
	//2.输入登录账号和密码
	web_submit_form("web_submit_form",
		ITEMDATA,
		"Name=username", "Value={zhangsan}", ENDITEM,
		"Name=password", "Value=123", ENDITEM,
		LAST);
 
	//结束事务2
	lr_end_transaction("login_trans", LR_AUTO);
	
	//结束事务1
	lr_end_transaction("index_trans", LR_AUTO);
	return 0;
}


执行结果和分析:
在这里插入图片描述

创建测试场景(Controller)

a)针对我们已经编写好的脚本打开controller工具,创建测试脚本,如下:
在这里插入图片描述

b)设置执行策略如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这个过程就像是舞台表演一样,如下:
在这里插入图片描述

c)场景运行结果
在这里插入图片描述

生成测试报告(Analysis)

在 controller 创建的场景中直接生成测试报告,如下:
在这里插入图片描述

1.虚拟用户运行图:
在这里插入图片描述

作用:通过显示的虚拟用户数量可以判断出哪个时间段服务器负载最大(上图00:40 ~ 01:40负载最大)。

2.点击数图(每秒点击率)
在这里插入图片描述

作用:通过点击率可以判断出某时间段内服务器的负载。

3.吞吐量图
在这里插入图片描述

问题一:为什么吞吐量图和点击数图相似,但是吞吐量图要滞后一点?

  • 因为吞吐量表示的是响应返回的资源数量,肯定是现有请求再有返回。

问题二:如果请求变多但是吞吐量没变化,原因是什么?

  • 服务器响应太慢,来不及反应;
  • 压力没有到服务器;
  • 服务器设计一定的阈值(到达阈值以后,虽然也收到了请求,但是服务器不会做任何处理),保证了服务器不会因为并发量过大而出现宕机的情况;

4.事务图
在这里插入图片描述

5.平均事务响应时间图
在这里插入图片描述

作用: 可以观察到,虚拟用户在性能测试中,每秒在服务器上命中的次数,可以根据命中的次数评估虚拟用户生成的负载量。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JAVAWeb11-服务器渲染技术 -JSP-02-EL表达式(会使用)

1. EL 表达式介绍 EL 表达式全称&#xff1a;Expression Language&#xff0c;是表达式语言EL 表达式主要是代替 jsp 页面的表达式脚本<%request.getAttribute("xx")%>EL 表达式输出数据时&#xff0c;比 jsp 的表达式脚本简洁EL 表达式基本语法&#xff1a; …

HAL库版FreeRTOS(下)

目录 FreeRTOS 事件标志组FreeRTOS 事件标志组简介FreeRTOS 事件标志组相关API 函数FreeRTOS 事件标志组实验功能设计软件设计下载验证 FreeRTOS 任务通知FreeRTOS 任务通知简介任务通知的优势任务通知的缺点 FreeRTOS 任务通知相关API 函数 FreeRTOS 事件标志组 事件标志组与…

【Leetcode】241. 为运算表达式设计优先级

241. 为运算表达式设计优先级&#xff08;中等&#xff09; 解法一&#xff1a;分治法 对于这道题&#xff0c;加括号其实就是决定运算次序&#xff0c;所以我们可以把加括号转化为&#xff0c;「对于每个运算符号&#xff0c;先执行处理两侧的数学表达式&#xff0c;再处理此…

提高APP安全性的必备加固手段——深度解析代码混淆技术

APP 加固方式 Android APP 加固是优化 APK 安全性的一种方法&#xff0c;常见的加固方式有混淆代码、加壳、数据加密、动态加载等。下面介绍一下 Android APP 加固的具体实现方式。 混淆代码&#xff1a; 使用 ProGuard 工具可以对代码进行混淆&#xff0c;使得反编译出来的代…

makefile语法解析

gcc语法简介 gcc (-c) hello.c world.c -o main-c表示只编译不链接 -o目标文件 此外&#xff0c;还有-I&#xff08;大写的I&#xff09;表示给gcc添加自定义的头文件的路径 -L表示给gcc添加额外的搜索库的路径 -g选项的意义是“生成调试信息&#xff0c;该程序可以被调试器调…

【Java入门合集】第五章抽象类和接口(二)

博主&#xff1a;命运之光 专栏&#xff1a;JAVA入门 学习目标 1.了解什么是抽象类&#xff0c;什么是接口&#xff1b; 2.掌握抽象类和接口的定义方法&#xff1b; 3.理解接口和抽象类的使用场景&#xff1b; 4.掌握多态的含义和用法&#xff1b; 5.掌握内部类的定义方法和使用…

【Hive实战】Hive元数据存储库数据增多的分析

Hive元数据存储库数据增多的分析 2023年5月8日 文章目录 Hive元数据存储库数据增多的分析问题新增Hive相关的DDL操作创建Hive库库授权到用户 创建Hive表 内部表非分区表表授权到用户一级分区表二级分区表分桶表分桶排序表 查询指令核心表分析表关系图表以库表为主以hive表为主以…

人人都可用的ChatGPT,Edge浏览器-免费ChatGPT保姆级教程!非常详细!

人工智能大浪潮已经来临&#xff0c;对于ChatGPT&#xff0c;我觉得任何一个玩互联网的人&#xff0c;都应该重视起来&#xff0c;用起来。但是国内使用需要解决科学上网、注册、收费等繁琐问题。 所以&#xff0c;今天这篇文章就来推荐一个插件&#xff0c;无需任何繁琐操作&…

第一次省赛团队训练 - BAPC 2022 Pre(DAPC 2022)

B (2). Bubble-bubble Sort [ 提交记录 ] [ 问题 3462 ] 时间限制: 2000MS 空间限制: 256MB 结果评判: 文本对比 正确/提交: 7 (5) / 15 官方标签: 用户标记: 题目描述 Bubbles! As a fanatical supporter of the Bubbles Are Perfect Creatures movement, you have ac…

第8章 未执行缓存的强制清理操作导致显示异常解决方案

1 异常产生原因&#xff1a; 由于未为Role实体定义相就的缓存强制销毁器类&#xff1a;Services.Customers.Caching.RoleCacheEventConsumer,从而导致Services.Events.EventPublisher.PublishAsync<TEvent>(TEvent event)中的 consumers实例为0,如下图所示&#xff1a; 2…

深入理解移动端布局:Viewport与设备像素比

在移动端开发中&#xff0c;了解和掌握不同设备的布局特点是非常重要的。本文将介绍两个关键概念&#xff1a;Viewport 和设备像素比&#xff08;DPR&#xff09;&#xff0c;帮助你更好地理解移动端布局。 一、什么是 Viewport&#xff1f; Viewport 是用户在浏览器中可见的网…

JS知识点(包括原型,原型对象,数据类型,数据类型的检测)

目录 1、JavaScript有哪些数据类型&#xff0c;它们的区别&#xff1f; 2、基本数据类型和引用数据类型地区别&#xff1a; 3、数据类型检测的方式有哪些: 4、判断数组的方式有那些&#xff1f; 5、null和undefined区别&#xff1a; 6、为什么typeOf null得到object而不是n…

22个提升生产力的工具推荐,稳了

子曰&#xff1a;工欲善其事&#xff0c;必先利其器。 本文给大家推荐22个提高生产力的工具&#xff0c;总有一款符合你的需求。&#x1f604;&#x1f604;&#x1f604; 提高生产效率工具推荐 滴答清单/Todoist文件检索利器&#xff1a;Everything文件管理软件-Allen Explor…

基于SpringBoot的大学生租房系统

背景 大学生租房系统设计的目的是建立一个高效的平台&#xff0c;采用简洁高效的Java语言与Mysql数据库等技术&#xff0c;设计和开发了本大学生租房系统设计。该系统主要实现了用户和房主通过系统注册用户&#xff0c;登录系统后能够编辑自己的个人信息、查看首页&#xff0c…

【电子学会】2023年03月图形化三级 -- 猫猫的儿童节

猫猫的儿童节 儿童节到了&#xff0c;给小猫绘制一个七彩的气球。 1. 准备工作 &#xff08;1&#xff09;保留小猫角色&#xff1b; &#xff08;2&#xff09;选择“Button2”角色&#xff0c;添加文字“开始”&#xff1b; &#xff08;3&#xff09;默认白色背景。 2…

有人抱怨Android找不到工作,有人却收到了好几个Offer~

不知不觉&#xff0c;往年常说的面试黄金季就这样过去了&#xff0c;相信现在很多人都会抱怨说&#xff0c;现在是市场岗位缩水裁员季。有人抱怨&#xff0c;自然也有人喜悦&#xff0c;有失业人群在&#xff0c;自然就业人群也有&#xff0c;有人想找一份合理工作很难&#xf…

C高级(day1)

作业: 初始工作路径不在家目录下&#xff0c;在不切换路径的情况下&#xff0c;在家目录下创建一个subdir目录&#xff0c;在subdir这个目录下&#xff0c;创建subdir1和subdir2&#xff0c;并且把/etc/passwd拷贝到subdir1中&#xff0c;把/etc/group文件拷贝到subdir2中&…

David Silver Lecture 5: Model-Free Control

1 Introduction 1.1 内容 上一章是对一个unknown MDP进行value function的预测&#xff0c;相当于policy evaluation。这一章是对unknown MDP找到一个最优的policy&#xff0c; optimise value function. 1.2 On and Off-Policy Learning On-policy learning learn on the…

[oeasy]python0050_动态类型_静态类型_编译_运行

动态类型_静态类型 回忆上次内容 上次了解了 帮助文档的 生成 开头的三引号注释 可以生成 帮助文档文档 可以写成网页 python3 本身 也有 在线的帮助手册 目前的程序 提高了 可读性 有什么方法 可以让程序 更可读么&#xff1f;&#x1f914; 变量名 首先 在变量名上想办…

opencv_c++学习(六)

一、视频加载与摄像头调用 视频、摄像头加载 VideoCapture(filename, CAP_ANY)对以上实例解释如下&#xff1a; 若读取的为本地视频&#xff0c;则filename为视频名称&#xff0c;若读取的是摄像头数据&#xff0c;则为int类型的摄像头id。 视频属性的获取 视频属性可以通过…