一、项目背景

基于Spring AI的企业级 AI 恋爱大师智能体,为用户提供情感指导服务。支持记忆持久化、RAG 知识库检索等能力,并且基于 ReAct 模式,能够自主思考并调用工具来完成复杂任务。

1. 测试目标及测试任务概括

  • 测试对象:基于SpringAI的MonadFlowAI
  • 测试目的:校验项目功能是否符合预期
  • 测试方法和工具:主要是黑盒测试,自动化工具使用 Selenium 和 Junit, 性能测试工具使用Jmeter, 接口测试工具: postman
  • 测试环境:
    硬件:Lenovo r7000p 2023(R7-7840H/16GB/1T/独显)。
    浏览器:Google Chrome 版本 143.0.7499.110(正式版本) (64 位)。
    操作系统:Windows 11。

2. 被测试的系统、代码包及文档等信息

项目源码链接
MonadFlowAI项目 - Gitee.com

接口:

用户功能:

/api/user/login(登录/get)

系统功能:

/api/ai/love_app/chat/sse(AI恋爱大师/get)
/api/ai/JManus/chat(AI超级智能体/get)
/api/ai/wangzhe/chat(王者农药攻略/get)

3. 产品需求和设计文档等

二、测试安排

三、测试分类

1. 功能测试

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

手动测试

登录页面测试

测试场景一 : 用户名/密码都不填
预期结果 : 系统提示用户名/密码为空

在这里插入图片描述

在这里插入图片描述

测试场景二: 用户名为空,密码不为空
预期结果 : 系统提示"请输入用户名"

在这里插入图片描述

测试场景三 : 用户名不为空,密码为空
预期结果 : 系统提示"请输入密码"

在这里插入图片描述

测试场景四 : 用户名不空,密码不空(格式都不正确)
预期结果 : 系统提示错误信息

在这里插入图片描述

测试场景五 : 用户名不空,密码不空(格式都正确,但是用户名不存在)
预期结果 : 系统提示"用户不存在"

在这里插入图片描述

测试场景六 : 用户名不空,密码不空(格式都正确,用户名存在,密码错误)
预期结果 : 系统提示错误信息"用户名或密码错误"

在这里插入图片描述

测试场景七 : 用户名不空,密码不空(格式都正确,用户名存在,密码正确)
预期结果 : 从登录页跳转至主页

在这里插入图片描述

主页测试

测试场景一 : 登录后,跳转至主页
预期结果 : 主页正常显示

在这里插入图片描述

测试场景二: 注销后,跳转至登录页
预期结果 : 成功跳转

在这里插入图片描述

AI恋爱大师页面测试

测试场景一 : 从主页点击后, 跳转至AI恋爱大师页面
预期结果 : 跳转成功,显示聊天页面

在这里插入图片描述

测试场景二 : 发送恋爱问题,聊天助手能精确快速响应解答
预期结果 : 以打字机效果输出文本

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

测试场景三 : 验证AI恋爱大师的对话记忆的能力,重复询问我的名字,看他是否还记得
预期结果 : 正确告诉我我的名字

在这里插入图片描述

测试场景四 : 验证AI恋爱大师的中断对话能力,当他还在输出文本的时候能不能临时打断
预期结果 : 成功打断

输出中:
在这里插入图片描述
打断后:
在这里插入图片描述

测试场景五 : 验证消息上下文的引用功能
预期结果 : 成功将消息引用至输入框内

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

测试场景六 : 验证消息上下文的复制功能
预期结果 : 成功将消息复制,并且可ctrl+v粘贴

在这里插入图片描述

测试场景七 : 验证消息的发送按钮,不仅仅发送按钮可以发送消息,回车键也能发送消息
预期结果 : 成功将消息发送

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

AI超级智能体页面测试

测试场景一 : 从主页点击后, 跳转至AI超级智能体页
预期结果 : 跳转成功,显示聊天页面

在这里插入图片描述

测试场景二: 给AI超级智能体发布任务描述后, 执行工具,完成任务
预期结果 : 有具体的思考过程让用户感知, 任务完成符合预期

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

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

测试场景三: 给AI超级智能体发布任务描述后, 执行工具,有最大工具的调用次数限制
预期结果 : 思考过程自动检测是否超过了最大歩奏

在这里插入图片描述

测试场景四: 测试引用功能是否正常可用
预期结果 : 引用正常可用

在这里插入图片描述

测试场景五: 测试复制功能是否正常可用
预期结果 : 复制功能正常可用

在这里插入图片描述

测试场景六: 测试正常结束的时候是否会调用doTerminate工具
预期结果 : 成功调用,任务成功结束

在这里插入图片描述

在这里插入图片描述

测试场景八 : 验证消息的发送按钮,不仅仅发送按钮可以发送消息,回车键也能发送消息
预期结果 : 成功将消息发送

在这里插入图片描述

测试场景九 : 验证消息的中断按钮能正常中断思考过程
预期结果 : 成功中断

在这里插入图片描述

王者农药攻略页面测试

测试场景一 : 由于该功能正在开发中,所以原则上不可访问
预期结果 : 提示"敬请期待"

在这里插入图片描述

自动化测试

测试快照
package com.example.testyaoagent.testutil;

import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;

import java.io.File;
import java.io.IOException;
import java.sql.Driver;
import java.text.SimpleDateFormat;

public class TestUtil {

    public static Driver driver = null;
    public static void pictureShot(WebDriver driver) throws IOException {
        SimpleDateFormat s1 = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat s2 = new SimpleDateFormat("HHmmssSS");
        String t1 = s1.format(System.currentTimeMillis());
        String t2 = s2.format(System.currentTimeMillis());

        //从调用栈里获取方法名:
        Throwable t = new Throwable();
        StackTraceElement[] stackTrace = t.getStackTrace();
        String className = stackTrace[0].getClassName();
        String methodName = stackTrace[0].getMethodName();
        String fileName = "src/test/java/com/example/testyaoagent/picture/"+t1+"/"+methodName+"-"+t2+".png";
        File picture = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
        FileUtils.copyFile(picture,new File(fileName));
    }
}

登录页测试
package com.example.testyaoagent.tests;

import com.example.testyaoagent.testutil.TestUtil;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.junit.jupiter.api.*;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;
import java.time.Duration;

@SpringBootTest
public class LoginTest {

    public static WebDriver driver = null;

    @BeforeEach
    public void setUp(){
//        WebDriverManager.chromedriver().setup();
        // 由于直接setup拉取chromedriver失败,所以这里手动指定本地ChromeDriver路径
        System.setProperty("webdriver.chrome.driver", "D:\\selenium-chromedriver\\chromedriver-win64\\chromedriver.exe");
        ChromeOptions chromeOptions = new ChromeOptions();
        chromeOptions.addArguments("--remote-allow-origins=*");
        driver = new ChromeDriver(chromeOptions);
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
    }
    @AfterEach
    public void tearDown() throws InterruptedException {
        Thread.sleep(1000);
        driver.close();
    }
    @Test
    public void login() throws InterruptedException, IOException {
        driver.get("http://pinwei.icu/");
        TestUtil.pictureShot(driver);
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@class=\"el-form el-form--default el-form--label-right login-form\"]/div[1]/div/div/div/input")).sendKeys("zhangsan");
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@class=\"el-form el-form--default el-form--label-right login-form\"]/div[2]/div/div/div/input")).sendKeys("admin666");
        TestUtil.pictureShot(driver);
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div/div[2]/form/div[3]/div/button")).click();
        TestUtil.pictureShot(driver);


        //验证跳转:
        Thread.sleep(2000);
        String text = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[4]/p")).getText();
        Assertions.assertEquals("选择您需要的AI应用",text);
        Thread.sleep(2000);
        String text1 = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[3]/button/span[2]")).getText();
        Assertions.assertEquals("注销登录",text1);

    }
    //密码错误测试:
    @Test
    public void login_error() throws InterruptedException, IOException {
        driver.get("http://pinwei.icu/");
        TestUtil.pictureShot(driver);
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@class=\"el-form el-form--default el-form--label-right login-form\"]/div[1]/div/div/div/input")).sendKeys("zhangsan");
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@class=\"el-form el-form--default el-form--label-right login-form\"]/div[2]/div/div/div/input")).sendKeys("123456");
        TestUtil.pictureShot(driver);
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div/div[2]/form/div[3]/div/button")).click();
        TestUtil.pictureShot(driver);
        //验证登录错误
        Thread.sleep(2000);
        String text = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div/div[1]/div/span")).getText();
        Assertions.assertEquals("用户登录",text);

    }

    @Test
    public void logout() throws InterruptedException, IOException {
        driver.get("http://pinwei.icu/");
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@class=\"el-form el-form--default el-form--label-right login-form\"]/div[1]/div/div/div/input")).sendKeys("zhangsan");
        Thread.sleep(2000);
        //之所以密码设置复杂一点,就是为了防止google弹窗不安全
        driver.findElement(By.xpath("//*[@class=\"el-form el-form--default el-form--label-right login-form\"]/div[2]/div/div/div/input")).sendKeys("admin666");
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div/div[2]/form/div[3]/div/button")).click();
        //验证跳转:
        Thread.sleep(2000);
        String text = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[4]/p")).getText();
        Assertions.assertEquals("选择您需要的AI应用",text);
        Thread.sleep(2000);
        String text1 = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[3]/button/span[2]")).getText();
        Assertions.assertEquals("注销登录",text1);

        //测试注销登录
        Thread.sleep(2000);
        TestUtil.pictureShot(driver);

        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[3]/button")).click();
        TestUtil.pictureShot(driver);

        //测试是否注销成功
        Thread.sleep(2000);
        String text2 = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div/div[2]/form/div[3]/div/button")).getText();
        Assertions.assertEquals("登 录",text2);
        Thread.sleep(2000);
        String text3 = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div/div[1]/div/span")).getText();
        Assertions.assertEquals("用户登录",text3);

    }
}

AI恋爱大师页测试 & AI超级智能体页测试
package com.example.testyaoagent.tests;

import com.example.testyaoagent.testutil.TestUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;
import java.time.Duration;

@SpringBootTest
public class LoveAppTest {

    public static WebDriver driver = null;

    @BeforeEach
    public void setUp(){
//        WebDriverManager.chromedriver().setup();
        // 由于直接setup拉取chromedriver失败,所以这里手动指定本地ChromeDriver路径
        System.setProperty("webdriver.chrome.driver", "D:\\selenium-chromedriver\\chromedriver-win64\\chromedriver.exe");
        ChromeOptions chromeOptions = new ChromeOptions();
        chromeOptions.addArguments("--remote-allow-origins=*");
        driver = new ChromeDriver(chromeOptions);
//        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(3));
    }
    //    @AfterEach
//    public void tearDown() throws InterruptedException {
//        Thread.sleep(1000);
//        driver.close();
//    }
    @Test
    public void loveapp() throws InterruptedException, IOException {
        driver.get("http://pinwei.icu/");
        //先登录
        driver.findElement(By.xpath("//*[@class=\"el-form el-form--default el-form--label-right login-form\"]/div[1]/div/div/div/input")).sendKeys("zhangsan");
        driver.findElement(By.xpath("//*[@class=\"el-form el-form--default el-form--label-right login-form\"]/div[2]/div/div/div/input")).sendKeys("admin666");
        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div/div[2]/form/div[3]/div/button")).click();
        //验证跳转:
        Thread.sleep(2000);
        String text = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[4]/p")).getText();
        Assertions.assertEquals("选择您需要的AI应用",text);
        String text1 = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[3]/button/span[2]")).getText();
        Assertions.assertEquals("注销登录",text1);
        //验证功能
        //进入loveapp聊天页面:
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[5]/div[1]/div[3]")).click();
        //验证跳转:
        Thread.sleep(2000);
        String text2 = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[2]/h1")).getText();
        Assertions.assertEquals("AI 恋爱大师",text2);

        //发送测试消息:
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[4]/div/input")).sendKeys("我失恋了怎么办啊?");
        //发送:
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[4]/button/div")).click();
        //截图保存
        TestUtil.pictureShot(driver);

        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// 等待元素出现
        WebElement contentDiv = wait.until(
                ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id=\"app\"]/div/div[3]/div[2]/div[1]/div"))
        );

// 等待内容稳定
        String previousContent = "";
        int stableCount = 0;

        while (stableCount < 3) {
            Thread.sleep(500);
            String currentContent = contentDiv.getText();
            if (currentContent.equals(previousContent) && !currentContent.isEmpty()) {
                stableCount++;
            } else {
                stableCount = 0;
                previousContent = currentContent;
            }
        }

        String aiResponse = contentDiv.getText();

        TestUtil.pictureShot(driver);
//        //由于流式输出的时候不能定位,我们等输出完以后测试:
//        Thread.sleep(2000);
//        String text3 = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[3]/div[2]/div[1]/div")).getText();
        String text3 = aiResponse;
        Assertions.assertNotNull(text3);
        //测试引用功能是否可用:
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[3]/div[2]/div[2]/div[2]/button[1]")).click();
        //检查输入框是否多了东西,如果多了东西,肯定可以发送,需要显式等待了:
        Thread.sleep(2000);
        WebElement element = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[4]/div/input"));
        // 检查元素是否可见且可交互
        boolean ok = element.isDisplayed() && element.isEnabled();
        Assertions.assertTrue(ok);

    }
    @Test
    public void ai_agent() throws InterruptedException, IOException {
        driver.get("http://pinwei.icu/");
        //先登录
        driver.findElement(By.xpath("//*[@class=\"el-form el-form--default el-form--label-right login-form\"]/div[1]/div/div/div/input")).sendKeys("zhangsan");
        driver.findElement(By.xpath("//*[@class=\"el-form el-form--default el-form--label-right login-form\"]/div[2]/div/div/div/input")).sendKeys("admin666");
        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div/div[2]/form/div[3]/div/button")).click();
        //验证跳转:
        Thread.sleep(2000);
        String text = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[4]/p")).getText();
        Assertions.assertEquals("选择您需要的AI应用",text);
        String text1 = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[3]/button/span[2]")).getText();
        Assertions.assertEquals("注销登录",text1);

        //验证功能
        //进入智能体聊天页面:
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[5]/div[2]/div[3]")).click();
        //验证跳转:
        Thread.sleep(2000);
        String text2 = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[2]/h1")).getText();
        Assertions.assertEquals("AI 超级智能体",text2);

        //发送测试消息:
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[4]/div/input")).sendKeys("我想明天下午在杭州西湖附近找到一些合适的约会地点,找几张西湖附近餐厅的照片,帮我制定一个约会计划,整理成一个pdf");
        //发送:
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[4]/button/div")).click();
        //截图保存
        TestUtil.pictureShot(driver);
        Thread.sleep(10000);
        TestUtil.pictureShot(driver);

        //测试引用功能是否可用:
        Thread.sleep(2000);
        driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[3]")).click();
        //检查输入框是否多了东西,如果多了东西,肯定可以发送,需要显式等待了:
        Thread.sleep(2000);
        WebElement element = driver.findElement(By.xpath("//*[@id=\"app\"]/div/div[4]/button"));
        // 检查元素是否可见且可交互
        boolean ok = element.isDisplayed() && element.isEnabled();
        Assertions.assertTrue(ok);
    }
}

  • 自动化测试覆盖模块: 连接管理页, 首页, channel管理页
  • 自动化测试代码:
  • 自动化测试用例数量: 20
  • 自动化测试结果: pass: 19/20 , fail :1/20
  • 自动化测试问题是否修复: 是

2. 接口测试

接口jmeter测试

登录接口

在这里插入图片描述

恋爱大师接口

由于汉字编码问题,不能正常显示,但实际数据在前端解析是正确的
在这里插入图片描述

AI超级智能体接口

由于汉字编码问题,不能正常显示,但实际数据在前端解析是正确的
在这里插入图片描述

接口自动化测试

3. 性能测试

负载测试

活跃线程可视化

在这里插入图片描述

聚合报告

在这里插入图片描述

TPS可视化

在这里插入图片描述

响应时间可视化

由于AI恋爱大师和AI超级智能体使用的是SSE传输,所以传输时间相对来讲很长,但可接受
在这里插入图片描述

性能测试报告

在这里插入图片描述

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

4. 兼容性测试

测试场景一:在Edge浏览器中进入
预期结果:无异常且布局/显示/功能与预期一致

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

测试场景二:在火狐浏览器中进入
预期结果:无异常且布局/显示/功能与预期一致

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

测试场景三:在联想浏览器中进入
预期结果:无异常且布局/显示/功能与预期一致

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

5. 易用性测试

测试场景一:操作错误是否有详细的报错信息
预期结果:错误有引导,反馈清晰

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

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

6. 安全测试

测试场景一:长时间未操作
预期结果:自动重新登录

在这里插入图片描述

测试场景二:密码是否经过加密存储
预期结果:密码经过加密存储

在这里插入图片描述

四、遗留风险

    1. 测试时间紧张, 先保证整个系统主功能没有问题, 可能存在细节测试不到位等风险
    1. 某些操作失败后只有笼统的错误提示, 没有细化
    1. 对于移动端的支持有待提高
    1. AI超级智能体流式输出的思考过程有时候会出现思考过程输出不全面,输出残缺的问题,这一点可以在前端好好优化
    1. 报错信息可以更加细化, 方便用户针对性解决

五、测试结果评估

    1. 接口全部正常
    1. 主功能测试通过, 项目可以上线
    1. 通过高并发测试已达到预期性能指标
    1. 项目上线后高优观察线上数据, 查看线上用户操作日志, 及时跟进用户反馈
Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐