使用SSM框架实现个人博客管理平台以及实现Web自动化测试

文章目录

  • 前言
    • 1. 项目概述
    • 2. 项目需求
      • 2.1功能需求
      • 2.2 其他需求
      • 2.3 系统功能模块图
    • 3. 开发环境
    • 4. 项目结构
    • 5. 部分功能介绍
      • 5.1 数据库密码密文存储
      • 5.2 统一数据格式返回
      • 5.3 登录拦截器
    • 6. 项目展示
    • 7. 项目测试
      • 7.1 测试用例
      • 7.2 执行部分自动化测试用例

前言

在几个月前实现了一个servlet版本的博客系统,本项目则是在原有基础上进行升级。使用SSM框架、数据库密码使用加盐算法加密、使用Redis对session进行持久化存储从而支持分部式部署、通过拦截器实现用户登录校验、对于数据格式返回以及异常处理进行统一功能处理、增加分页以及点击量统计等功能。

1. 项目概述

用户进入主页可以看到他人发布的博客,同时也可以注册账号通过本平台编写发布博客,平台支持markdown编辑;用户也可以在个人中心修改个人信息,同时对博客可以随写随存以草稿形式,之后也可以发布修改草稿。用户可以通过博客的点击量,判断博客质量等等。

2. 项目需求

2.1功能需求

  • 登录与注册功能
  • 个人中心(查看、修改个人信息)
  • 查看博客详情
  • 编写个人博客
  • 修改个人博客
  • 删除个人博客
  • 草稿箱(保存博客为草稿,修改博客为草稿,发布草稿为博客,草稿增删改查)
  • 博客列表分页查询功能
  • 博客阅读量统计
  • 注销功能

2.2 其他需求

  • 系统界面有良好的视觉体验,营造美观舒适的界面;
  • 操作简单流畅,无可视卡顿;
  • 有良好的引导提示,增强用户体验;

2.3 系统功能模块图

在这里插入图片描述

3. 开发环境

1.前端开发:
技术:HTML、CSS、JavaScript
工具:IntelliJ IDEA 2021.3.1
2.后端开发:
服务器:tomcat-8.5.50
数据库:MySQL5.7
开发语言:Java
技术框架:SpringBoot、SpringMVC、Mybatis、Redis
管理工具:Maven
开发工具:Intellij IDEA 2020.1.4
操作系统:Windows10

4. 项目结构

Java源代码位于com.example.blog_system中;

  1. common包:设置统一数据格式返回代码(AjaxResult)、全局变量用于session存储(AppVariable)、密码加盐已经密码验证(PasswordUtils)、获取当前用户(UserSessionUtils)。
  2. config包:系统配置文件(Appconfig)用于设置登录拦截器规则、登录拦截器的实现(LoginIntercept)、数据返回增强,校验是否是统一格式返回不是则封装为统一格式(ResponseAdvice)
  3. controller包:控制层;连接页面请求和服务层,获取页面请求的参数,通过自动装配,映射不同的URL到相应的处理函数,并获取参数,对参数进行处理,之后传给服务层。关于博客文章的控制器(ArticleController),实现博客以及草稿的增删改查以及列表分页点击量统计等功能、关于用户的控制器(UserController)实现增删改查功能。
  4. entity包:实体类,定义文章以及用户属性,同时定义视图UserVo的属性。
  5. service包:服务层;为控制层提供服务,接受控制层的参数,完成相应的功能,并返回给控制层;
  6. mapper包:持久层;根据service相关功能定义与数据库相关的增删改查方法。

在这里插入图片描述

在resources包中保存着系统资源,其中:

  1. mapper:根据 com.example.blog_system.mapper中的方法实现sql语句的增删改查。
  2. static:存储系统的前端页面HTML,CSS样式,markdown编辑器,jquery等相关资源。

5. 部分功能介绍

5.1 数据库密码密文存储

基于spring框架提供的md5加密的基础上通过UUID生成随机盐值,对用户密码进行加密;

加密过程如下:

//1.加盐并生成密码public static String encrypt(String password){//1.生成盐值String salt = UUID.randomUUID().toString().replace("-","");//2.生成加盐之后的密码String saltpassword = DigestUtils.md5DigestAsHex((salt+password).getBytes());//3。生成最终密码,32位盐值+"$"+加盐密码String finalpassword = salt+"$"+saltpassword;retur

在进行用户登录密码校验中,密码解密过程,从数据库取出密码,得到盐值,根据盐值以及用户输入密码进行加密,校验与数据库存储密码是否相同。

具体实现如下:

//2.生成最终密码(数据库中的,便于密码验证)public static String encrypt(String password,String salt){//2.通过盐值和原始密码生成加盐之后的密码String saltpassword = DigestUtils.md5DigestAsHex((salt+password).getBytes());
//        最终密码String finalPassword = salt+"$"+saltpassword;return finalPassword;}//3.验证密码/**** @param inputPassword 用户输入密码* @param finalPassword 数据库存储密码* @return*/public static boolean check(String inputPassword,String finalPassword){//先进性非空校验和长度校验if (StringUtils.hasLength(inputPassword)&&StringUtils.hasLength(finalPassword)&&finalPassword.length()==65){//1.得到盐值,从finalPassword,对$加上\\进行转义,它是关键字String salt = finalPassword.split("\\$")[0];//2.使用之前加密步骤将明文加密并生成最终密码String confinmrPassword =  PasswordUtils.encrypt(inputPassword,salt);//3,进行比较return confinmrPassword.equals(finalPassword);}return false;}

5.2 统一数据格式返回

定义数据格式为状态码+状态码描述信息+返回数据,实现成功以及失败的返回。

具体实现如下:

package com.example.blog_system.common;import lombok.Data;import java.io.Serializable;/*** 统一数据格式返回*/
@Data
public class AjaxResult implements Serializable {// 状态码private Integer code;// 状态码描述信息private String msg;// 返回的数据private Object data;/*** 操作成功返回的结果*/public static AjaxResult succecc(Object data) {AjaxResult result = new AjaxResult();result.setCode(200);result.setMsg("");result.setData(data);return result;}public static AjaxResult succecc(int code, Object data) {AjaxResult result = new AjaxResult();result.setCode(code);result.setMsg("");result.setData(data);return result;}public static AjaxResult succecc(int code, String msg, Object data) {AjaxResult result = new AjaxResult();result.setCode(code);result.setMsg(msg);result.setData(data);return result;}/*** 返回失败结果*/public static AjaxResult fail(int code, String msg) {AjaxResult result = new AjaxResult();result.setCode(code);result.setMsg(msg);result.setData(null);return result;}public static AjaxResult fail(int code, String msg, Object data) {AjaxResult result = new AjaxResult();result.setCode(code);result.setMsg(msg);result.setData(data);return result;}}

5.3 登录拦截器

继承HandlerInterceptor实现一个登录拦截器,判断在用户进入主页、登录页、注册页、以外的页面进行相关操作时判断是否登录,未登录则自动跳转到登录页面。

具体实现如下:

/**登录拦截器* @author zq* @date 2023-07-22 22:33*/public class LoginIntercept implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HttpSession session = request.getSession(false);if(session!=null&&session.getAttribute(AppVariable.USER_SESSION_KEY)!=null){//用户已登录return true;}//跳转到登录页面response.sendRedirect("/login.html");return false;}
}

6. 项目展示

主页:
在这里插入图片描述

我的博客列表页:
在这里插入图片描述

博客编辑页:
在这里插入图片描述

草稿列表页:
在这里插入图片描述

草稿编辑页:
在这里插入图片描述

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

修改个人信息:
在这里插入图片描述

登录页面:
在这里插入图片描述
注册页面:
在这里插入图片描述

7. 项目测试

接下来将对个人博客管理平台功能以及界面进行测试,编写Web自动化测试用例。

7.1 测试用例

编写的测试用例如下:

在这里插入图片描述

7.2 执行部分自动化测试用例

登录功能:执行登录测试用例检验结果是否符合预期、界面是否符合预期;

检测页面是否正确加载:

  /*** 打开网页*/@Test@Order(1)public  void openWeb() throws InterruptedException {driver.get("http://124.221.76.124:59090/login.html");sleep(3000);}//检测页面是否正常打开,元素是否存在@Test@Order(2)public void elementAppear(){driver.findElement(By.cssSelector("body > div.login-container > div > h3"));driver.findElement(By.cssSelector("#username"));driver.findElement(By.cssSelector("#password"));driver.findElement(By.cssSelector("#submit"));}

测试结果如下:符合预期

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

检测用户名密码错误是否登录失败:

  @ParameterizedTest@CsvSource({"admin,12345"})@Order(2)//登录异常检测public void loginAbnormalTest(String username, String password) throws IOException, InterruptedException {//先清除用户名和密码框driver.findElement(By.cssSelector("#username")).clear();driver.findElement(By.cssSelector("#password")).clear();//输入账号和密码driver.findElement(By.cssSelector("#username")).sendKeys(username);driver.findElement(By.cssSelector("#password")).sendKeys(password);driver.findElement(By.cssSelector("#submit")).click();//期望结果和实际结果String expect = "登录失败,用户名或密码错误请重试";String actual = driver.findElement(By.cssSelector("body")).getText();Assertions.assertEquals(expect, actual);sleep(5000);}

测试结果如下:符合预期

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

检测用户名已经密码正确是否登录成功并且跳转到正确的用户下

/***登录正常测试*/@ParameterizedTest@CsvSource({"admin,123456"})@Order(3)public void loginNormalTest(String username, String password) throws IOException, InterruptedException {//先清除用户名和密码框driver.findElement(By.cssSelector("#username")).clear();driver.findElement(By.cssSelector("#password")).clear();//输入用户名和密码并点击登录driver.findElement(By.cssSelector("#username")).sendKeys(username);driver.findElement(By.cssSelector("#password")).sendKeys(password);driver.findElement(By.cssSelector("#submit")).click();//查看跳转页面是否是博客列表页sleep(3000);String url = "http://124.221.76.124:59090/myblog_list.html";Assertions.assertEquals(url,driver.getCurrentUrl());// 校验当前登录的用户是不是admin,如果是测试通过,否则测试不通过String user_name = driver.findElement(By.cssSelector("#username")).getText();driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);Assertions.assertEquals(username, user_name);sleep(5000);}

测试结果如下:符合预期
在这里插入图片描述

在这里插入图片描述

分页查询功能:执行分页查询功能的测试用例查看主页响应是否符合预期

检查首页的博客内容是否正确显示:

//检查首页的博客标题,内容是否正确显示@Test@Order(1)public void checkElements(){String actual = driver.findElement(By.cssSelector("#artListDiv > div:nth-child(1) > div.title")).getText();Assertions.assertEquals("保姆级自动化测试教程(Selenium+java)",actual);String actual1 = driver.findElement(By.cssSelector("#artListDiv > div:nth-child(1) > div.date")).getText();Assertions.assertEquals("2023-07-31",actual1);String actual2 = driver.findElement(By.cssSelector("#artListDiv > div:nth-child(1) > div.desc")).getText();Assertions.assertEquals("自动化测试指软件测试的自动化,在预设状态下运行应用程序或者系统,预设条件包括正常和异常,最后评估运行结果。将人为驱动的测试行为转化为机器",actual2);}

测试结果如下:符合预期
在这里插入图片描述
在这里插入图片描述

检查分页查询:首页、上一页、下一页、末页功能是否正常

//检查分页查询功能@Testpublic void checkCha() throws InterruptedException {//首页driver.findElement(By.cssSelector("body > div.container > div > div.blog-pagnation-wrapper > button:nth-child(1)")).click();sleep(3000);Alert alert = driver.switchTo().alert();String atext = alert.getText();Assertions.assertEquals("已经是第一页",atext);//点击确定关闭弹窗alert.accept();//上一页driver.findElement(By.cssSelector("body > div.container > div > div.blog-pagnation-wrapper > button:nth-child(2)")).click();sleep(3000);Alert alert2 = driver.switchTo().alert();String atext2 = alert.getText();Assertions.assertEquals("已经是第一页",atext2);alert2.accept();//下一页driver.findElement(By.cssSelector("body > div.container > div > div.blog-pagnation-wrapper > button:nth-child(3)")).click();sleep(3000);Alert alert3 = driver.switchTo().alert();String atext3 = alert.getText();Assertions.assertEquals("已经是最后一页",atext3);alert3.accept();//尾页driver.findElement(By.cssSelector("body > div.container > div > div.blog-pagnation-wrapper > button:nth-child(4)")).click();sleep(3000);Alert alert4 = driver.switchTo().alert();String atext4 = alert.getText();Assertions.assertEquals("已经是最后一页",atext4);alert4.accept();}

测试结果如下:符合预期
在这里插入图片描述

在这里插入图片描述

博客编辑功能:查看编辑发布博客、保存为草稿以及博客编辑页面是否正常显示

检查不输入标题时提示信息是否正确,检查输入标题发布文章时是否跳转到我的博客列表页

 @Test//@Order(2)//检查发布文章功能是否正常,分为输入为空、非空public void checkAdd() throws InterruptedException {sleep(3000);driver.findElement(By.cssSelector("body > div.nav > a:nth-child(5)")).click();sleep(3000);//1.输入为空driver.findElement(By.xpath("/html/body/div[2]/div[1]/button[1]")).click();sleep(3000);Alert alert1 = driver.switchTo().alert();String atext1 = alert1.getText();Assertions.assertEquals("确认提交?",atext1);alert1.accept();sleep(3000);Alert alert = driver.switchTo().alert();String atext = alert.getText();Assertions.assertEquals("请先输入标题!",atext);alert.accept();sleep(3000);//2,输入不为空时,是否正常发布,跳转到博客列表页driver.findElement(By.cssSelector("#title")).sendKeys("发布文章测试");sleep(3000);driver.findElement(By.xpath("/html/body/div[2]/div[1]/button[1]")).click();sleep(3000);Alert alert2 = driver.switchTo().alert();String atext2 = alert1.getText();Assertions.assertEquals("确认提交?",atext1);alert1.accept();sleep(3000);//不再继续提交Alert alert3 = driver.switchTo().alert();alert3.dismiss();sleep(3000);//验证当前页面是否是博客列表页面String url = driver.getCurrentUrl();Assertions.assertEquals("http://124.221.76.124:59090/myblog_list.html",url);}

测试结果如下:符合预期
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

同上述,检查保存为草稿时是否符合预期:
测试结果如下符合预期:
在这里插入图片描述
在这里插入图片描述

博客相关功能:删除、查看全文、修改
草稿相关功能:删除、查看全文、修改

测试代码与结果如下符合预期:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
篇幅有限,关于其他功能的测试在此省略,测试完整代码可以查看我的码云:https://gitee.com/zxxqqa/automated-testing

总结:根据测试用例结果基本符合预期可以确定个人博客管理平台的基本功能能够正确执行。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4 Promethues监控主机和容器

目录 目录 1. 监控节点 1.1 安装Node exporter 解压包 拷贝至目标目录 查看版本 1.2 配置Node exporter 1.3 配置textfile收集器 1.4 启动systemd收集器 1.5 基于Docker节点启动node_exporter 1.6 抓取Node Exporter 1.7 过滤收集器 2. 监控Docker容器 2.1 运行cAdviso…

【微软知识】微软相关技术知识分享

微软技术领域 一、微软操作系统: 微软的操作系统主要是 Windows 系列,包括 Windows 10、Windows Server 等。了解 Windows 操作系统的基本使用、配置和故障排除是非常重要的。微软操作系统(Microsoft System)是美国微软开发的Wi…

电商高并发设计之SpringBoot整合Redis实现布隆过滤器

文章目录 问题背景前言布隆过滤器原理使用场景基础中间件搭建如何实现布隆过滤器引入依赖注入RedisTemplate布隆过滤器核心代码Redis操作布隆过滤器验证 总结 问题背景 研究布隆过滤器的实现方式以及使用场景 前言 本篇的代码都是参考SpringBootRedis布隆过滤器防恶意流量击穿缓…

Data Structure, Algorithm,and Applications in C++

在学习这本书进阶内容之前,我们可以跟着它的第一章部分再巩固和复习。本书由Sartaj Sahni撰写,由王立柱和刘志红翻译。全书通俗易懂,内容丰富,是巩固C内容的不二选择。希望本文对各位有所帮助。 目录 1.函数与参数 1.1.传值参数…

wxwidgets Ribbon构建多个page与按钮响应

新建一个控制台应用程序,添加好头文件的依赖与lib库文件的依赖,修改属性: 将进入ribbon界面的文件与主界面的类分开: 1、RibbonSample.cpp #include "stdafx.h" #include "MyFrame.h" class MyApp : public…

flutter:轮播

前言 介绍几个比较有不错的轮播库 swipe_deck 与轮播沾边,但是更多的是一种卡片式的交互式界面设计。它的主要概念是用户可以通过左右滑动手势浏览不同的卡片,每张卡片上都有不同的信息或功能。 Swipe deck通常用于展示图片、产品信息、新闻文章、社…

【WebRTC---序篇】(七)RTC多人连麦方案

服务端可以选择mediasoup,作为SFU服务器,只负责转发数据 下图举例三个Client (browser或者客户端)同时加入一个房间,每个app同时发布一路视频和一路音频,并且接受来自其他app的音视频流,mediasoup内部的结构如下&…

使用Django自带的后台管理系统进行数据库管理的实例

Django自带的后台管理系统主要用来对数据库进行操作和管理。它是Django框架的一个强大功能,可以让你快速创建一个管理界面,用于管理你的应用程序的数据模型。 使用Django后台管理系统,你可以轻松地进行以下操作: 数据库管理&…

宝塔Linux面板Java项目一键部署(springboot)

部署项目之前请安装相关软件: jdk1.8、redis、nginx 、mysql 等等(项目中用到的) 1. 网站 2. 创建项目文件夹 文件 - /www/wwwroot - 新建项目文件夹 - 存放jar文件 3. 上传jar文件 (直接拖进来) 4. 添加Java项目 5. jar包路径 - 项目端口, 提交(启动项目) 6. 成功运行 7. 浏览…

.NET网络编程——TCP通信

一、网络编程的基本概念 : 1. 网络 就是将不同区域的电脑连接到一起,组成局域网、城域网或广域网。把分部在不同地理区域的计算机于专门的外部设备用通信线路 互联成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息&#xff0c…

eclipse版本与jdk版本对应关系

官网:Eclipse/Installation - Eclipsepedia eclipse历史版本(2007-):Older Versions Of Eclipse - Eclipsepedia Eclipse Packaging Project (EPP) Releases | Eclipse Packages

Spring Security 构建基于 JWT 的登录认证

一言以蔽之,JWT 可以携带非敏感信息,并具有不可篡改性。可以通过验证是否被篡改,以及读取信息内容,完成网络认证的三个问题:“你是谁”、“你有哪些权限”、“是不是冒充的”。 为了安全,使用它需要采用 …

亚马逊鲲鹏系统是怎么引流的?

亚马逊鲲鹏系统有三种引流方式,可设置通过亚马逊站点搜索、站外引流、直接访问产品页面进入到相关产品页面进行操作。 1、通过亚马逊站点搜索 正常的登录到我们的亚马逊主页,然后通过设置关键词及asin,最后进入你指定的产品,进行…

Redis的订阅者和发布者模式、主从双备和密码认证

四、Redis的订阅者和发布者模式、主从双备和密码认证 1、Redis的订阅者和发布者模式 两个数据库,一个是10,一个是15。订阅频道: 向频道推数据: 接收到数据: 2、redis的高可用(HA)主从双备 模拟…

基于传统检测算法hog+svm实现图像多分类

直接上效果图: 代码仓库和视频演示b站视频005期: 到此一游7758258的个人空间-到此一游7758258个人主页-哔哩哔哩视频 代码展示: 数据集在datasets文件夹下 运行01train.py即可训练 训练结束后会保存模型在本地 运行02pyqt.py会有一个可视化…

golang单元测试及mock总结

文章目录 一、前言1、单测的定位2、vscode中生成单测 二、构造测试case的注意事项1、项目初始化2、构造空interface{}3、构造结构体的time.Time类型4、构造json格式的test case 三、运行单测文件1、整体运行单测文件2、运行单个单测文件报错(1)command-l…

应用案例|基于3D视觉的高反光金属管件识别系统解决方案

Part.1 项目背景 在现代制造业中,高反光金属管件的生产以及质量的把控是一个重要的挑战。传统的2D视觉系统常常难以准确地检测和识别高反光金属管件,因为它们的表面特征不够明显,容易受到光照和阴影的干扰。为了应对这个问题,基于…

华为数通HCIP-IGMP(网络组管理协议)

IGMP(网络组管理协议) 作用:维护、管理最后一跳路由器以及组播接收者之间的关系; 应用:最后一跳路由器以及组播接收者之间; 原理:当组播接收者需要接收某个组别的流量时,会向最后…

kafka 理论知识

1 首先要了解kafka是什么 Kafka是一个分布式的消息订阅系统 1.1 kafka存储消息的过程 消息被持久化到一个topic中,topic是按照“主题名-分区”存储的,一个topic可以分为多个partition,在parition(分区)内的每条消息都有一个有序的id号&am…

高并发与性能优化的神奇之旅

作为公司的架构师或者程序员,你是否曾经为公司的系统在面对高并发和性能瓶颈时感到手足无措或者焦头烂额呢?笔者在出道那会为此是吃尽了苦头的,不过也得感谢这段苦,让笔者从头到尾去探索,找寻解决之法。 目录 第一站…