MonadFlowAI测试报告
基于Spring AI的企业级 AI 恋爱大师智能体,为用户提供情感指导服务。支持记忆持久化、RAG 知识库检索等能力,并且基于 ReAct 模式,能够自主思考并调用工具来完成复杂任务。
一、项目背景
基于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. 安全测试
测试场景一:长时间未操作
预期结果:自动重新登录

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

四、遗留风险
-
- 测试时间紧张, 先保证整个系统主功能没有问题, 可能存在细节测试不到位等风险
-
- 某些操作失败后只有笼统的错误提示, 没有细化
-
- 对于移动端的支持有待提高
-
- AI超级智能体流式输出的思考过程有时候会出现思考过程输出不全面,输出残缺的问题,这一点可以在前端好好优化
-
- 报错信息可以更加细化, 方便用户针对性解决
五、测试结果评估
-
- 接口全部正常
-
- 主功能测试通过, 项目可以上线
-
- 通过高并发测试已达到预期性能指标
-
- 项目上线后高优观察线上数据, 查看线上用户操作日志, 及时跟进用户反馈
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)