当前位置: 首页 > news >正文

自动化测试项目2 --- 比特纵横 [软件测试实战 Java 篇]

目录

项目介绍

项目源码 库地址

项目功能测试

1. 自动化实施步骤

1.1 编写测试用例

1.2 自动化测试脚本开发

1.2.1 配置相关环境, 添加依赖

1.2.2 代码编写

2. 编写自动化脚本过程问题总结

2.1 Actons 方法的使用

2.2 等待的使用

2.3 页面操作

项目性能测试

1. 进行性能测试

2. 生成测试报告 

项目总结

1. 部分使用缺陷总结

2. 测试结论与建议


项目介绍

// "比特纵横" 项目是一个专注于技术交流与知识分享的平台,旨在为开发者、技术爱好者及专业人士提供一个便捷、高效的互动空间;​

// 在功能方面,它支持用户发布技术文章、提出问题、分享项目和私信交流经验等。无论是探讨热门的编程语言、交流前沿的技术框架,还是解决开发过程中遇到的实际难题,都能在这里实现。用户可以对感兴趣的内容进行评论、点赞和私信回复,方便随时与他人交流看法。同时,系统具备强大的导航栏功能,用户能够快速定位到自己需要的技术内容;

// 使用本技术论坛系统,用户可以拓展技术视野,学习他人的优秀经验,提升自身技术水平。同时,还能结识志同道合的朋友,共同成长进步,助力每一位技术人在探索之路上不断前行。​

项目源码 库地址

比特纵横项目 git 库地址, 点击跳转https://gitee.com/zhou-jintao2002/forum-auto-test

项目功能测试

1. 自动化实施步骤

1.1 编写测试用例

1.2 自动化测试脚本开发

1.2.1 配置相关环境, 添加依赖

// 自动化测试基础知识及相关函数, 包括 Selenium 的安装和使用基础教程

1.2.2 代码编写

// 我们按照上面图中的不同分类层级分别进行创建, 方便后期代码视为管理和修改

// 以下是具体的代码内容, 需要源码可以点击上面的链接在 git 仓库中进行观看和引用

Utils.java

/*
通用配置*/
public class Utils {public static WebDriver driver;public static  WebDriver createDriver() {if (driver == null) {// 创建驱动对象WebDriverManager.chromedriver().setup();ChromeOptions options = new ChromeOptions();// 设置无头模式options.addArguments("-headless");// 设置浏览器加载策略options.setPageLoadStrategy(PageLoadStrategy.NORMAL);// 允许访问所以链接options.addArguments("--remote-allow-origins=*");// 驱动创建好了driver = new ChromeDriver(options);// 创建隐式等待driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(2));}return driver;}public Utils(String url) {// 调用 driver 对象driver = createDriver();// 访问 urldriver.get(url);}/*创建屏幕截图*/public void getScreenShot(String str) throws IOException {// 屏幕截图SimpleDateFormat sim1 = new SimpleDateFormat("yyyy-MM-dd");SimpleDateFormat sim2 = new SimpleDateFormat("HHmmssSS");String dirTime = sim1.format(System.currentTimeMillis());String fileTime = sim2.format(System.currentTimeMillis());// 生成文件的格式为: 年-月-日/test.类名-时分秒毫秒.pngString fileName = "./src/test/image/" + dirTime + "/" + str + "-" + fileTime + ".png";// System.out.printf("fileName:" + fileName);File srcFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);// srcFile 放到指定位置FileUtils.copyFile(srcFile,new File((filename)));}
}

RunTests.java 

/*
所有代码的运行*/
public class RunTests {public static void main(String[] args) throws InterruptedException {// 登录测试LoginPage login = new LoginPage();login.LoginPageRight();login.LoginSuc();login.LoginNoUserNameAndPassword();login.LoginNoUserName();login.LoginNoPassward();login.LoginUserNameError();login.LoginPasswordError();// 注册测试RegisterPage regist = new RegisterPage();regist.ReqisterPageRight();regist.GitContent();regist.ReqisterReturnLogin();regist.RegisterSuc();regist.RegisterNoUserName();regist.RegisterNoNickName();regist.RegisterNoPassword();regist.RegisterPasswordAdmitFailuer();regist.RegisterNoAgreeAccord();// 论坛首页测试HomePage home = new HomePage();home.HomeLogin();home.HomeSearch();home.HomeColorSwitch();home.HomeReceiveMessages();home.HomeCloseMessage();home.HomeReplyMessage();home.HomeMyAccountSuc();home.HomeCloseMyAccount();home.HomePostSkip();// 帖子详情页测试PostDetailsPage postDetails = new PostDetailsPage();postDetails.HomeOpenSuc();postDetails.PostOpen();postDetails.PostThumbsUp();postDetails.PostRewriteEnter();postDetails.PostRewrite();postDetails.PostDelete();postDetails.PostReply();// 新发布帖子测试AddPostPage addPost = new AddPostPage();addPost.AddPostEnter();addPost.AddPost();// 用户详情页测试UserInfoPage userInfo = new UserInfoPage();userInfo.UserInfoEnter();userInfo.PasswordChange();userInfo.PasswordChangeFail1();userInfo.PassWordChangeFail2();// 我的帖子页测试MyPostsPage myPost = new MyPostsPage();myPost.MyPostEter();myPost.PostEnterSuc();// 未登录的所有测试PageByNoLogin noLogin = new PageByNoLogin();noLogin.ByNoLogin();Thread.sleep(1000);driver.quit();}
}

 LoginPage.java

/*
登录功能测试*/
public class LoginPage extends Utils {public static String url = "http://127.0.0.1:58080/sign-in.html";public LoginPage() {super(url);}// 检查登录界面是否正常打开public void LoginPageRight() throws InterruptedException {// 获取页面元素String loginname = driver.findElement(By.cssSelector("body > div > div > div > div:nth-child(1) > div > div.card.card-md > div > h2")).getText();// 断言检查assert loginname.equals("用户登录");}// 成功登录public void LoginSuc() throws InterruptedException {// 刷新页面driver.navigate().refresh();// 输入账号密码,并点击登录按钮driver.findElement(By.cssSelector("#username")).sendKeys("zhangsan");driver.findElement(By.cssSelector("#password")).sendKeys("123");driver.findElement(By.cssSelector("#submit")).click();// 通过断言检查是否登录成功String title = driver.findElement(By.cssSelector("#article_list_board_title")).getText();assert title.equals("首页");}// 异常登录 (用户名和密码都为空)public void LoginNoUserNameAndPassword() throws InterruptedException {// 从成功登录界面回退到登录界面driver.navigate().back();// 刷新界面, 清空输入框中的内容driver.navigate().refresh();// 找到用户名输入框, 输入为空driver.findElement(By.cssSelector("#username")).sendKeys("");// 找到密码输入框, 输入为空driver.findElement(By.cssSelector("#password")).sendKeys("");// 点击登录按钮driver.findElement(By.cssSelector("#submit")).click();// 检查用户名输入框下面的提示是否为"用户名不能为空"String tip1 = driver.findElement(By.cssSelector("#signInForm > div.mb-3 > div")).getText();assert tip1.equals("用户名不能为空");// 检查密码输入框下面的提示是否为"密码不能为空"String tip2 = driver.findElement(By.cssSelector("#signInForm > div.mb-2 > div > div")).getText();assert tip2.equals("密码不能为空");}// 异常登录 (用户名为空)public void LoginNoUserName() {// 刷新界面, 清空输入框中的内容driver.navigate().refresh();// 找到用户名输入框, 输入为空driver.findElement(By.cssSelector("#username")).sendKeys("");// 找到密码输入框, 输入正确密码driver.findElement(By.cssSelector("#password")).sendKeys("123");// 点击登录按钮driver.findElement(By.cssSelector("#submit")).click();// 检查用户名输入框下面的提示是否为"用户名不能为空"String tip1 = driver.findElement(By.cssSelector("#signInForm > div.mb-3 > div")).getText();assert tip1.equals("用户名不能为空");}// 异常登录 (密码为空)public void LoginNoPassward() {// 刷新界面, 清空输入框中的内容driver.navigate().refresh();// 找到用户名输入框, 输入用户名driver.findElement(By.cssSelector("#username")).sendKeys("zhangsan");// 找到密码输入框, 输入为空driver.findElement(By.cssSelector("#password")).sendKeys("");// 点击登录按钮driver.findElement(By.cssSelector("#submit")).click();// 检查密码输入框下面的提示是否为"密码不能为空"String tip1 = driver.findElement(By.cssSelector("#signInForm > div.mb-2 > div > div")).getText();assert tip1.equals("密码不能为空");}// 异常登录(用户名错误)public void LoginUserNameError() throws InterruptedException {// 刷新界面, 清空输入框中的内容driver.navigate().refresh();// 找到用户名输入框, 输入错误的用户名driver.findElement(By.cssSelector("#username")).sendKeys("zhngsan");// 找到密码输入框, 输入正确的密码driver.findElement(By.cssSelector("#password")).sendKeys("123");// 点击登录按钮driver.findElement(By.cssSelector("#submit")).click();// 获取 urlThread.sleep(1000);String loginUrl = driver.getCurrentUrl();// 判断当前是否还是登录页面, 还在登录页面则说明登录失败assert loginUrl.equals("http://127.0.0.1:58080/sign-in.html");}// 异常登录(密码错误)public void LoginPasswordError() throws InterruptedException {// 刷新界面, 清空输入框中的内容driver.navigate().refresh();// 找到用户名输入框, 输入正确的用户名driver.findElement(By.cssSelector("#username")).sendKeys("zhangsan");// 找到密码输入框, 输入正确的密码driver.findElement(By.cssSelector("#password")).sendKeys("1243");// 点击登录按钮driver.findElement(By.cssSelector("#submit")).click();// 获取 urlThread.sleep(1000);String loginUrl = driver.getCurrentUrl();// 判断当前是否还是登录页面, 还在登录页面则说明登录失败assert loginUrl.equals("http://127.0.0.1:58080/sign-in.html");}
}

 RegisterPage.java

/*
注册功能测试*/
public class RegisterPage extends Utils {public static String url = "http://127.0.0.1:58080/sign-up.html";public RegisterPage() {super(url);}// 检查注册页面是否正常打开public void ReqisterPageRight() {// 获取界面元素String title = driver.findElement(By.cssSelector("#signUpForm > div > h2")).getText();// 通过断言判断界面是否加载啊成功assert title.equals("用户注册");}/*部分按钮测试*/// 测试显示/隐藏密码的按钮是否正常// 因为前端获取不到文本信息, 所以自动化代码无法测试, 但是肉眼可以观察到这个功能正常public void GitContent() throws InterruptedException {// 在密码输入框输入密码driver.findElement(By.cssSelector("#password")).sendKeys("1122");// 点击隐藏/显示密码 按钮driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(4) > div > span")).click();Thread.sleep(2000);// 获取密码String passwardShow = driver.findElement(By.cssSelector("#password")).getText();// 断言判断assert passwardShow.equals("1122");
//        System.out.println(passwardShow);
//        System.out.println("1");}// 返回登录按钮测试public void ReqisterReturnLogin() {// 点击登录按钮driver.findElement(By.cssSelector("body > div > div > div.text-center.text-muted.mt-3 > a")).click();// 通过断言判断是否成功返回String loginUrl = driver.getCurrentUrl();assert loginUrl.equals("http://127.0.0.1:58080/sign-in.html");}/*注册功能测试*/// 注册成功// 注册用户名每次测试都需要更改public void RegisterSuc() throws InterruptedException {// 返回注册页面操作driver.navigate().back();// 刷新页面, 确保输入框无内容driver.navigate().refresh();// 输入正确的用户名driver.findElement(By.cssSelector("#username")).sendKeys("ceshi1.3");// 输入正确的昵称driver.findElement(By.cssSelector("#nickname")).sendKeys("ceshi13");// 输入正确的密码driver.findElement(By.cssSelector("#password")).sendKeys("111");// 输入真确的确认密码driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("111");// 勾选同意按钮driver.findElement(By.cssSelector("#policy")).click();// 点击注册driver.findElement(By.cssSelector("#submit")).click();Thread.sleep(2000);// 获取界面 urlString loginUrl = driver.getCurrentUrl();// 断言检测是否跳转到登录页面assert loginUrl.equals("http://127.0.0.1:58080/sign-in.html");}// 注册失败 (用户名为空)public void RegisterNoUserName() {// 重新返回注册页进行操作driver.navigate().back();// 刷新页面, 确保输入框无内容driver.navigate().refresh();// 在用户名输入框输入空driver.findElement(By.cssSelector("#username")).sendKeys("");// 需要点击一下页面之后才会出现提示driver.findElement(By.cssSelector("#submit")).click();// 获取输入框下的提示信息String tip1 = driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(2) > div")).getText();// 断言检测提示信息是否为"用户名不能为空"assert tip1.equals("用户名不能为空");}// 注册失败 (昵称为空)public void RegisterNoNickName() {// 刷新页面, 确保输入框无内容driver.navigate().refresh();// 在昵称输入框输入空driver.findElement(By.cssSelector("#nickname")).sendKeys("");// 需要点击一下页面之后才会出现提示driver.findElement(By.cssSelector("#submit")).click();// 获取输入框下的提示信息String tip1 = driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(3) > div")).getText();// 断言检测提示信息是否为"昵称不能为空"assert tip1.equals("昵称不能为空");}// 注册失败 (密码为空)public void RegisterNoPassword () {// 刷新页面, 确保输入框无内容driver.navigate().refresh();// 在密码输入框中输入空driver.findElement(By.cssSelector("#password")).sendKeys("");// 点击一下页面driver.findElement(By.cssSelector("#submit")).click();// 获取输入框下的提示信息String tip2 = driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(4) > div > div")).getText();// 断言检测是否为"密码不能为空"assert tip2.equals("密码不能为空");}// 注册失败 (确认密码不正确)public void RegisterPasswordAdmitFailuer() {// 刷新页面driver.navigate().refresh();// 在密码输入框中输入正确的密码driver.findElement(By.cssSelector("#password")).sendKeys("123");// 在确认密码框输入与原密码不同的密码driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("12");// 点击一下页面driver.findElement(By.cssSelector("#submit")).click();// 获取输入框下的提示信息String tip3 = driver.findElement(By.cssSelector("#signUpForm > div > div:nth-child(5) > div > div")).getText();// 断言检测是否为"请检查确认密码"assert tip3.equals("请检查确认密码");}// 注册失败 (未勾选同意协议框)public void RegisterNoAgreeAccord() {// 刷新界面, 确保输入框为空driver.navigate().refresh();// 在用户名输入框输入正确的用户名driver.findElement(By.cssSelector("#username")).sendKeys("ceshi2");// 在昵称输入框输入真确的昵称driver.findElement(By.cssSelector("#nickname")).sendKeys("ceshi2.0");// 在密码输入框输入正确的密码driver.findElement(By.cssSelector("#password")).sendKeys("123");// 在确认密码输入框输入正确的密码driver.findElement(By.cssSelector("#passwordRepeat")).sendKeys("123");// 点击注册driver.findElement(By.cssSelector("#submit")).click();// 获取页面 urlString registerUrl = driver.getCurrentUrl();// 断言检查是否为注册界面的url, 以此判断不勾选同意协议框是否能注册成功assert registerUrl.equals("http://127.0.0.1:58080/sign-up.html");}
}

 HomePage.java

/*
论坛首页测试*/
public class HomePage extends Utils {public static String url = "http://127.0.0.1:58080/sign-in.html";public HomePage() {super(url);}/*未登录状态下 --- 访问论坛首页由 PageByNoLogin 进行测试*//*登录状态下 --- 测试论坛首页*/// 检查页面(论坛首页)是否加载成功public void HomeLogin() throws InterruptedException {// 返回登录页并刷新页面driver.navigate().refresh();// 重新登录driver.findElement(By.cssSelector("#username")).sendKeys("zhangsan");driver.findElement(By.cssSelector("#password")).sendKeys("123");driver.findElement(By.cssSelector("#submit")).click();// 加一个显式等待WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(1));wait.until(ExpectedConditions.urlToBe("http://127.0.0.1:58080/index.html"));// 获取首页 urlString HomeUrl = driver.getCurrentUrl();// 断言判断是否跳转到了论坛首页assert HomeUrl.equals("http://127.0.0.1:58080/index.html");}// 搜索功能测试public void HomeSearch() throws InterruptedException {// 找到搜索框并输入想要搜索的内容driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input")).sendKeys("1");// 点击搜索按钮(没有点击按钮, 选择从键盘输入 Enter 跳转)
//        driver.findElement(By.cssSelector("")).click();// 查找搜索框元素WebElement element = driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input"));// 鼠标选中搜索框并点击Actions actions = new Actions(driver);actions.click(element).perform();// 键盘输入 Enter 进行搜索
//        actions.keyDown(Keys.ENTER).perform();// 因为 Enter 键一般不单独使用, 需要结合其他 Alt 按键使用, 所以修改代码actions.sendKeys(Keys.ENTER).build().perform();// 添加显式等待WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(1));wait.until(ExpectedConditions.urlToBe("http://127.0.0.1:58080/?"));// 获取当前页面的 urlString searchUrl = driver.getCurrentUrl();// 断言检查是否搜索成功assert searchUrl.equals("http://127.0.0.1:58080/?");}//白天和夜间模式切换测试public void HomeColorSwitch() {// 找到模式切换按钮并点击, 即切换为夜间模式driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-dark > svg")).click();// 添加显式等待WebDriverWait wait1 = new WebDriverWait(driver, Duration.ofSeconds(1));wait1.until(ExpectedConditions.urlToBe("http://127.0.0.1:58080/?theme=dark"));// 获取界面 urlString blackUrl = driver.getCurrentUrl();// 断言检查是否切换成功assert blackUrl.equals("http://127.0.0.1:58080/?theme=dark");// 再次点击模式切换按钮, 即切换为白天模式driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-light > svg")).click();// 添加显式等待WebDriverWait wait2 = new WebDriverWait(driver, Duration.ofSeconds(1));wait2.until(ExpectedConditions.urlToBe("http://127.0.0.1:58080/?theme=light"));// 获取界面 urlString lighteUrl = driver.getCurrentUrl();// 断言判断是否切换成功assert lighteUrl.equals("http://127.0.0.1:58080/?theme=light");}// 接收站内消息通知测试public void HomeReceiveMessages() throws InterruptedException {// 点击消息查看按钮driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg")).click();// 添加一个强制等待Thread.sleep(1000);//断言检查是否正常打开String receive = driver.findElement(By.cssSelector("#offcanvasEndLabel")).getText();assert receive.equals("所有站内信");}// 回复私信测试public void HomeReplyMessage() throws InterruptedException {// 点击消息查看消息详情driver.findElement(By.cssSelector("#index_div_message_list > div > div > div:nth-child(3)")).click();// 点击回复按钮driver.findElement(By.cssSelector("#btn_index_message_reply")).click();// 这个输入框不能直接交互, 所以先点击之后再输入WebElement write = driver.findElement(By.cssSelector("#index_message_reply_receive_content"));Actions actions = new Actions(driver);actions.click(write).perform();// 输入回复内容driver.findElement(By.cssSelector("#index_message_reply_receive_content")).sendKeys("收到!");// 点击发送按钮driver.findElement(By.cssSelector("#btn_index_send_message_reply")).click();}// 发送完之后关闭消息窗口public void HomeCloseMessage() {// 关闭查看消息页面driver.findElement(By.cssSelector("#index_message_offcanvasEnd > div.offcanvas-header > button")).click();}// 我的账户打开测试public void HomeMyAccountSuc() {// 点击头像打开driver.findElement(By.cssSelector("#index_nav_avatar")).click();// 获取界面信息String ele1 = driver.findElement(By.cssSelector("#index_user_settings")).getText();// 断言检测ele1.equals("个人中心");}// 查看完之后关闭我的账户页面public void HomeCloseMyAccount() {// 关闭我的账户页面driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none")).click();}// 导航栏跳转查看详细帖子测试(以Java 板块为例)public void HomePostSkip() throws InterruptedException {// 点击 Java 板块driver.findElement(By.cssSelector("#topBoardList > li:nth-child(2) > a")).click();// 获取该页帖子标题信息Thread.sleep(1000);String content = driver.findElement(By.cssSelector("#artical-items-body > div > div > div.col > div.text-truncate > a > strong")).getText();// 断言检查是否正确跳转assert content.equals("3");}
}

PostDetailsPage.java

/*
帖子详情页的测试*/
public class PostDetailsPage extends Utils {public static String url = "http://127.0.0.1:58080/index.html";public PostDetailsPage() {super(url);}// 首页打开测试public void HomeOpenSuc() {// 需要重新登录// 返回登录页并刷新页面driver.navigate().refresh();// 重新登录driver.findElement(By.cssSelector("#username")).sendKeys("zhangsan");driver.findElement(By.cssSelector("#password")).sendKeys("123");driver.findElement(By.cssSelector("#submit")).click();// 获取界面元素String ele1 = driver.findElement(By.cssSelector("#article_list_board_title")).getText();// 断言判断是否打开成功assert ele1.equals("首页");}// 点击打开帖子, 查看详情public void PostOpen() {// 点击帖子标题, 进入帖子详情页driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();// 获取页面元素String ele2 = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3")).getText();// 断言判断是否进入帖子详情页assert ele2.equals("最新回复");}// 点赞功能测试public void PostThumbsUp() {// 点击点赞按钮driver.findElement(By.cssSelector("#details_btn_like_count")).click();// 获取当前点赞数String ele3 = driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div.col-auto.d-none.d-md-inline > ul > li:nth-child(3)")).getText();// 断言判断是否正确assert ele3.equals("4");}// 编辑功能测试// 编辑页面进入测试public void PostRewriteEnter() throws InterruptedException {Thread.sleep(1000);// 点击编辑按钮, 进入编辑界面driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div:nth-child(1) > div.col-9.card.card-lg > div.card-footer.bg-transparent.mt-auto.justify-content-end > div > div:nth-child(2) > div")).click();// 获取界面元素Thread.sleep(1000);String ele4 = driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div > h2")).getText();// 断言判断是否进入成功assert ele4.equals("编辑");}// 编辑内容测试public void PostRewrite() throws InterruptedException {// 找到标题框元素WebElement ele5 = driver.findElement(By.cssSelector("#edit_article_title"));// 点击帖子标题框Actions actions = new Actions(driver);actions.click(ele5).perform();// 清空原有内容
//        actions.keyDown(Keys.BACK_SPACE).perform();
//        driver.findElement(By.cssSelector("#edit_article_title")).sendKeys("");actions.keyDown(Keys.CONTROL).sendKeys("a").keyUp(Keys.CONTROL).perform();actions.sendKeys("").perform();// 输入修改内容// 也可以用 action.sendKeys();driver.findElement(By.cssSelector("#edit_article_title")).sendKeys("1.5");// 获取修改后的内容
//        String ele6 = driver.findElement(By.cssSelector("#edit_article_title")).getText();// 找到帖子详情内容修改框WebElement ele7 = driver.findElement(By.cssSelector("#edit_article_content_area > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll"));// 点击内容修改框actions.click(ele7).perform();// 清空原有内容
//        Thread.sleep(1000);
//        actions.keyDown(Keys.BACK_SPACE).perform();
//        driver.findElement(By.cssSelector("#edit_article_content_area > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre")).sendKeys("");actions.keyDown(Keys.CONTROL).sendKeys("a").keyUp(Keys.CONTROL).perform();actions.sendKeys("").perform();// 输入要修改的内容
//        driver.findElement(By.cssSelector("#edit_article_content_area > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre")).sendKeys("2.01");actions.sendKeys("1.50").perform();// 点击提交按钮Thread.sleep(1000);WebElement ele8 = driver.findElement(By.cssSelector("#edit_article_submit"));actions.click(ele8).perform();// 获取新帖子标题信息Thread.sleep(2000);String ele9 = driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate")).getText();// 断言判断修改后的帖子标题, 看是否和修改内容相同assert ele9.equals("1.5");}// 删除功能测试public void PostDelete() {// 进入帖子详情页driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();// 点击删除按钮driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div:nth-child(1) > div.col-9.card.card-lg > div.card-footer.bg-transparent.mt-auto.justify-content-end > div > div:nth-child(3) > div > a")).click();// 点击确认按钮driver.findElement(By.cssSelector("#details_artile_delete")).click();// 通过获取新界面的 url 来通过断言判断是否删除成功返回首页String homeUrl = driver.getCurrentUrl();assert homeUrl.equals("http://127.0.0.1:58080/index.html");}// 回复功能测试public void PostReply() throws InterruptedException {// 进入帖子详情页driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong")).click();// 找到回复输入框元素WebElement ele9 =driver.findElement(By.cssSelector("#article_details_reply > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre"));// 点击输入框Actions actions = new Actions(driver);actions.click(ele9).perform();// 输入想要发送的内容Thread.sleep(1000);actions.sendKeys("11").perform();//driver.findElement(By.cssSelector("#article_details_reply > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty > div.CodeMirror-scroll")).sendKeys("测试回复1");// 点击回复Thread.sleep(3000);driver.findElement(By.cssSelector("#details_btn_article_reply")).click();}
}

 AddPostPage.java

/*
新帖发布的测试*/
public class AddPostPage extends Utils {public static String url = "http://127.0.0.1:58080/index.html";public AddPostPage() {super(url);}// 进入发帖界面public void AddPostEnter() throws InterruptedException {// 需要重新登陆// 返回登录页并刷新页面driver.navigate().refresh();// 重新登录driver.findElement(By.cssSelector("#username")).sendKeys("zhangsan");driver.findElement(By.cssSelector("#password")).sendKeys("123");driver.findElement(By.cssSelector("#submit")).click();// 点击 "发布帖子" 按钮 进入发帖界面driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post")).click();// 获取界面元素Thread.sleep(1000);String ele1 = driver.findElement(By.cssSelector("#bit-forum-content > div.page-header.d-print-none")).getText();// 断言判断是否成功进入assert ele1.equals("发新贴");}// 发布新帖测试public void AddPost() throws InterruptedException {// 获取标题输入框元素WebElement ele2 = driver.findElement(By.cssSelector("#article_post_title"));// 点击输入框Actions actions = new Actions(driver);actions.click(ele2).perform();// 输入标题actions.sendKeys("自动输入的标题2");
//        // 获取输入的标题内容
//        String ele3 = driver.findElement(By.cssSelector("#article_post_title")).getText();// 获取内容输入框元素WebElement ele4 = driver.findElement(By.cssSelector("#edit-article > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre"));// 点击输入框actions.click(ele4).perform();// 输入内容actions.sendKeys("自动输入的内容2").perform();// 点击发布按钮// 不能用显式等待, 发布按钮执行太快WebElement ele5 = driver.findElement(By.cssSelector("#article_post_submit"));
//        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(3));
//        wait.until(ExpectedConditions.elementToBeClickable(ele5));// 两种点击方式: 1. 界面定位元素点击; 2. 定位元素模拟鼠标点击
//        driver.findElement(By.cssSelector("#article_post_submit")).click();Thread.sleep(2000);actions.click(ele5).perform();// 刷新页面driver.navigate().refresh();// 获取新页面第一个帖子的标题String ele6 = driver.findElement(By.cssSelector("#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a")).getText();// 断言判断是否发布成功assert ele6.equals("自动输入的标题2");}
}

UserInfoPage.java 

/*
用户详情页测试*/
public class UserInfoPage extends Utils {public static String url = "http://127.0.0.1:58080/index.html";public UserInfoPage() {super(url);}// 进入个人中心测试public void UserInfoEnter() throws InterruptedException {// 需要重新登录// 返回登录页并刷新页面driver.navigate().refresh();// 重新登录driver.findElement(By.cssSelector("#username")).sendKeys("zhangsan");driver.findElement(By.cssSelector("#password")).sendKeys("123");driver.findElement(By.cssSelector("#submit")).click();// 点击 "用户头像" 按钮, 进入选择进入用户个人中心界面driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.dropdown")).click();// 点击 "个人中心" 按钮, 进入个人中心driver.findElement(By.cssSelector("#index_user_settings")).click();// 获取界面元素WebElement ele1 = driver.findElement(By.cssSelector("#settings_submit_password"));WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(1));wait.until(ExpectedConditions.elementToBeClickable(ele1));String ele2 = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div > div.col-3.d-none.d-md-block.border-end > div > div > a")).getText();// 断言判断是否成功进入assert ele2.equals("我的账户");}// 昵称, 邮箱地址, 电话号, 个人介绍 修改后都找不到可以对比的值, 人工测试都正常, 就不加入自动化脚本中了// 密码修改成功测试public void PasswordChange() {// 找到原密码输入框WebElement ele3 = driver.findElement(By.cssSelector("#settings_input_oldPassword"));// 点击原密码输入框Actions actions = new Actions(driver);actions.click(ele3).perform();// 输入原密码actions.sendKeys("123").perform();// 找到新原密码输入框WebElement ele4 = driver.findElement(By.cssSelector("#settings_input_newPassword"));// 点击新原密码输入框actions.click(ele4).perform();// 输入新原密码actions.sendKeys("123").perform();// 找到确认密码输入框WebElement ele5 = driver.findElement(By.cssSelector("#settings_input_passwordRepeat"));// 点击确认密码输入框actions.click(ele5).perform();// 输入确认密码actions.sendKeys("123").perform();// 点击修改密码按钮WebElement ele6 = driver.findElement(By.cssSelector("#settings_submit_password"));WebDriverWait wait = new WebDriverWait(driver,Duration.ofSeconds(1));wait.until(ExpectedConditions.elementToBeClickable(ele6));actions.click(ele6).perform();// 断言检查是否修改成功 (修改成功后跳转到登录页面)wait.until(ExpectedConditions.urlToBe("http://127.0.0.1:58080/sign-in.html"));String ele7 = driver.getCurrentUrl();assert ele7.equals("http://127.0.0.1:58080/sign-in.html");}// 密码修改失败 (原密码输入错误)public void PasswordChangeFail1() {// 重新登录driver.findElement(By.cssSelector("#username")).sendKeys("zhangsan");driver.findElement(By.cssSelector("#password")).sendKeys("123");driver.findElement(By.cssSelector("#submit")).click();// 点击 "用户头像" 按钮, 进入选择进入用户个人中心界面driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.dropdown")).click();// 点击 "个人中心" 按钮, 进入个人中心driver.findElement(By.cssSelector("#index_user_settings")).click();// 找到原密码输入框WebElement ele8 = driver.findElement(By.cssSelector("#settings_input_oldPassword"));// 点击原密码输入框Actions actions = new Actions(driver);actions.click(ele8).perform();// 输入原密码actions.sendKeys("1234").perform();// 找到新原密码输入框WebElement ele9 = driver.findElement(By.cssSelector("#settings_input_newPassword"));// 点击新原密码输入框actions.click(ele9).perform();// 输入新原密码actions.sendKeys("123").perform();// 找到确认密码输入框WebElement ele10 = driver.findElement(By.cssSelector("#settings_input_passwordRepeat"));// 点击确认密码输入框actions.click(ele10).perform();// 输入确认密码actions.sendKeys("123").perform();// 点击修改密码按钮WebElement ele11 = driver.findElement(By.cssSelector("#settings_submit_password"));WebDriverWait wait = new WebDriverWait(driver,Duration.ofSeconds(1));wait.until(ExpectedConditions.elementToBeClickable(ele11));actions.click(ele11).perform();// 通过获取界面 url 来判断修改是否成功String ele12 = driver.getCurrentUrl();assert ele12.equals("http://127.0.0.1:58080/index.html");}// 密码修改失败 (确认密码错误)public void PassWordChangeFail2() {// 找到原密码输入框WebElement ele13 = driver.findElement(By.cssSelector("#settings_input_oldPassword"));// 点击原密码输入框Actions actions = new Actions(driver);actions.click(ele13).perform();// 输入原密码actions.sendKeys("123").perform();// 找到新原密码输入框WebElement ele14 = driver.findElement(By.cssSelector("#settings_input_newPassword"));// 点击新原密码输入框actions.click(ele14).perform();// 输入新原密码actions.sendKeys("123").perform();// 找到确认密码输入框WebElement ele15 = driver.findElement(By.cssSelector("#settings_input_passwordRepeat"));// 点击确认密码输入框actions.click(ele15).perform();// 输入确认密码actions.sendKeys("1234").perform();// 点击修改密码按钮WebElement ele16 = driver.findElement(By.cssSelector("#settings_submit_password"));WebDriverWait wait = new WebDriverWait(driver,Duration.ofSeconds(1));wait.until(ExpectedConditions.elementToBeClickable(ele16));actions.click(ele16).perform();// 通过获取界面 url 来判断修改是否成功String ele17 = driver.getCurrentUrl();assert ele17.equals("http://127.0.0.1:58080/index.html");}
}

MyPostsPage.java 

/*
我的帖子页测试*/
public class MyPostsPage extends Utils {public static String url = "http://127.0.0.1:58080/index.html";public MyPostsPage() {super(url);}// 进入我的帖子测试public void MyPostEter() {// 需要重新登录// 返回登录页并刷新页面driver.navigate().refresh();// 重新登录driver.findElement(By.cssSelector("#username")).sendKeys("zhangsan");driver.findElement(By.cssSelector("#password")).sendKeys("123");driver.findElement(By.cssSelector("#submit")).click();// 点击 "用户头像" 按钮, 进入选择进入用户我的帖子界面driver.findElement(By.cssSelector("body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.dropdown")).click();// 点击 "我的帖子" 按钮, 进入个人中心driver.findElement(By.cssSelector("#index_user_profile")).click();// 获取界面元素WebElement ele1 = driver.findElement(By.cssSelector("#profile_article_body > li:nth-child(1) > div.card.timeline-event-card > div > div > div > div > div.text-truncate"));WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(1));wait.until(ExpectedConditions.elementToBeClickable(ele1));String ele2 = driver.findElement(By.cssSelector("#bit-forum-content > div.page-body > div > div > div.col-lg-4 > div > div > div > div > h2")).getText();// 断言判断是否成功进入assert ele2.equals("个人介绍");}// 点击帖子进入帖子详情页// 因为帖子是动态变化的, 无法选中标题从而进入, 人为测试正常, 自动化测试脚本跳过public void PostEnterSuc() {// 选择一篇帖子标题, 点击进入driver.findElement(By.cssSelector("#profile_article_body > li:nth-child(1) > div.card.timeline-event-card > div > div > div > div > div.text-truncate")).click();// 获取页面元素WebElement ele3 = driver.findElement(By.cssSelector("#details_btn_like_count"));WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(1));wait.until(ExpectedConditions.elementToBeClickable(ele3));// 断言判断是否进入assert ele3.equals("点赞");}
}

PageByNoLogin.java 

/*
所以没有登录界面的测试*/
public class PageByNoLogin extends Utils {public static String url = "http://127.0.0.1:58080/index.html";public PageByNoLogin() {super(url);}// 判断是否为未登录状态public void ByNoLogin() throws InterruptedException {// 刷新页面driver.navigate().refresh();// 获取界面 urlThread.sleep(1000);String url1 = driver.getCurrentUrl();System.out.println(url1);// 断言判断是否为登录页面的 urlWebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(3));wait.until(ExpectedConditions.urlToBe("http://127.0.0.1:58080/sign-in.html"));assert url1.equals("http://127.0.0.1:58080/sign-in.html");}
}

// 部分代码在其他设备上运行可能会出现报错, 我们可以在评论区交流

2. 编写自动化脚本过程问题总结

2.1 Actons 类的使用

// 有些输入框是不能直接交互的, 需要点击之后才能输入, 这时候就需要用到 Actions 类中的一些方法

// 例如: click() , keyDown(Keys.)

// 使用这些方法运行时可能肉眼看不出效果, 我们可以加 .perform() 和 强制等待

// 使用 keyDown(Keys.Enter) 操作键盘 Enter 键时, 最好搭配其他 Alt 键一起使用, 否则可能会报错

// 不能删除原有内容

// 使用 Actions中的 DELETE, CLEAR, BACK_SPACE 清除内容失败的时候, 可以使用 CONTROL + "a"  (有时 "A" 会识别错误)选中之后输入空的字符串的方式来清空输入框

// 一般按钮用 driver 的 click() 方法更好, 输入框用 Actions.click() 更好

2.2 等待的使用

 // 在获取新界面的 URL 或者元素之前最好加一个等待, 避免因界面没有渲染完全导致获取不到元素而报错

// 在发布新帖的时候用了一次显式等待, 结果发布按钮执行太快了, 前面的输入操作来不及执行完

2.3 页面操作

// 点击方式有两种, 可以在界面定位元素后在界面实现点击, 也可以通过Action模拟鼠标点击

项目性能测试

1. 进行性能测试

 

2. 生成测试报告 

//点击查看生成的性能测试报告

 

项目总结

1. 部分使用缺陷总结

2. 测试结论与建议

相关文章:

自动化测试项目2 --- 比特纵横 [软件测试实战 Java 篇]

目录 项目介绍 项目源码 库地址 项目功能测试 1. 自动化实施步骤 1.1 编写测试用例 1.2 自动化测试脚本开发 1.2.1 配置相关环境, 添加依赖 1.2.2 代码编写 2. 编写自动化脚本过程问题总结 2.1 Actons 方法的使用 2.2 等待的使用 2.3 页面操作 项目性能测试 1. 进…...

【学习笔记】深入理解Java虚拟机学习笔记——第1章 走进Java

第1章 走进Java 1.1 概述 Java成功的原因 1>一次编写到处运行 2>内存管理安全,自动回收 3>运行时编译 4>强大成熟的第三方库 1.2 Java技术体系 1>Java技术体系组成: -Java语言 -Java虚拟机实现 -class文件格式 -Java类库API -第三方J…...

JavaScript性能优化实战之运行时性能优化

在 JavaScript 开发中,运行时性能优化是确保网页响应迅速和流畅的重要环节。优化运行时性能不仅能提高用户体验,还能在高并发的情况下保证应用的稳定性。本文将细化几个常见的 JavaScript 运行时性能优化策略,帮助你提高代码执行效率。 1️⃣ 避免不必要的内存分配和释放 J…...

走进AI的奇妙世界:探索历史、革命与未来机遇

2022年11月30日,ChatGPT的横空出世像一枚深水炸弹,掀起了全球范围的AI狂潮。但这场革命并非偶然——它背后是80年AI发展史的厚积薄发。从图灵的哲学思辨到深度学习的技术突破,再到生成式AI的“涌现”时刻,AI正以惊人的速度模糊人机…...

用c 编写的笔记搜索程序

{XXX文本记录} 文本记录格式 xxx 搜索词条 #include <stdio.h> #include <string.h> #include <stdlib.h>int main(void){FILE *ffopen("help.txt","r");if(fNULL){perror("file");return -1;}char nr[2000];f…...

鼎讯信通 智能通信干扰设备:多频段多模态信号压制解决方案

在万物互联时代&#xff0c;通信安全已成为现代社会的核心基础设施防护重点。面对日益复杂的电磁环境挑战&#xff0c;新一代智能通信干扰设备通过技术创新实现了信号压制能力的革命性突破。本文将深入解析该设备的八大核心功能与技术特性&#xff0c;展现其在商业通信保障、工…...

软件测试概念

这里写目录标题 需求开发模型软件生命周期瀑布模型螺旋模型增量模型、迭代模型敏捷模型Scrum 测试模型V模型W模型&#xff08;双V模型&#xff09; 需求 用户需求&#xff1a;没有经过合理的评估&#xff0c;通常就是一句话 软件需求&#xff1a;是开发人员和测试人员执行工作…...

数据库性能杀手与调优实践

目录 前言一、索引缺失引发的全表扫描灾难1.现象与影响2.优化策略 二、SELECT * 的隐性成本1.危害分析2.优化实践 三、分页查询的性能陷阱1.深度分页问题2.优化方案对比 四、执行计划分析方法论1.关键指标解读2.典型劣化模式识别 五、综合优化最佳实践总结 前言 在数据库应用开…...

初始化列表详解

1.类中包含以下成员&#xff0c;必须放在初始化列表位置进行初始化&#xff1a; 1. 引用成员变量 2.const成员变量 3. 自定义类型成员(且该类没有默认构造函数时 ) 2. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序&#xff0c;与其在初始化列表中的先后次序无关…...

【CVE-2025-1094】:PostgreSQL 14.15 SQL注入漏洞导致的RCE_ 利用代码和分析

目标 PostgreSQL 14.15BeyondTrust Privileged Remote Access (PRA) 和 Remote Support (RS) 软件受影响的版本:使用PostgreSQL 14.15及其版本的BeyondTrust产品Explain CVE-2025-1094 是 PostgreSQL 14.15 版本的 psql 交互式工具中发现的 SQL 注入漏洞。由于输入值的验证不…...

【验证技能】VIP项目大总结

VIP项目快做一段落了&#xff0c;历时一年半&#xff0c;也该要一个大汇总。 VIP简介 VIP开发流程 VIP难点 进程同步 打拍插入不同bit位宽数据问题。 动态升降lane VIP做的不好的地方和改进想法 各层之间交互 testsuite两端关键 所有层的实现架构不统一 VIP经验 ** 架构…...

MyBatis 参数处理全解析

在 Java 开发领域&#xff0c;MyBatis 作为一款优秀的持久层框架&#xff0c;凭借其简洁的设计和强大的功能&#xff0c;受到了广大开发者的青睐。而参数处理作为 MyBatis 中一个至关重要的环节&#xff0c;掌握好它能让我们更高效地使用 MyBatis 进行数据库操作。本文将全面深…...

【自然语言处理与大模型】使用Xtuner进行QLoRA微调实操

本文首先对Xtuner这一微调框架进行简单的介绍。手把手演示如何使用Xtuner对模型进行微调训练&#xff0c;包括数据准备、训练命令执行及训练过程中的监控技巧。最后&#xff0c;在完成微调之后&#xff0c;本文还将介绍如何对微调结果进行简单对话测试。 一、Xtuner微调框架 X…...

2023华为od统一考试B卷【二叉树中序遍历】

前言 博主刷的华为机考题&#xff0c;代码仅供参考&#xff0c;因为没有后台数据&#xff0c;可能有没考虑到的情况 如果感觉对你有帮助&#xff0c;请点点关注点点赞吧&#xff0c;谢谢你&#xff01; 题目描述 思路 0.用Character数组存储树&#xff0c;index下标的左右…...

Midjourney 绘画 + AI 配音:组合玩法打造爆款短视频!

一、引言:AI 重构短视频创作范式 在某短视频工作室的深夜剪辑室里,资深编导正在为一条古风剧情视频发愁:预算有限无法实拍敦煌场景,人工绘制分镜耗时 3 天,配音演员档期排到一周后。而使用 Midjourney 生成敦煌壁画风格的场景图仅需 15 分钟,AI 配音工具实时生成多角色台…...

敏感词 v0.25.1 新特性之返回匹配词,修正 tags 标签

开源项目 敏感词核心 https://github.com/houbb/sensitive-word 敏感词控台 https://github.com/houbb/sensitive-word-admin 版本特性 大家好&#xff0c;我是老马。 敏感词以前在实现的时候&#xff0c;没有返回底层实际匹配的词&#xff0c;有时候问题排查非常耗费时间。 …...

【多线程】六、基于阻塞队列的生产者消费者模型

文章目录 Ⅰ. 生产者消费者模型的概念Ⅱ. 生产者消费者模型的优点Ⅲ. 基于阻塞队列的生产者消费者模型MakefileBlock_queue.hpptask.hpptest.cppⅣ. 如何理解提高了效率❓❓❓Ⅰ. 生产者消费者模型的概念 ​ 生产者消费者模型是一种常见的并发模式,用于解决生产者和消费者之间…...

解决Flutter项目中Gradle构建Running Gradle task ‘assembleDebug‘卡顿问题的终极指南

解决Flutter项目中Gradle构建Running Gradle task ‘assembleDebug‘卡顿问题的终极指南 前言 在开发Flutter应用时,经常会遇到Gradle构建卡在Running Gradle task assembleDebug阶段的问题。本文将分享如何通过配置华为云镜像和使用自定义脚本下载依赖的方法解决这些问题。…...

IntelliJ IDEA 保姆级使用教程

文章目录 一、创建项目二、创建模块三、创建包四、创建类五、编写代码六、运行代码注意 七、IDEA 常见设置1、主题2、字体3、背景色 八、IDEA 常用快捷键九、IDEA 常见操作9.1、类操作9.1.1、删除类文件9.1.2、修改类名称注意 9.2、模块操作9.2.1、修改模块名快速查看 9.2.2、导…...

从此,K8S入门0门槛!

前言 当你想要入门K8S的时候&#xff0c;往往会被各种概念搞的晕乎乎的&#xff0c;什么API Server&#xff0c;Scheduler&#xff0c;Controller manager&#xff0c;Etcd&#xff0c;Pod&#xff0c;Kubelet&#xff0c;kube-proxy&#xff0c;deployment…… 哪怕你使用了…...

vue2和vue3组件如何监听子组件生命周期

在 Vue 中监听子组件的生命周期是一个常见需求&#xff0c;但 Vue 官方并不直接推荐这么做&#xff0c;因为这会打破组件的封装性。但在**一些特定场景&#xff08;如自动化监控、封装逻辑复用&#xff09;**下仍是有意义的。 下面分别讲解 Vue 2 和 Vue 3 中如何监听 子组件的…...

如何用Python绘制两个圆之间的8条公切线

引言 在几何学中&#xff0c;两圆之间存在多种类型的公共切线。本文将通过Python代码演示如何绘制两个同心圆&#xff08;半径分别为1.0和3.0&#xff09;之间的8条公切线&#xff0c;并解释相关数学原理与代码实现细节。 环境准备 import matplotlib.pyplot as plt import …...

会话历史管理——持久化

​​需求场景​​​​推荐方案​​​​理由​​中小企业级应用&#xff0c;需复杂查询MySQL/PostgreSQL事务支持完善&#xff0c;开发成本低海量数据高并发写入Cassandra水平扩展性强&#xff0c;写入性能高非结构化历史数据快速检索MongoDB灵活存储&#xff0c;内置全文检索本…...

C++之IO流

目录 一、C语言的输入与输出 二、流是什么 三、CIO流 3.1、C标准IO流 3.2、C文件IO流 四、stringstream的简单介绍 一、C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键盘)读取数据&#xff0c;并将值存放…...

maven install时报错:【无效的目标发行版: 17】

在很多次运行项目前的maven install时&#xff0c;我总是遇到无效的目标发行版: 17的问题&#xff0c;解决过之后就又忘了怎么解决&#xff0c;浪费了很多时间。。 今天把他总结一下&#xff0c;如图报错&#xff1a; 解决方法 注意&#xff1a; 如果只想解决这个项目的问题…...

开闭原则(OCP)

非常棒的问题&#xff01;&#x1f50d; 开闭原则&#xff08;OCP, Open/Closed Principle&#xff09;是软件设计的核心原则之一&#xff0c;下面我将从定义、意义、优劣分析、Python示例和结构图五个方面完整解析给你。 &#x1f9e0; 什么是开闭原则&#xff1f; 开闭原则&a…...

FHQ Treap

按值分裂 /* 按值x分裂Treap&#xff1a;将树u分裂为<x的树l和>x的树r */ void split(int u, int x, int& l, int& r) {if (!u) { l r 0; return; } // 空树直接返回if (t[u].val < x) { // 当前节点值<x&#xff0c;应放入左树l u; …...

题解传送门

做个算法分类&#xff0c;这样找特定算法的题目就方便多了23333 竞赛工具 【竞赛工具】——sublime text4 xcpc竞赛向配置教程 【竞赛工具】——vscode xcpc竞赛向配置教程 算法讲解 [算法学习]——通过RMQ与dfs序实现O(1)求LCA&#xff08;含封装板子&#xff09; [算法…...

ASP.NET MVC​ 入门与提高指南七

39. 量子安全通信与 MVC 应用保障 39.1 量子安全通信概念 量子安全通信基于量子力学原理&#xff0c;利用量子态的特性&#xff08;如量子纠缠、量子不可克隆定理&#xff09;来实现信息的安全传输。与传统加密方式相比&#xff0c;量子安全通信能够提供更高的安全性&#xf…...

Linux工作台文件操作命令全流程解析

全文目录 1 确认当前工作路径2 导航与目录管理2.1 关键命令2.2 逻辑衔接 3 文件基础操作3.1 创建 → 备份 → 重命名 → 清理3.2 文件查看和编辑3.3 文件链接3.4 文件diff 4 文件权限与所有权管理5 文件打包与归档6 参考文献 写在前面 shell是一种命令解释器&#xff0c;它提供…...

03 - spring security自定义登出页面

spring security自定义登出页面 文档 00 - spring security框架使用01 - spring security自定义登录页面02 - spring security基于配置文件及内存的账号密码 自定义登出页面 调整配置类WebSecurityConfig.java package xin.yangshuai.springsecurity03.config;import org.…...

unity webgl netbox2本地部署打开运行

unity webgl netbox2本地部署打开运行 复制NetBox2.exe和index.html 在同一级目录下使用&#xff0c;双击netbox2.exe。 下载文件 下载地址&#xff1a; netbox2.exe...

华为OD机试真题 Java 实现【水库蓄水问题】

前言 博主刷的华为机考题&#xff0c;代码仅供参考&#xff0c;因为没有后台数据&#xff0c;可能有没考虑到的情况 如果感觉对你有帮助&#xff0c;请点点关注点点赞吧&#xff0c;谢谢你&#xff01; 题目描述 思路 1. 其实就是找一个最大的水坑&#xff0c;两个…...

A2A 协议与 MCP 协议:智能代理生态系统的双轮驱动

本文将探讨A2A 协议与MCP 协议的特点、区别及协同作用&#xff0c;帮助开发者和产品设计师更好地理解这一新兴技术领域。 A2A 协议&#xff1a;打造代理间的沟通桥梁 A2A 协议的核心概念 A2A 协议是由 Google 与 50 多家行业合作伙伴共同开发的开放协议&#xff0c;旨在实现…...

使用AI-01开发板和开源后端服务搭建整套小智服务系统

使用AI-01开发板和开源后端服务搭建整套小智服务系统 四博智联的AI-01开发板&#xff0c;基于乐鑫ESP32-C2 专属定制的离线语音模组&#xff0c;能够完美的接入小智AI服务平台&#xff0c;再使用开源后端服务&#xff0c;就能够搭建一个完整的小智AI服务系统了。 下面是具体…...

第三章 权限维持-linux权限维持-隐藏

简介 ssh rootenv.xj.edisec.net -p 密码 xjqxwcyc 1.黑客隐藏的隐藏的文件 完整路径md5 2.黑客隐藏的文件反弹shell的ip端口 {ip:port} 3.黑客提权所用的命令 完整路径的md5 flag{md5} 4.黑客尝试注入恶意代码的工具完整路径md5 5.使用命令运行 ./x.xx 执行该文件 将查询的…...

Linux操作系统系统编程:x86-64架构下的系统调用

在Linux操作系统里&#xff0c;系统编程如同精密仪器的核心部件&#xff0c;掌控着系统运行的关键。而 x86-64 架构下的系统调用&#xff0c;更是连接用户空间程序与内核的关键桥梁。你可以把用户空间的程序想象成一个个 “工匠”&#xff0c;它们有着各式各样的需求&#xff0…...

linux下如何在一个录目中将一个文件复制到另一个录目,删除目录

一.文件复制到另一个目录 在Linux系统中&#xff0c;要将一个文件从一个目录复制到另一个目录&#xff0c;你可以使用cp命令。下面是一些基本的用法&#xff1a; 1. 使用绝对路径 如果你知道文件的绝对路径和目标目录的绝对路径&#xff0c;你可以直接使用cp命令。例如&…...

用Selenium开启自动化网页交互与数据抓取之旅

用Selenium开启自动化网页交互与数据抓取之旅 在当今数字化时代&#xff0c;数据的价值不言而喻&#xff0c;而网页作为海量数据的重要载体&#xff0c;如何高效获取其中的关键信息成为众多开发者和数据爱好者关注的焦点。Selenium这一强大工具&#xff0c;为我们打开了自动化…...

RabbitMQ的交换机

一、三种交换机模式 核心区别对比​​ ​​特性​​​​广播模式&#xff08;Fanout&#xff09;​​​​路由模式&#xff08;Direct&#xff09;​​​​主题模式&#xff08;Topic&#xff09;​​​​路由规则​​无条件复制到所有绑定队列精确匹配 Routing Key通配符匹配…...

多模态大语言模型arxiv论文略读(五十五)

MoMA: Multimodal LLM Adapter for Fast Personalized Image Generation ➡️ 论文标题&#xff1a;MoMA: Multimodal LLM Adapter for Fast Personalized Image Generation ➡️ 论文作者&#xff1a;Kunpeng Song, Yizhe Zhu, Bingchen Liu, Qing Yan, Ahmed Elgammal, Xiao…...

TMI投稿指南(四):投稿相关网址

TMI官网&#xff1a;https://ieeetmi.org/ 模版选择器&#xff1a;选择合适的latex模版 IEEE-Template Selector 评审过程状态查看&#xff1a;​​​​​​ScholarOne Manuscripts AE assigns reviewers&#xff1a;副编辑已经开始选择和邀请审稿人&#xff0c;但同意审稿…...

Oracle无法正常OPEN(三)

在Oracle数据库中&#xff0c;如果几个数据文件丢失&#xff0c;导致数据库无法启动&#xff0c;报错“ORA-01157: cannot identify/lock data file 2 - see DBWR trace file”&#xff0c;如果没有物理备份的情况下&#xff0c;位于丢失数据文件的数据是无法找回的&#xff0c…...

SQL语句练习 自学SQL网 在查询中使用表达式 统计

目录 Day 9 在查询中使用表达式 Day 10 在查询中进行统计 聚合函数 Day 11 在查询中进行统计 HAVING关键字 Day12 查询执行顺序 Day 9 在查询中使用表达式 SELECT id , Title , (International_salesDomestic_sales)/1000000 AS International_sales FROM moviesLEFT JOIN …...

当LLM遇上Agent:AI三大流派的“复仇者联盟”

你一定听说过ChatGPT和DeepSeek&#xff0c;也知道它们背后的LLM&#xff08;大语言模型&#xff09;有多牛——能写诗、写代码、甚至假装人类。但如果你以为这就是AI的极限&#xff0c;那你就too young too simple了&#xff01; 最近&#xff0c;**Agent&#xff08;智能体&a…...

模拟开发授权平台

这次只是实现应用的curd和公私钥的校验以及第三方的通知dmeo项目&#xff0c;大家可以拓开视野来编写 进入主题 项目链接&#xff1a;桌角的眼镜/develop_auth_platform 直接下拉并运行就行 回调应用代码在test包中 回调应用测试代码 package mainimport ("encoding/…...

python数据分析(八):Pandas 文本数据处理

Pandas 文本数据处理全面指南 1. 引言 在数据分析中&#xff0c;文本数据是常见的数据类型之一。Pandas 提供了强大的字符串处理方法&#xff0c;可以方便地对文本数据进行各种操作。本文将详细介绍 Pandas 中的文本处理功能&#xff0c;包括字符串连接(cat)、分割(split)、替…...

Spring AI:简化人工智能功能应用程序开发

Spring AI&#xff1a;简化人工智能功能应用程序开发 一、项目简介 Spring AI 项目致力于简化包含人工智能功能的应用程序的开发工作&#xff0c;并且不会引入不必要的复杂性。该项目从著名的 Python 项目&#xff08;如 LangChain 和 LlamaIndex&#xff09;中获取灵感&#…...

【算法基础】三指针排序算法 - JAVA

一、基础概念 1.1 什么是三指针排序 三指针排序是一种特殊的分区排序算法&#xff0c;通过使用三个指针同时操作数组&#xff0c;将元素按照特定规则进行分类和排序。这种算法在处理包含有限种类值的数组时表现出色&#xff0c;最经典的应用是荷兰国旗问题&#xff08;Dutch …...

从实列中学习linux shell9 如何确认 服务器反应迟钝是因为cpu还是 硬盘io 到底是那个程序引起的。cpu负载多高算高

在 Linux 系统中,Load Average(平均负载) 是衡量系统整体压力的关键指标,但它本身没有绝对的“高/低”阈值,需要结合 CPU 核心数 和 其他性能指标 综合分析。以下是具体判断方法: 一、Load Average 的基本含义 定义:Load Average 表示 单位时间内处于可运行状态(R)和不…...