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

【软件测试】个人博客项目测试报告

目录

1.报告概要

2、测试环境

3、手动测试用例编写

4、自动化测试用例


1.报告概要

测试对象:基于SSM项目的博客系统。

测试目的:检测博客项目是否符合预期,并且对测试知识进行练习和巩固。

测试点:主要针对常用的功能进行测试如:博客项目的注册、登录、博客列表页、博客编辑页、个人列表页、导航栏等进行测试。

测试结果及结论:测试的常用功能全部正常,测试注册页面的密码时,发现密码没有限制长度以及强度的限制。

2、测试环境

硬件:Lenovo Y7000 2020

软件:Windows 、Google Chrome、IDEA

测试工具:自动化测试工具Selenium3

浏览器版本:Google Chrome  118.0.5993.118

3、手动测试用例编写

用户注册页 

用户登录页 

个人列表页

博客列表页(主页)

 

博客详情页

博客编辑页

4、自动化测试用例

博客注册页面

package Blog;import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.openqa.selenium.By;import java.util.concurrent.TimeUnit;import static java.lang.Thread.sleep;
//根据程序员指定的顺序执行
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class RegCases extends InitAndEnd{/** 注册成功* */@Order(1)@ParameterizedTest@CsvSource({"zhangsan,12345,12345"})void regSuccess(String username,String password,String conPassword ) throws InterruptedException {//打开主页面webDriver.get("http://154.8.139.1:48080/login.html");webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);//找到注册按钮webDriver.findElement(By.cssSelector("body > div.nav > a:nth-child(5)")).click();webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);sleep(3000);//输入注册的用户名,密码webDriver.findElement(By.cssSelector("#username")).sendKeys(username);webDriver.findElement(By.cssSelector("#password")).sendKeys(password);webDriver.findElement(By.cssSelector("#password2")).sendKeys(conPassword);webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);//点击提交webDriver.findElement(By.cssSelector("#submit")).click();sleep(3000);//处理弹窗String test = webDriver.switchTo().alert().getText();String except = "恭喜:注册成功!";webDriver.switchTo().alert().accept();Assertions.assertEquals(except,test);}/*** 用户名为空注册失败* */@Order(2)@ParameterizedTest@CsvSource(value = {"'',123,123","'',123,456","'',546,''","'','',456","'','',''","'张三','',''","'张三','123',''","'张三','123','123'",})void RegFail(String username,String password,String conPassword) throws InterruptedException {//打开主页webDriver.get("http://154.8.139.1:48080/login.html");webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);sleep(3000);//找到注册点击webDriver.findElement(By.cssSelector("body > div.nav > a:nth-child(5)")).click();webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);//输入用户名、密码、确认密码webDriver.findElement(By.cssSelector("#username")).sendKeys(username);webDriver.findElement(By.cssSelector("#password")).sendKeys(password);webDriver.findElement(By.cssSelector("#password2")).sendKeys(conPassword);webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);//点击提交webDriver.findElement(By.cssSelector("#submit")).click();webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);sleep(3000);//处理弹窗//获取弹窗信息String test = webDriver.switchTo().alert().getText();webDriver.switchTo().alert().accept();String except = "恭喜:注册成功!";//断言Assertions.assertNotEquals(except,test);}}

博客登录页面

import static java.lang.Thread.sleep;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BlogCases extends InitAndEnd{/** 输入正确的账号、密码登录成功* */@Order(1)@ParameterizedTest@CsvFileSource(resources = "LoginSuccess.csv")void LoginSuccess(String username,String password,String blog_list_url) throws InterruptedException {//打开博客登录页面webDriver.get("http://154.8.139.1:48080/login.html");webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);//输入账号adminwebDriver.findElement(By.cssSelector("#username")).sendKeys(username);webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);//输入密码123webDriver.findElement(By.cssSelector("#password")).sendKeys(password);webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);//点击提交按钮webDriver.findElement(By.cssSelector("#submit")).click();sleep(3000);//处理弹窗//用于接受警告对话框(确认)webDriver.switchTo().alert().accept();//跳转到列表页//获取到当前的页面的urlString cur_url = webDriver.getCurrentUrl();webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);//判断url是否和预期相等Assertions.assertEquals(blog_list_url,cur_url);}/** 输入错误的密码和账号* */@Disabled@Order(2)@ParameterizedTest@CsvSource(value = {"张帆,123","lisi,123","'',123456","张帆,''","'',''"})void LoginFail(String username,String password) throws InterruptedException {//打开博客登录页面webDriver.get("http://154.8.139.1:48080/login.html");webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);//输入账号和密码webDriver.findElement(By.cssSelector("#username")).sendKeys(username);webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);webDriver.findElement(By.cssSelector("#password")).sendKeys(password);webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);//点击提交按钮webDriver.findElement(By.cssSelector("#submit")).click();sleep(3000);//处理弹窗//获取弹窗内容String test = webDriver.switchTo().alert().getText();String except = "恭喜,登录成功!";//用于接受警告对话框(确认)webDriver.switchTo().alert().accept();//断言两个字符串的内容不相等,测试通过Assertions.assertNotEquals(except,test);System.out.println("登录不成功");}

个人列表页

    /** 博客列表页* */@Testvoid BlogList(){//打开博客列表页webDriver.get("http://154.8.139.1:48080/myblog_list.html");//获取页面上所有博客标题对应的元素webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);int title_num = webDriver.findElements(By.cssSelector("title")).size();//如果元素数量不为0,测试通过System.out.println(title_num);Assertions.assertNotEquals(0,title_num);}

博客详情页

   /** 博客详情页校验* url* 博客标题* 页面title是"博客详情页"* 用户名* 文章数* */@Order(4)@ParameterizedTest@MethodSource("Generator")void BlogDetail(String expect_url,String expected_title,String expected_blog_title,String except_username ) throws InterruptedException {//找到第一篇博客对应的查看全文按钮webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);webDriver.findElement(By.xpath("//*[@id=\"artListDiv\"]/div[1]/a[1]")).click();//获取当前页面的URLString cur_url = webDriver.getCurrentUrl();//获取当前页面的titleString cur_title = webDriver.getTitle();//获取博客标题webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);String cur_blog_title = webDriver.findElement(By.cssSelector("#title")).getText();webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);//获取用户名String cur_username = webDriver.findElement(By.cssSelector("#username")).getText();webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);//获取文章数String cur_blog_count = webDriver.findElement(By.cssSelector("#artcount")).getText();webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);Assertions.assertEquals(except_username,cur_username);Assertions.assertNotEquals(0,cur_blog_count);if(cur_url.contains(expect_url)){System.out.println("测试通过");}else{System.out.println(cur_url);System.out.println("测试不通过");}Assertions.assertEquals(expected_title,cur_title);Assertions.assertEquals(expected_blog_title, cur_blog_title);}public class BlogCases extends InitAndEnd{public static Stream<Arguments> Generator() {return             Stream.of(Arguments.arguments("http://154.8.139.1:48080/blog_content.html","博客正文","自动化测试","张帆"));}

 博客编辑页,文章发布完成之后进入博客列表页检测发布文章的标题和时间。

    /** 博客编辑页* */@Order(5)@Testvoid EditBlog() throws InterruptedException {//打开博客列表页//webDriver.findElement(By.cssSelector("http://154.8.139.1:48080/myblog_list.html"));webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);//找到写博客按钮,点击webDriver.findElement(By.cssSelector("body > div.nav > a:nth-child(5)")).click();webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);//通过JS进行输入((JavascriptExecutor)webDriver).executeScript("document.getElementById(\"title\").value=\"自动化测试\"");sleep(3000);//点击发布webDriver.findElement(By.cssSelector("body > div.blog-edit-container > div.title > button")).click();sleep(3000);String cur_text = webDriver.switchTo().alert().getText();webDriver.switchTo().alert().dismiss();String except_text = "恭喜:添加成功!是否继续添加文章?";Assertions.assertEquals(except_text,cur_text);}/** 校验已发布博客标题* 校验已发布博客时间* */@Order(6)@Testvoid BlogInfoChecked(){webDriver.get("http://154.8.139.1:48080/blog_list.html");//获取第一篇博客标题String first_blog_title = webDriver.findElement(By.cssSelector("#artListDiv > div:nth-child(1) > div.title")).getText();//获取第一篇博客发布时间String first_blog_time= webDriver.findElement(By.cssSelector("#artListDiv > div:nth-child(1) > div.date")).getText();Assertions.assertEquals("自动化测试",first_blog_title);if(first_blog_time.contains("2023-11-02")){System.out.println("测试通过");}else{System.out.println("文章发布时间是:"+first_blog_time);System.out.println("测试不通过");}}

删除功能

    /** 测试博客列表页上删除功能* */@Order(7)@Testvoid DeleteCases() throws InterruptedException {//进入个人列表页webDriver.get("http://154.8.139.1:48080/myblog_list.html");webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);//点击删除按钮webDriver.findElement(By.cssSelector("#artListDiv > div:nth-child(1) > a:nth-child(6)")).click();sleep(3000);String text = webDriver.switchTo().alert().getText();webDriver.switchTo().alert().accept();String except_text = "恭喜:删除成功";sleep(3000);String first_blog_title = webDriver.findElement(By.cssSelector("#artListDiv > div:nth-child(1) > div.title")).getText();webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);String except_title = "自动化测试";webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);Assertions.assertEquals(except_text,text);Assertions.assertEquals(first_blog_title,except_title);}

注销功能

    /** 注销功能测试* */@Order(8)@Testvoid Logout(){webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);webDriver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)")).click();webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);//获取alert()文字String text = webDriver.switchTo().alert().getText();String except_text = "是否确定注销?";webDriver.switchTo().alert().accept();webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);//校验URL(登录)String cur_url = webDriver.getCurrentUrl();webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);//校验提交按钮WebElement webElement = webDriver.findElement(By.cssSelector("#submit"));webDriver.manage().timeouts().implicitlyWait(3,TimeUnit.SECONDS);Assertions.assertEquals(except_text,text);Assertions.assertEquals("http://154.8.139.1:48080/login.html",cur_url);Assertions.assertNotNull(webElement);}

InitAndEnd类

package Blog;import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;import java.util.concurrent.TimeUnit;public class InitAndEnd {static WebDriver webDriver;@BeforeAllstatic void SetUp(){webDriver = new ChromeDriver();}@AfterAllstatic void TearDown(){webDriver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);webDriver.quit();}
}

问题总结:

❓在测试程序执行报unexpected alert open: {Alert text : 恭喜:删除成功}这种异常,那么就是你没有将这个弹窗关闭掉

✨可以使用accept()方法接受或者dismiss()拒绝

相关文章:

【软件测试】个人博客项目测试报告

目录 1.报告概要 2、测试环境 3、手动测试用例编写 4、自动化测试用例 1.报告概要 测试对象&#xff1a;基于SSM项目的博客系统。 测试目的&#xff1a;检测博客项目是否符合预期&#xff0c;并且对测试知识进行练习和巩固。 测试点&#xff1a;主要针对常用的功能进行测…...

Express框架开发接口之今日推荐等模块

1.初始化 const handleDB require(../handleDB/index) // 获取全部模块 exports.allModule (req, res) > {(async function () {})() } // 更新或者添加模块 exports.upModule (req, res) > {(async function () {})() } // 根据id删除模块 exports.delModule (req, …...

UTONMOS:元宇宙顺势而上,重构数字化发展新形态

元宇宙&#xff08;Metaverse&#xff09;是一个虚拟的、且与现实世界平行的虚拟世界&#xff0c;由一系列相互关联的技术组成。在这个虚拟世界中&#xff0c;人们可以通过 VR、 AR等设备进入其中&#xff0c;与虚拟人物进行互动。 随着新一代信息技术的飞速发展&#xff0c;元…...

【Nginx37】Nginx学习:SSL模块(一)简单配置与指令介绍

Nginx学习&#xff1a;SSL模块&#xff08;一&#xff09;简单配置与指令介绍 又是一个重点模块&#xff0c;SSL 模块&#xff0c;其实就是我们常见的 HTTPS 所需要的配置模块。HTTPS 的重要性不用多说了吧&#xff0c;现在所有的 App、小程序 都强制要求是 HTTPS 的&#xff0…...

CompletableFuture 异步调用,获取返回值

ExecutorService executor new ThreadPoolExecutor(8, 16, 60,TimeUnit.MINUTES,new ArrayBlockingQueue<>(100));Random randomnew Random(10);//模拟查询用户列表List<User> listselectUsers();//需要执行的任务列表// 任务列表List<CompletableFuture<Us…...

excel利用正则匹配和替换指定内容

上班中, 突然接到电话, 屋里的上司大人发来个excel, 说要替换里面x-x-xxx列的内容为x栋x单元xxx. 大致表格如下, 原表格我就不发了 身为程序猿的我, 肯定第一就想到了 正则! 打开excel-开始-查找和替换, 我擦, 只能完全匹配和替换 比如一次只能替换1-1- -> 为1栋1单元 1-2…...

IPv4首部格式

IPv4首部格式 IPv4数据报的首部格式及其内容是实现IPv4协议各种功能的基础。 在TCPIP标准中&#xff0c;各种数据格式常常以32比特(即4字节)为单位来描述。 IPv4首部格式图 ## IPv4数据报的组成 主要由固定部分(20字节)可变部分(最大40字节) - 固定部分是指每个IPv4数据报都必…...

点云从入门到精通技术详解100篇-基于 3D 激光雷达的车厢冻煤存量检测

目录 前言 研究意义 研究现状(Research status) 3D 激光雷达检测技术研究现状...

idea使用MyBatisX插件

1.MyBatisX功能 (1).实现mapper和xml的跳转 (2).自动生成java文件&#xff0c;比如mapper、service、dao、pojo 2.安装MyBatisX插件 install后然后重启idea即可 3.使用MyBatieX实现mapper和xml跳转 &#xff08;1&#xff09;.点击mapper中的红色图标即可跳转到对应的xml方…...

Open3D(C++) 最小二乘拟合平面(间接平差法)

目录 一、算法原理1、原理概述2、参考文献二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。 一、算法原理 1、原理概述 通过传统最小二乘法对点云数据进行平面拟合时,可将误差只归因于一个方向上,本文假设误差只存在于 Z Z...

Linux中搭建coturn服务器

1、下载coturn源码 git clone https://github.com/coturn/coturn.git2、进入到coturn路径下&#xff0c;执行一下命令。 ./configure出现以下错误&#xff1a; 问题1&#xff1a;ERROR: OpenSSL Crypto development libraries are not installed properly in required locati…...

【Proteus仿真】【Arduino单片机】SG90舵机控制

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真Arduino单片机控制器&#xff0c;使用SG90舵机等。 主要功能&#xff1a; 系统运行后&#xff0c;舵机开始运行。 二、软件设计 /* 作者&#xff1a;嗨小易&#xff08;QQ&#x…...

程序员有哪些规避风险的合法兼职渠道?

近期&#xff0c;承德程序员事件冲上热搜&#xff0c;这对许多程序员的心灵是多么大的伤害啊&#xff01; 人人自危&#xff0c;大家开始顾虑自己接私活、找兼职的方式和前景了。毕竟&#xff0c;谁也不想”辛辛苦苦几十年&#xff0c;一把回到解放前“。那有什么办法既可以接私…...

OpenGL_Learn04

我这边并不是教程&#xff0c;只是学习记录&#xff0c;方便后面回顾&#xff0c;代码均是100%可以运行成功的。 1. 渐变三角形 #include <glad/glad.h> #include <GLFW/glfw3.h>#include <iostream> #include <cmath>void framebuffer_size_callba…...

【嵌入式】HC32F07X CAN通讯配置和使用配置不同缓冲器以连续发送

一 背景说明 使用小华&#xff08;华大&#xff09;的MCU HC32F07X实现 CAN 通讯配置和使用 二 原理分析 【1】CAN原理说明&#xff08;参考文章《CAN通信详解》&#xff09;&#xff1a; CAN是控制器局域网络(Controller Area Network, CAN)的简称&#xff0c;是一种能够实现…...

Linux的常见指令(一)

目录 一、文件 二、常见指令 1、pwd 2、ls 1、ls -a 2、ls -l 3、ls -i ​编辑 3、touch 4、mkdir 5、cd 6、rmdir 和 rm 7、man 8、cp 一、文件 目录和文件是在磁盘上建立的&#xff0c;空文件是在磁盘上占用空间的&#xff08;文件包括文件内容和文件的各种属…...

Jenkins 参数动态获取目录里面的内容

Jenkins 参数动态获取目录里面的内容 假如我们想把一个目录下面的tar.gz文件作为jenkins参数&#xff0c;这个目录会实时更新&#xff0c;每次运行job的时候需要把目录里面的文件作为输入&#xff0c;这时候我们可以使用jenkins自带的Active Choices Parameter参数 在参数中写…...

centos 搭建内网ntp时间服务器

在 CentOS 搭建内网 NTP 时间服务器&#xff0c;你可以按照以下步骤操作&#xff1a; 安装 NTP 服务&#xff1a; 打开终端并以 root 用户身份登录。使用以下命令安装 NTP 服务&#xff1a; sudo yum install ntp配置 NTP 服务器&#xff1a; 打开 NTP 配置文件 /etc/ntp.conf&…...

FreeRTOS-消息队列的使用

1. 定义队列传输的内容和队列结构体对象 使用 xQueueHandle结构体创建对象 typedef struct {u8 TaskNum;u8 Cmd;u8 * buf;}QueueObject_t;xQueueHandle xQueue NULL; 2. 调用xQueueCreate API创建队列 xQueueCreate()函数&#xff1a;用于创建一个消息队列。 QueueHan…...

喜欢 Android 14 的 14 个理由

和去年 8 月中旬发布的 Android 13 正式版不同&#xff0c;今年的 Android 14 正式版延后到了 10 月 4 日——也就是 Pixel 8 系列发布的同一天。原因我们似乎也能从 Google 宣传新特性中略窥一二&#xff1a; 除了明确表示会率先向特定 Pixel 机型推送的 AI 壁纸生成&#xf…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

多模态图像修复系统:基于深度学习的图片修复实现

多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

使用ch340继电器完成随机断电测试

前言 如图所示是市面上常见的OTA压测继电器&#xff0c;通过ch340串口模块完成对继电器的分路控制&#xff0c;这里我编写了一个脚本方便对4路继电器的控制&#xff0c;可以设置开启时间&#xff0c;关闭时间&#xff0c;复位等功能 软件界面 在设备管理器查看串口号后&…...