十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

软件测试入门必学:运算符与数据类型撑起边界值分析

软件测试入门必学:运算符与数据类型撑起边界值分析 软件测试入门阶段运算符和数据类型往往被误认为是纯编程知识。实际上测试用例中的边界值设计、接口字段校验、自动化断言和缺陷定位全部要回到这两个基础概念上。以登录功能为例用户名长度限制是 6 到 20 位测试用例为什么要在 5、6、20、21 这四个位置各设计一条数据因为字符串类型的边界恰好决定了缺陷最容易出现的位置。本文是软件测试入门到精通系列的第二课前序内容已经完成了软件测试基础与开发环境准备这一课重点补齐运算符和数据类型的进阶知识并解决它们如何应用到实际测试项目中的问题。学完这一课你至少能回答三件事测试数据应该如何按类型设计边界运算逻辑出现异常结果时应该从哪个环节排查自动化断言里为什么不能随便用比较浮点数和字符串。1. 为什么软件测试先要从运算符和数据类型讲起1.1 测试断言本身就是运算符软件测试中“预期结果”和“实际结果”的比较在自动化层面对应的是断言而断言的底层就是运算符。一个最简单的接口测试断言可能是这样的Assert.assertEquals(expectedCode, actualCode);这个断言的判断逻辑等价于expectedCode actualCode如果expectedCode是整数200actualCode也是整数200结果没问题。但如果其中一个来自 JSON 响应体而 JSON 解析器把它解析成了字符串200那么expectedCode actualCode就会变成200 200结果直接为false。测试失败的原因不是接口返回错误而是数据类型不匹配。所以测试人员理解运算符和数据类型不是为了写开发代码而是为了能够准确判断“问题到底出在业务逻辑上还是出在类型与运算方式上”。这个判断能力在缺陷定位阶段非常关键。1.2 类型边界决定测试用例的落点测试用例设计中最常用的两个方法是等价类划分和边界值分析。等价类划分把输入数据分成有效和无效两个集合边界值分析则在集合的边缘上取值。而类型的取值范围正好是边界值分析的第一层依据。表格中的这些边界就是测试用例的直接来源类型典型边界测试数据示例int 整数21474836472147483646、2147483647、2147483648字符串长度6 到 20 位5、6、20、21日期范围1970-01-01 到 2038-01-19边界前后各一天金额精度保留两位小数1.99、2.00、2.01如果测试人员不理解 int 的最大值就很难想到“库存加 1 后变成负数”这种经典缺陷。如果不知道浮点数存在精度误差就容易写出0.1 0.2 0.3这种永远失败的断言。1.3 这一课在软件测试系列中的位置从软件测试学习路线来看第一课通常是测试基础包括软件测试的定义、流程、分类和生命周期。第二课进入数据层面也就是运算符与数据类型。这两部分看似偏编程实际上是后面编写测试用例、设计测试数据、定位缺陷、编写自动化脚本的公共基础。本系列后续课程会进入测试用例设计、接口测试、性能测试和项目实战。这一课如果掌握不牢到了接口测试阶段就很容易遇到以下现象字段类型明明对不上却不知道怎么看断言结果不稳定却找不到原因阅读开发代码时看不懂类型转换逻辑。因此这一课的任务不是把每种语言的语法都过一遍而是建立一套“测试人员看数据、看运算、看边界”的思维方式。2. 数据类型进阶先理解取值范围才能设计有效边界2.1 基础类型、包装类型与 null 语义以 Java 为例数据类型分为基础类型和引用类型。基础类型有明确的取值范围和默认值引用类型可以指向一个对象也可以为null。测试中最常踩的坑是把基础类型的默认值和引用类型的null混为一谈。下面的代码展示了它们的区别int number 0; // 基础类型默认值为 0 Integer numberObj null; // 包装类型默认值为 null String name null; // 字符串为 null String empty ; // 空字符串这里要特别注意null、空字符串、空格字符串 是三份完全不同的测试数据。用户名为空时前端可能提示“请输入用户名”用户名为null时后端接收参数可能出现NullPointerException用户名是三个空格时后端如果做了trim()处理会判定为空如果没有做则会当作一个合法的三位字符串。在设计测试用例时这三类数据都要覆盖。不要想当然地认为“空就是空”。2.2 常用数据类型取值范围与测试边界测试人员不一定要记住所有类型在每种语言里的精确取值范围但必须知道去哪里查并且对最常用的 int、long、double、String 的边界敏感。类型占用空间取值范围默认值典型测试边界byte1 字节-128 到 1270127、-128、128short2 字节-32768 到 32767032767、32768int4 字节-2147483648 到 21474836470最大值、最小值、最大值 1long8 字节-9223372036854775808 到 92233720368547758070L超过 int 范围的大数float4 字节约 ±3.4E386 到 7 位有效数字0.0f小数位数较多的数值double8 字节约 ±1.7E30815 到 16 位有效数字0.0d0.1 0.2 的精度问题char2 字节0 到 65535\u0000中文字符、emoji、特殊符号boolean未明确规定true / falsefalse条件组合这个表格对测试用例设计最直接的帮助是当需求中出现“数量”“金额”“年龄”“库存”这类数值字段时先确认字段在数据库和代码中是什么类型再按照该类型的最大最小值去构造边界数据。2.3 字符串边界不只是长度字符串是测试中出现频率最高的数据类型。很多测试人员只关注“长度边界”但字符串的边界至少包含四个方面长度边界空、1 位、恰好等于最大值、最大值 1。内容边界纯数字、纯字母、中英文混合、Unicode 特殊字符、emoji。空白边界空字符串、空格、制表符、换行符、首尾空格。转义边界单引号、双引号、反斜杠、SQL 注入关键字、HTML 标签。例如一个备注字段限制为 100 个字符测试时除了测 99、100、101还要考虑String note ; // 全部是空格 String note2 a\nb; // 包含换行 String note3 scriptalert(1)/script; // 包含 HTML 标签这些数据往往不会导致长度校验失败但会触发后续的存储、展示、安全校验逻辑。接口测试阶段这类数据尤其重要。2.4 浮点数测试人员必须警惕的精度缺口浮点数在计算机中无法精确表示所有十进制小数这是由二进制存储机制决定的。经典例子是double a 0.1; double b 0.2; double c a b; System.out.println(c); // 输出 0.30000000000000004测试人员只要遇到金额、折扣率、利率、坐标这类字段就要立刻联想到浮点精度问题。断言时不能直接比较浮点数是否相等应该使用误差范围或精确数值类型// 错误断言 Assert.assertEquals(0.3, 0.1 0.2); // 正确做法用误差范围比较 Assert.assertEquals(0.3, 0.1 0.2, 0.0001); // 或使用 BigDecimal BigDecimal amount new BigDecimal(99.90);注意BigDecimal构造时建议传入字符串而不是直接传入 double。new BigDecimal(0.1)得到的仍然是不精确的二进制结果new BigDecimal(0.1)才是精确的十进制 0.1。3. 运算符进阶从语法规则到测试断言3.1 算术运算符除法、取余与溢出算术运算符包括、-、*、/、%、、--。测试人员需要重点关注三个容易出错的地方第一整数除法会截断小数。int result 5 / 2; System.out.println(result); // 输出 2如果需求要求保留小数开发却使用了 int 类型测试时用 5 和 2 作为输入就能复现结果错误。这类问题在价格计算、平均值计算中很常见。第二取余结果的符号与被除数一致。System.out.println(5 % 2); // 1 System.out.println(-5 % 2); // -1 System.out.println(5 % -2); // 1取余结果的正负号容易被忽略。测试用例中如果涉及负数运算要增加负数取余的验证点。第三整数运算会溢出。int max Integer.MAX_VALUE; // 2147483647 int result max 1; System.out.println(result); // 输出 -2147483648溢出不会报错但结果会变成负数。这种缺陷在库存增加、计数器累加、积分累计场景中最容易出现。测试用例要覆盖类型最大值附近的数据。3.2 比较运算符 与 equals 的分水岭比较运算符包括、!、、、、。、这类比较用于数值和字符规则相对简单。最需要注意的是。在 Java 中比较基础类型时比较的是值比较引用类型时比较的是内存地址。字符串的经典问题如下String a abc; String b abc; String c new String(abc); System.out.println(a b); // true字符串常量池中的同一对象 System.out.println(a c); // falsec 是 new 出来的新对象 System.out.println(a.equals(c)); // true比较的是内容同样的现象也出现在包装类型中Integer x 127; Integer y 127; Integer m 128; Integer n 128; System.out.println(x y); // true-128 到 127 有缓存 System.out.println(m n); // false超过缓存范围自动断言中如果直接使用assertEquals(expected, actual)框架内部通常已经处理了 equals 逻辑。但如果手工写断言条件尤其是编写条件表达式时使用了就很可能踩到上面的坑。3.3 逻辑运算符短路求值如何影响测试逻辑运算符、||、!用于组合布尔条件其核心特性是短路求值。String user null; // 如果 user 为 null这条语句会抛出 NullPointerException if (user ! null user.length() 3) { System.out.println(用户名长度大于 3); }user ! null放在前面当 user 为 null 时后面的条件不会执行从而避免了空指针。如果调换顺序if (user.length() 3 user ! null) {测试数据为 null 时程序直接崩溃。这说明条件顺序本身就是一种缺陷来源。测试人员设计用例时必须包含 null、空值、正常值三类条件数据并观察程序是否在短路径条件下仍然表现正常。测试判断条件时可以借助真值表来系统设计组合ABA BA || B!Atruetruetruetruefalsetruefalsefalsetruefalsefalsetruefalsetruetruefalsefalsefalsefalsetrue条件组合越多越适合使用正交试验或判定表法来压缩测试用例数量但前提是理解了逻辑运算符本身的行为。3.4 位运算符与状态判断位运算符包括、|、^、~、、。在普通 Web 项目中用得不多但在权限系统、状态字段、嵌入式设备、协议解析中很常见。一个典型的权限判断代码int READ 1; // 二进制 0001 int WRITE 2; // 二进制 0010 int DELETE 4; // 二进制 0100 int userPermission READ | WRITE; // 二进制 0011 boolean canRead (userPermission READ) ! 0; boolean canDelete (userPermission DELETE) ! 0; System.out.println(canRead); // true System.out.println(canDelete); // false权限系统中用位运算表示“用户拥有哪些权限”能显著节省存储空间。测试时权限组合越多越要覆盖全组合或者采用正交表抽样。位运算测试的重点不是“结果对不对”而是“权限叠加、撤销、越权访问”三种场景下的结果是否正确。3.5 运算符优先级很容易被忽略复合表达式中运算符优先级决定计算顺序。实际项目里最稳妥的做法是用括号明确表达式的计算顺序不依赖记忆。int result a b * c; // 等价于 a (b * c) int result2 (a b) * c; // 明确先加后乘测试人员阅读开发代码时如果看到没有括号的长表达式应该主动怀疑其可读性和潜在歧义。排查和复现问题时第一步就是拆分表达式确认每一步运算结果是否符合预期。下面这张表可以放在手边随时查阅分类常见符号测试关注点典型错误现象算术 - * / % --除 0、取余符号、整数除法、溢出结果截断或为负数关系 ! 引用比较与值比较equals 漏用逻辑 || !短路求值条件顺序错误引发空指针位运算 | ^ ~ 权限位判断与组合状态被误判赋值 - * / %与混淆条件恒真三元? :可读性与嵌套逻辑分支判断错误4. 类型转换与比较陷阱最容易漏测的一类缺陷4.1 隐式转换自动提升不等于安全在运算过程中Java 会自动把小范围类型提升为大范围类型。例如int a 5; long b 10; long result a b; // int 自动提升为 long这种转换通常安全。但数据类型转换也经常出现在接口参数、数据库映射、前端展示之间。一个最常见的例子是int quantity 9; double price 19.9; double total quantity * price; // 结果是 179.09999999999997测试人员如果看到计算结果出现一串小数第一反应不应该是“代码写错了”而是“double 精度问题”。真正的缺陷不是计算逻辑而是数据类型选择不当。4.2 强制转换精度丢失与溢出强制转换是显式写的但同样危险。高精度转低精度时结果可能超出目标类型的范围。long bigNumber 3000000000L; // 超过 int 最大值 int casted (int) bigNumber; System.out.println(casted); // 输出负数double pi 3.99; int result (int) pi; System.out.println(result); // 输出 3小数部分被直接丢弃测试人员在构造数据时如果发现字段类型是 int而数据库或接口返回了超过 2147483647 的数字就必须判断“是类型设计不合理还是转换逻辑有漏洞”。对应的测试数据要覆盖最大值、最大值 1、最小值、最小值 - 1。4.3 字符串与数值互转的异常场景字符串转数值的失败是接口测试和自动化测试中的高频问题String age 18; int ageValue Integer.parseInt(age); // 正常 String errorAge 18a; int errorValue Integer.parseInt(errorAge); // 抛出 NumberFormatException下面是常见的转换异常输入输入字符串转换方法结果123Integer.parseInt12312.5Integer.parseInt抛 NumberFormatExceptionInteger.parseInt抛 NumberFormatExceptionnullInteger.parseInt抛 NullPointerException99999999999Integer.parseInt抛 NumberFormatException 123 Integer.parseInt抛 NumberFormatException需要先 trim接口测试中字段类型校验是必须覆盖的用例。前端传18是合法字符串后端可能转成 int前端传abc则要验证后端是否返回明确的参数错误而不是 500 异常。测试数据里应包含数字字符串、含小数点的字符串、空字符串、null、超长数字、负数、带空格字符串。4.4 SQL 和接口中的类型转换隐患数据库查询中的类型转换同样隐蔽。假设订单表的order_no字段是 varchar 类型查询时却使用了数字SELECT * FROM orders WHERE order_no 12345678901234567890;部分数据库会对字符串字段与数值比较时尝试隐式转换可能导致全表扫描或索引失效。更常见的是接口字段类型不一致前端把金额作为浮点数传输后端用 BigDecimal 接收或者接口文档定义字段为 string实际返回却是 number。接口测试人员应该养成一个习惯校验字段类型而不只是校验字段值。JSON 响应体中的id: 123和id: 123从展示上看差别不大但下游系统解析时行为完全不同。使用断言时要用expectedType和actualType同时校验。5. 把运算符和数据类型应用到典型测试场景5.1 登录注册场景用边界值法设计用例登录注册模块是边界值分析最好的练习场景。假设用户名规则为 6 到 20 位字符测试用例至少应包含用例编号输入预期结果设计依据TC-01用户名 5 位提示长度不足边界 6 的下边界 - 1TC-02用户名 6 位通过边界 6TC-03用户名 20 位通过边界 20TC-04用户名 21 位提示超长边界 20 的上边界 1TC-05用户名为空提示必填空值TC-06用户名全为空格提示必填或合法取决于需求空串与空格串的区别TC-07用户名包含 emoji提示不支持或正常存储字符集边界TC-08用户名首尾有空格自动 trim 或保留取决于需求字符串处理逻辑这套用例完全基于字符串类型的边界特性。如果测试人员没有建立“空、空格、null、长度 1、长度 - 1”这类数据意识自动化用例覆盖度就会明显不足。5.2 金额计算场景用精度问题反向验证需求电商系统中的优惠价格计算是典型高风险场景。例如商品价格 99.90 元折扣率 0.8用 double 计算double price 99.9; double discount 0.8; double payPrice price * discount; System.out.println(payPrice); // 79.91999999999999前端期望显示79.92后端计算结果却可能是79.91999999999999两者在订单对账时就会出现不一致。测试这个场景的正确做法是先确认需求中的舍入规则四舍五入、向上取整、向下取整再确认代码中金额的存储类型double、BigDecimal、还是数据库 decimal最后用边界数据验证0.01、0.00、9999999.99、-1.00。金额相关的断言统一使用精确数值类型和明确的精度BigDecimal price new BigDecimal(99.90); BigDecimal discount new BigDecimal(0.8); BigDecimal payPrice price.multiply(discount).setScale(2, RoundingMode.HALF_UP);5.3 权限系统场景用位运算验证组合状态权限测试中用户可能同时拥有多个权限。假设权限码设计如下权限权限值二进制查询10001新增20010修改40100删除81000用户拥有“查询 修改”权限时权限值为1 | 4 5。测试用例要覆盖单权限用户能否访问对应功能。组合权限用户能否同时访问多个功能。权限叠加时值是否正确。删除权限后是否立即失效。越权访问未授权功能是否被拦截。这种场景下测试人员不直接写位运算代码但必须能读懂权限值是怎么算出来的。排查“用户有权限却无法访问”的问题时第一步就是计算权限值是否符合预期。5.4 接口测试场景类型和值都要校验接口测试中JSON 响应字段经常出现类型混淆。一个典型的响应体{ code: 0, message: success, data: { userId: 10086, balance: 99.9 } }从响应看userId是字符串型数字balance是浮点数。自动化断言时除了比较值还要确认类型// 校验类型 Assert.assertTrue(response.get(data.userId) instanceof String); Assert.assertTrue(response.get(data.balance) instanceof Double);如果后续版本中userId被改成数字类型10086值没变但类型变了。旧版本客户端可能因为类型不一致解析失败。接口测试中加入类型断言可以在版本升级时提前发现这类兼容性风险。6. 常见问题现象与排查路径6.1 断言结果不稳定明明显示 0.3 却断言失败项目内容现象接口返回 0.3断言 expected 0.3 仍然失败可能原因浮点数二进制存储精度误差实际结果是 0.30000000000000004检查方式打印actual值的完整精度观察是否有长尾小数处理建议使用 BigDecimal 或assertEquals(expected, actual, 0.0001)预防措施测试框架中不要对浮点数使用无误差范围的断言。如果项目里已有类似断言排查时优先怀疑精度问题。6.2 整数计算结果变成负数项目内容现象库存增加 1 后变成 -2147483648可能原因int 类型溢出检查方式确认字段在代码和数据库中的类型是 int 还是 long处理建议将字段改为 long 或 BigDecimal并在边界值附近补测试用例预防措施在设计用例时对数值型字段加入最大最小值测试不要只测业务中看起来合理的数值。6.3 短路条件仍然报空指针项目内容现象使用了但 user 为 null 时程序仍然崩溃可能原因条件顺序写反null 判断没有放在短路前面检查方式查看报错行确认是从user.length()处抛出异常处理建议把user ! null放在的左侧预防措施测试用例中加入 null 和空值数据覆盖条件表达式的多分支路径。6.4 排查顺序从数据到代码逐层排除遇到运算符和数据类型相关的缺陷时推荐按以下顺序排查排查顺序检查对象具体动作1测试数据输入类型是否正确是否有隐式转换是否有边界值2断言方式是否使用比较浮点数或字符串是否使用 equals3字段类型前端、接口、数据库字段类型是否一致4运算表达式是否存在整数除法、溢出、优先级不明确5异常日志是否出现 NumberFormatException、NullPointerException按照这个顺序排查可以快速定位大部分问题。不要一上来就怀疑数据库或网络先确认数据本身和比较方式是否正确。7. 本课要点清单与学习建议7.1 可复用检查清单以后设计测试用例或排查缺陷时可以直接对照这份清单[ ] 每个字段是否覆盖了合法边界、非法边界、空值、null、特殊字符和超长数据。[ ] 数值型字段是否确认了类型范围是否包含最大值、最大值 1、最小值。[ ] 浮点数断言是否使用误差范围或 BigDecimal。[ ] 字符串比较是否使用 equals 而不是。[ ] 条件表达式是否考虑短路求值null 判断是否放在前面。[ ] 接口字段是否同时校验类型和值。[ ] 涉及类型转换时是否覆盖数字字符串、空字符串、null、超长数字和负数。[ ] SQL 查询中的字段类型与数据库字段类型是否一致。[ ] 金额、利率、折扣字段是否使用精确数值类型。[ ] 运算符表达式是否有括号保证优先级清晰。7.2 面试与项目中的高频问法软件测试面试中运算符和数据类型相关的题目出现频率很高。常见问法包括0.1 0.2的结果是什么为什么Java 中和 equals 的区别是什么int 类型最大值加 1 结果是什么字符串比较为什么不能用和有什么区别强制转换会带来哪些风险设计测试用例时如何利用边界值分析覆盖类型边界这些问题的答案要点实际上都分布在本课各个章节中。面试时不要只背结论要能结合一个具体项目场景说明测试用例是如何设计的。7.3 下一步学习建议这一课的内容是软件测试入门到精通路径中从“理解概念”走向“动手测试”的转折点。熟练掌握后可以先做一个练习为登录功能编写一份完整的边界值测试用例覆盖字符串长度、空值、空格、Unicode 字符和特殊符号然后尝试用任意一门编程语言写一段自动化断言分别比较浮点数、字符串和整数的断言方式差异。下一步建议进入第三课也就是软件测试流程与测试用例设计方法。到时会把等价类划分、边界值分析、判定表、场景法等具体方法应用到项目实战中而这一课掌握的数据类型和运算符知识会成为用例设计时判断“数据要不要测、边界在哪里、断言怎么写”的基础能力。
返回列表