
在很长一段时间里软件测试技能树主要围绕 Web 应用和 App 展开。测试人员熟悉 HTTP 接口、数据库、前端页面和自动化测试框架但一旦进入制造型企业内部的工业软件项目比如 MES制造执行系统、SCADA数据采集与监控系统、设备管理平台、上位机监控软件技术栈就会突然从 JSON 变成寄存器地址从 HTTP 状态码变成 PLC 扫描周期。项目组里的软件测试人员如果完全不了解 PLC会出现一种很典型的状况界面显示和设备实际状态不一致时不知道问题在上位机、通信链路还是 PLC 逻辑。这不是少数情况。智能制造和工业互联网落地之后软件测试岗位的边界正在向工业软件和数据采集链路扩展。很多测试工程师不直接编写 PLC 程序却必须验证与 PLC 相关软件的功能、接口和异常处理。这篇文章围绕一个核心问题展开零基础软件测试为什么会与 PLC 发生交集以及在不懂梯形图的前提下如何用最有效的方式把 PLC 知识转化为测试能力。全程以可复现为主线。先理解 PLC 的工作机制再借助仿真设备和通信协议搭建最小测试环境最后用一段 Python 脚本读取 PLC 数据并完成校验。读者学完之后至少能看懂设备接口文档里的寄存器表能搭起一个 Modbus TCP 模拟环境能写出一条验证 PLC 数据到界面显示是否一致的测试用例。1. 为什么软件测试会与 PLC 产生交集1.1 工业软件测试的对象是一条完整数据链传统 Web 测试的链路相对固定浏览器或 App 发起请求后端服务处理逻辑数据库存储数据最终把结果返回前端。输入和输出都是可读的文本、JSON 或界面元素。工业软件测试的链路完全不同。以车间设备监控平台为例真实链路是物理设备 - PLC 输入模块 - PLC 程序 - PLC 输出/寄存器 - 通信协议 - 上位机软件 - 数据库 - 前端页面这台设备是开着的还是关着的转速是 1000 还是 2000温度是否超限最终都会以变量的形式存在 PLC 内部。上位机软件只是把 PLC 里某个寄存器的值读取过来再展示到界面上。用户在页面看到的每一个数据几乎都能映射到 PLC 里的某一个存储单元。如果测试人员只站在页面层验证“界面显示了正确的值”但没有追溯这个值来自哪个寄存器、以什么数据类型传输、经过什么协议转换就测不到真正的风险点。工业软件最容易出问题的位置恰恰是数据链路中间而不是页面两端。1.2 零基础测试人员接触 PLC 的四个常见场景不是所有软件测试岗位都会碰到 PLC但以下四类场景出现频率很高。场景类型典型测试对象与 PLC 的关系MES 系统测试工单下发、报工、物料追踪、设备状态管理MES 从 PLC 读取设备状态和产量数据测试时需要构造不同设备状态SCADA/上位机测试实时监控界面、告警、趋势曲线、报表界面数据直接来自 PLC 寄存器需要验证实时性、准确性和异常显示设备接口测试采集服务、协议网关、消息中间件采集程序通过 Modbus/OPC UA 从 PLC 取数测试重点是协议解析和边界值自动化测试脚本测试脚本需要读取 PLC 数据作为断言依据用模拟器或真实 PLC 构造输入数据让自动化脚本判断软件行为是否符合预期这些场景里测试人员通常不需要独立完成 PLC 编程但需要能读懂设备点位表能操作仿真软件能理解“读取保持寄存器”和“写入线圈”这类通信行为。1.3 完全不懂 PLC 的测试人员会遇到什么困难不学 PLCZer 基础测试人员往往在项目里陷入被动。第一个困难是用例设计不完整。上位机软件显示设备温度PLC 里存储的是带符号整数界面经过量程转换后显示实际温度。如果测试人员不知道寄存器里可能是负值、可能超过量程、可能在断电后会变成默认值就不会设计低温、超量程、断电恢复这类用例。第二个困难是环境搭建能力不足。真实 PLC 通常只有一台且产线不能随意停。测试人员如果只会等开发提供数据或者只能在固定时间段操作设备测试效率会很低。会使用 PLC 仿真器或者 Modbus 模拟器之后测试环境就能随时搭起来。第三个困难是缺陷定位说不清。页面显示设备停机但设备实际在运行。这个缺陷可能出在上位机取数逻辑、通信超时处理、PLC 程序输出或者数据库缓存。测试人员如果说不清是哪一层的问题开发就需要重新排查一遍沟通成本成倍增加。底层逻辑是软件测试的核心能力是判断“实际结果和预期结果是否一致”而要判断工业软件的结果是否正确就必须理解结果从哪来。2. PLC 的工作原理软件测试人员需要掌握哪些核心概念2.1 扫描周期是理解 PLC 行为的钥匙PLC 不是事件驱动的程序它在一个循环里反复执行固定过程。整个过程通常分为输入采样、程序执行、输出刷新三个阶段。以西门子或三菱 PLC 的通用逻辑来说明while (true) { // 阶段 1输入采样 // 把物理输入信号读入输入映像区 读入所有 I / 外设信号; // 阶段 2程序执行 // 从第一条指令执行到最后一条结果写入输出映像区 执行用户程序; // 阶段 3输出刷新 // 把输出映像区的状态一次性地写到物理输出端子 刷新所有 Q / 外设输出; }一次完整循环就是扫描周期。普通 PLC 的扫描周期通常在几毫秒到几十毫秒之间取决于程序复杂度和 CPU 性能。这一点对测试很重要。上位机软件从 PLC 读取实时数据时看到的值其实是 PLC 在最近一次扫描周期结束后的结果。也就是说物理设备可能在几十毫秒前已经变化但软件读到的是稍旧的值。如果测试人员不理解扫描周期会把这种正常延迟当作 Bug甚至漏掉一部分由延迟引起的真实缺陷比如界面刷新频率高于 PLC 扫描频率时出现重复值。设计测试用例时建议关注三类与扫描周期相关的场景数据变化后的刷新延迟、连续快速变化时是否丢点、PLC 停机或扫描停止后界面如何显示。2.2 PLC 编程语言不止梯形图ST 语言更贴近软件思维PLC 的经典编程语言是梯形图Ladder DiagramLD它是从继电器控制电路演化来的工程师可以用触点、线圈、定时器、计数器组合出控制逻辑。但梯形图对软件工程师并不友好它表达逻辑分支、数组运算、字符串处理时非常繁琐。IEC 61131-3 标准定义了 PLC 的五种编程语言指令表IL、结构化文本ST、梯形图LD、功能块图FBD、顺序功能图SFC。软件测试人员学习 PLC 时优先理解结构化文本ST更高效因为 ST 的语法接近 Pascal 或高级语言。比如一个电机启动控制块逻辑可以写成// 伪结构化文本示例用于理解 PLC 程序逻辑 IF AutoMode AND NOT Fault THEN MotorRun : TRUE; RunCount : RunCount 1; ELSE MotorRun : FALSE; END_IF; IF Temperature 80.0 THEN OverTempAlarm : TRUE; ELSE OverTempAlarm : FALSE; END_IF;虽然不同品牌 PLC 的 ST 语法略有差异但核心结构一致IF-ELSE、CASE、FOR 循环、布尔变量、整数和实数运算。看懂 ST 之后至少能读懂 PLC 程序里做了什么判断也能在测试上位机软件时判断某个变量的置位逻辑是否符合需求。2.3 变量、数据类型、软元件寄存器测试的基本功PLC 内部有若干存储区域不同品牌叫法不同。西门子 PLC 常见的是 I输入映像区、Q输出映像区、M位存储区、DB数据块三菱 PLC 常见的是 X输入继电器、Y输出继电器、M辅助继电器、D数据寄存器。这些区域可以统称为软元件或变量存储区。软件测试人员更关注的是数据类型。PLC 里常见数据类型包括类型含义典型场景测试关注点BOOL布尔量0 或 1电机启停、阀门开关、报警状态置位/复位边界信号抖动BYTE/WORD/DWORD无符号位串状态字、模式字、通信报文位与 Word 之间转换是否正确INT有符号整数通常 16 位计数器、速度设定值32767/32768 边界负值DINT有符号双整数32 位累积产量、运行时长大数值溢出跨 PLC 扫描周期的累加REAL浮点数温度、压力、流量精度误差NaN/Infinity 处理STRING字符串工艺配方、报警文本长度越界编码格式这些数据类型直接决定了测试用例的输入范围。比如一个 INT 类型的转速寄存器测试人员至少要覆盖 -32768、-1、0、1、32767 和超出 32767 时的处理方式。如果 PLC 程序或采集程序把 INT 当成无符号数解析那么 65535 和 -1 会变成同一个值这类问题在接口联调中非常常见。注意看设备点位表时不能只看寄存器地址还要确认数据类型、字节顺序、缩放系数。同一个地址按 INT 解析和按 REAL 解析结果完全不同。3. 软件测试人员学习 PLC 的切入点协议、仿真与模拟3.1 先从通信协议入手而不是从梯形图入手零基础测试人员学习 PLC 最容易陷入的误区是想先学会写 PLC 程序。这个顺序对测试岗位并不合理。测试人员的核心需求是“能控制 PLC 的数据输入能验证软件的输出结果”所以切入点是通信协议。工业现场最常见的两类协议是 Modbus 和 OPC UA。Modbus 是应用层协议Modbus TCP 是它在以太网上的实现默认端口 502。它采用 Master/Slave 或 Client/Server 模型数据通过功能码读写。测试人员需要掌握的核心概念是寄存器地址和功能码对象功能码常见用途测试注意点线圈0x01 读 / 0x05 写开关量输出、启停命令只有 0/1 两种状态离散输入0x02 读读取物理开关状态只读不能写保持寄存器0x03 读 / 0x06 写设备的运行参数、设定值可读可写适合测试构造数据输入寄存器0x04 读读取模拟量采集结果只读代表设备实时输入OPC UA 在新一代工业软件中越来越常见。它不仅是数据读取协议还定义了信息模型服务器端会暴露节点、数据类型、方法和事件。如果用 OPC UA 客户端工具浏览服务器命名空间能直观看到设备数据是结构化的测试时可以用统一方式定位节点和写值。对测试工程师来说掌握 Modbus 是基础理解 OPC UA 的思路是进阶。Modbus 让你能做最小通信OPC UA 让你进入更标准的工业数据世界。3.2 用仿真器和模拟器代替真实 PLC真实 PLC 数量有限、端口有限、程序也不能随意改动所以测试阶段最有效的做法是使用仿真器。常见方式有三类。第一类是在编程软件里仿真。西门子 TIA Portal 配合 PLCSIM 可以下载程序到虚拟 PLC三菱 GX Works 也提供仿真功能。这种方式适合开发或测试需要真实工程文件的情况但依赖 PLC 编程软件且正版授权通常不便宜。第二类是通用协议模拟器。比如 Modbus Slave 类软件可以把电脑模拟成一个 Modbus 从站自行设置寄存器地址和值上位机软件读到的是电脑产生的数据。这种方式不依赖任何 PLC 品牌最适合软件测试人员快速搭建测试环境。第三类是利用 Python 等语言编写模拟器。通过 Modbus 库创建一个虚拟从站在脚本里动态修改寄存器值测试用例可以自动控制数据变化。在实际项目里建议先用协议模拟器把功能链路跑通再在真实 PLC 上做一次验证。两者的差异通常发生在通信超时、接线干扰、电磁噪声和物理信号延迟上。3.3 用 PLC 侧数据变化构造异常场景工业软件测试里最不好复现的是异常场景。设备超温、压力骤降、频繁启停、通信断线这些在真实产线上很难按测试人员的节奏出现但用模拟器可以轻松构造。方案是把模拟器里的指定寄存器改成边界值或异常值然后观察软件表现。比如把保持寄存器地址 40001 设置为 10000 验证 MES 页面显示的转速是否等于 10000 把保持寄存器地址 40001 设置为 -32768 验证页面是否出现异常提示而不是溢出成 32768 把模拟器断开 验证上位机是否在 10 秒内显示通信中断而不是继续显示旧数据这样做的本质是把 PLC 当成测试数据源或缺陷注入点。测试人员不修改被测系统的代码只改变下游输入就能覆盖软件的各种分支这是工业软件测试里非常有效的思路。4. 最小实现用 Python 读取 PLC 寄存器并校验数据4.1 环境准备与模拟器选择在这个最小示例里不需要真实 PLC。环境准备如下Python 3.8 或更高版本。pymodbus 库用于实现 Modbus TCP 客户端。一个 Modbus 从站模拟器可以是图形化工具也可以直接用 pymodbus 自带服务端示例。一个准备测试的上位机页面或数据采集接口。pymodbus 的 API 在不同版本之间有差异。早期版本常用ModbusClient较新版本推荐ModbusTcpClient。安装时建议锁定一个稳定版本避免示例代码不可用pip install pymodbus3.0,4.0如果希望快速验证通信效果可以先安装一个 Modbus 从站模拟工具在界面上创建保持寄存器并手动写入初始值。4.2 编写读取与校验脚本下面这段 Python 脚本解决两个问题连接 Modbus TCP 从站读取保持寄存器中的转速值把读取值和预期值做对比并输出测试结论。from pymodbus.client import ModbusTcpClient PLC_IP 127.0.0.1 PLC_PORT 502 PLC_TIMEOUT 3 # 设备点位表中约定的保持寄存器地址 REGISTER_ADDRESS 0 # 预期转速值由测试用例决定 EXPECTED_VALUE 1000 TOLERANCE 5 def read_speed_register(client, address): 读取保持寄存器中的转速值。 设备点位表说明该寄存器是 16 位有符号整数。 response client.read_holding_registers(address, count1, slave1) if response.isError(): raise RuntimeError(f读取寄存器失败: {response}) # values 可能为 None需要判断 if response.registers is None or len(response.registers) 1: raise RuntimeError(寄存器返回空数据) return response.registers[0] def validate_speed(value, expected, tolerance): 校验读取值与预期值的偏差是否在允许范围内。 return abs(value - expected) tolerance def main(): client ModbusTcpClient(PLC_IP, portPLC_PORT, timeoutPLC_TIMEOUT) connected client.connect() if not connected: print(测试失败无法连接 PLC 模拟器) return try: speed read_speed_register(client, REGISTER_ADDRESS) print(f读取到转速寄存器值: {speed}) if validate_speed(speed, EXPECTED_VALUE, TOLERANCE): print(f测试通过转速值 {speed} 在预期范围 {EXPECTED_VALUE}±{TOLERANCE} 内) else: print(f测试失败转速值 {speed} 不在预期范围 {EXPECTED_VALUE}±{TOLERANCE} 内) except Exception as exc: print(f测试异常: {exc}) finally: client.close() if __name__ __main__: main()这段脚本的关键点在于把“读取数据”和“校验结果”分开。测试场景里读取值来自 PLC 或模拟器预期值来自需求文档两者在脚本中对比后得到结论。这样后续对接 pytest 或 unittest只需要把执行逻辑抽成函数即可。4.3 从最小示例推导测试用例设计上面的示例虽然简单但可以推导出一套工业软件数据校验用例。测试人员可以按以下维度设计用例用例维度输入方式预期结果正常值读取模拟器写入 1000页面显示 1000偏差在允许范围内边界值读取模拟器写入 32767 或 -32768页面按量程显示无溢出超范围读取模拟器写入 40000超出 INT 范围页面出现异常提示或按上限截断连接断开停止模拟器页面提示通信失败不显示旧数据数据类型错误按 REAL 写入用 INT 解析采集程序或页面是否识别错误字节序差异高字节在前/在后切换数值是否出现高低位交换这些用例正好回答了“软件测试为什么要学习 PLC”不学习 PLC 数据类型、寄存器寻址和通信机制这些用例根本设计不出来。4.4 从脚本到自动化测试框架的扩展最小示例再往前走一步可以写成可复用的 PLC 数据检查工具函数。def assert_plc_register_equals(plc_ip, address, expected, tolerance0): client ModbusTcpClient(plc_ip, port502, timeout3) if not client.connect(): return False, 连接失败 try: response client.read_holding_registers(address, count1, slave1) if response.isError(): return False, f读取错误: {response} actual response.registers[0] if response.registers else None if actual is None: return False, 寄存器为空 passed abs(actual - expected) tolerance return passed, f期望 {expected}实际 {actual} finally: client.close()在真正的测试框架里调用方只需要传入设备 IP、寄存器地址和预期值框架负责输出通过/失败和日志。这样 PLC 就成了自动化测试数据集的一部分。注意写脚本前一定要拿到设备点位表。点位表里会写明寄存器地址、数据类型、缩放系数、读写属性和单位。缺少点位表而靠猜测写脚本是工业软件测试中最容易翻车的做法。5. 零基础软件测试学习 PLC 的进阶路线5.1 按测试岗位定制学习阶段零基础学习任何技术最大的问题不是难度而是不知道学到什么程度就算够用。针对软件测试岗位建议按下面四个阶段推进。阶段一理解控制对象和输入输出。这阶段要搞清楚 PLC 如何采集信号、如何输出控制。不需要编程只需要知道传感器、执行器、I/O 模块之间的关系。阶段二掌握仿真环境和协议工具。选择一种 PLC 仿真方式学习打开模拟器、建立寄存器、写入数据、断开连接。能准确读出和写入数据后就可以为上位机测试构造数据。阶段三掌握至少一种通信协议的读写。优先学习 Modbus TCP因为它简单、通用、文档多。有余力再学 OPC UA。阶段四进入真实项目或模拟项目。把一个简单的设备状态采集页面作为被测对象用模拟器当 PLC完整执行“读取设备点表 - 搭建模拟环境 - 设计数据校验用例 - 编写脚本 - 执行测试 - 输出缺陷报告”的全流程。这四个阶段对零基础测试人员来说并不需要先精通梯形图。梯形图和 ST 语法可以在遇到具体问题时再补充。5.2 不同品牌 PLC 的学习侧重点不同品牌的 PLC 在编程平台、寻址方式和通信参数上有明显差异。软件测试人员不一定要精通所有品牌但要知道自己所在项目用什么品牌再针对性学习。品牌常见编程平台常见通信方式测试人员优先级西门子TIA PortalPROFINET、Modbus TCP、OPC UA较高制造业项目占比大三菱GX Works / GX Developer内置协议、Modbus、Socket较高装备制造项目常见欧姆龙CX-One / Sysmac StudioEtherNet/IP、Modbus TCP中汇川AutoShop 等Modbus TCP、CANlink中国产品牌项目需求增长快基恩士KV Studio以太网、Modbus TCP低但设备自带 PLC 的场合会用到学习时不建议同时开多个品牌会有概念混淆。先把一个品牌的仿真环境和 Modbus 读写跑通再迁移到另一个品牌会发现大量概念是相通的。5.3 用一个小项目把软件测试和 PLC 串起来最有效的学习方式是做一个微型项目。这里给出一个可执行的方向。准备一个 Modbus 模拟器在里面建三个寄存器设备状态、当前温度、运行模式。然后准备一个简单的数据展示页面或命令行工具通过 Modbus 读取这三个值并展示。测试人员的任务是从“设备点位表”里找到这三个寄存器的地址、类型、含义。为每组数据设计至少 10 条测试用例覆盖正常、边界、异常、通信故障。用 Python 脚本自动读写寄存器构造不同数据。记录每个用例的执行结果发现缺陷后写一份标准缺陷报告。完成这个项目比看十本 PLC 教材都更有价值。因为整个过程复用了软件测试原有的能力只是把被测对象从 Web 系统换成工业数据链路。6. 常见错误与问题排查6.1 测试人员接触 PLC 数据时最常见的五个错误错误现象常见原因检查方式处理建议页面显示值与 PLC 不一致寄存器地址偏移搞错核对点位表中的地址和代码访问的地址注意寄存器地址是基于 0 还是基于 1数值翻倍或减半连续读取两个寄存器被当成一个数据确认数据类型长度32 位数据需要读取两个寄存器再组合负值显示成超大正数有符号数被按无符号数解析确认点位表中的数据类型INT 用有符号解析WORD 用无符号解析数据高低位颠倒字节序不匹配抓取报文查看字节顺序在采集脚本里交换高字节和低字节连接断开后页面仍显示旧值上位机没有处理通信超时停止模拟器后观察页面在测试用例中显式验证断线场景这些错误不仅出现在 PLC 工程师身上也经常出现在软件测试人员写的校验脚本里。写脚本读取 PLC 数据时要带着和写接口测试一样的严谨度。6.2 数据异常时的排查顺序当页面显示值和设备实际状态不一致时按下面的顺序排查每步都能留下明确日志。第一步检查界面层。确认是不是页面缓存、前端转换逻辑或刷新频率导致的显示问题。第二步检查采集服务。查看采集日志确认从 PLC 读到的原始值是多少。如果在采集层已经是错误值说明问题不在页面。第三步检查通信链路。用 Modbus 调试工具直接读取同一地址的寄存器值判断网络传输是否正常是否出现读写超时。第四步检查 PLC 侧变量。如果通过仿真器验证直接修改寄存器初始值观察采集服务读取结果是否随之变化。第五步检查点位表。确认地址偏移、数据类型、字节序、缩放系数是否用错。实际项目里大部分“数据不对”的缺陷都卡在第三步和第五步之间。测试人员如果能把排查边界锁定到“采集层原始值正确但页面错误”开发定位问题的速度会快很多。6.3 学习过程中要注意的隐形坑第一个隐形坑是依赖版本。pymodbus 在 2.x 和 3.x 之间 API 变化明显博客和教程里的代码不一定能直接运行。看教程时要先确认版本号如果报AttributeError优先怀疑库 API 变化。第二个隐形坑是地址偏移。Modbus 文档里常见的地址有 40001、40002 这种表示方式而程序里访问时通常用 0、1。如果不转换会偏移一个地址。第三个隐形坑是只验证正常路径不验证异常路径。测试人员学会读 PLC 数据之后很容易满足于“能读到值”却忽略了“读不到值时软件怎么表现”。工业软件里最严重的缺陷往往发生在异常路径。7. 最佳实践一套可以复用的 PLC 相关软件测试清单7.1 收到新项目时的前置检查清单进入 PLC 相关软件项目时先用下面的清单收集信息避免测试到一半才发现文档缺失[ ] 是否拿到设备点位表点位表是否包含寄存器地址、数据类型、读写属性、单位、缩放系数[ ] 是否明确通信协议是 Modbus TCP、OPC UA 还是厂商私有协议[ ] 测试环境是否具备是使用真实 PLC、仿真器还是协议模拟器[ ] 是否了解上位机软件的取数方式定时轮询还是订阅通知轮询周期是多少[ ] 是否明确异常场景处理规则断线重连、超时、读写失败时的界面表现是什么[ ] 是否确认字节序和数据类型同一种数据在 PLC、采集服务、前端展示中的类型是否一致这份清单在执行环境准备阶段最有用。不要等项目联调时才问那时已经晚了。7.2 测试用例设计清单覆盖正常值每个测量数据至少包含一个中间值。覆盖边界值数据类型上限、下限以及超出范围时的表现。覆盖通信异常从站关闭、IP 不可达、端口不通、超时设置过短。覆盖数据变更模拟器手动改值后观察页面刷新时间是否在需求允许范围内。覆盖断电重启PLC 重新上电后寄存器恢复默认值上位机是否重新同步。覆盖并发读写多个客户端同时读取或写入时数据是否出现互相覆盖。7.3 对零基础测试人员的三点建议第一不要从梯形图开始。对测试岗位而言从协议和仿真开始收益最大梯形图遇到具体程序时再学。第二不要依赖真实设备。尽早搭建模拟器环境模拟器越熟练在项目里越主动。第三把 PLC 知识沉淀成脚本和用例集。每完成一个项目把采集脚本、模拟器配置、通用校验函数整理成自己的工具包下一次遇到同类项目就能直接复用。回到最开始的问题。零基础软件测试学习 PLC不是为了转行做自动化工程师而是为了在工业软件测试项目中拥有完整的数据链路视角。能看懂设备点位表能搭出数据模拟环境能验证从 PLC 到页面的每个环节这样的测试人员才能设计出别人想不到的异常用例也才能在联调阶段快速定位问题方向。从一门协议、一个模拟器、一段读取脚本开始是最稳妥的路径。