ARTICLE DETAIL

资讯详情

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

多项式黑盒的两次测试:差分法看穿未知系统

多项式黑盒的两次测试:差分法看穿未知系统 很多人第一次听说“多项式黑盒”这个词第一反应是这不就是个输入输出规则未知的盒子吗没错它确实是个盒子但真正让它有意思的地方在于——你不知道里面装的是什么数学函数却可以通过少量输入输出反推出它的“骨架”。这就好像你走进一家餐厅老板不给你菜单只让你点几道菜猜出后厨的全部配方。我做自动化测试这些年遇到过太多类似的场景一个接口、一个加密模块、一个第三方服务文档缺失、代码不可见唯一的沟通方式就是“丢参数进去看结果出来”。这种时候如果只会一组一组去试效率低到让人怀疑人生但如果掌握了“多项式黑盒”的测试思路是真的可以实现降维打击的。这篇文章不聊高深的理论就聊我实际怎么用两次测试去“调戏”一个多项式黑盒以及背后的测试设计逻辑。无论你是刚入门测试开发还是已经写了几年用例的熟手只要你想在未知系统面前少走弯路这篇文章应该能给你一点不一样的启发。1. 这不只是“猜盒子”这是反向设计器1.1 什么是多项式黑盒先解释一下“多项式黑盒”到底是什么。假设有一个系统它接收一个输入参数 x然后返回一个输出 y你完全看不到内部计算过程只知道结果是一个数字。这种情况下如果内部关系恰好可以用多项式来描述那这个系统就是一个多项式黑盒。比如y 3x 1或者y 2x^3 - 5x^2 x - 7甚至更复杂但只要是多项式它就具备一个非常漂亮的数学性质——多项式是“光滑”且有“规律”的。什么意思呢就是当你改变输入的时候输出的变化不会突然变得完全没有逻辑它会遵循一定的趋势。这种性质让黑盒测试变得特别有章可循。我在实际工作中发现很多测试对象虽然不叫“多项式”但行为上却很像多项式。比如某些排序接口的效率曲线、某些推荐系统的打分函数、某些硬件设备的温度-电压响应关系它们在局部范围内都可以近似看作多项式。所以“多项式黑盒”这个思路不只是一个数学概念它更是一种测试策略。1.2 黑盒测试的核心困境做黑盒测试的人最怕什么不是系统太复杂而是不知道从哪里入手。特别是当你面对一个完全陌生的接口功能文档写得像天书甚至压根没有文档的时候你只能靠不断地发送请求、观察响应、猜测逻辑。传统的做法是“全量覆盖”把输入空间的所有值都试一遍。听起来很严谨但实际上是不可能的。因为输入空间往往非常大甚至接近无限。比如一个接收浮点数的接口可能的输入是无穷多个你总不能每个数都试吧于是测试设计就变成了一个“降维”问题如何在有限的测试次数内最大程度地还原黑盒的真实行为我这里说的“降维打击”就是从尽可能少的样本点反推出多项式黑盒的阶数、系数范围甚至是内部逻辑特征。两次测试听起来像是开玩笑但如果你会用“差分”的思想去做它真的能给你很多线索。1.3 谁适合读这篇文章如果你是以下几类人这篇文章的实操价值会比较大自动化测试工程师每天跟接口、模块、未知服务打交道需要设计高效的测试用例而不是盲目堆数据。测试开发新人想知道黑盒测试除了“猜测验证”之外还有没有更系统的方法论。做安全测试、渗透测试的朋友黑盒思维是渗透测试的底层能力之一掌握推理技巧比瞎打有用得多。对数学和测试结合感兴趣的人你会发现原来初等数学里的多项式在测试领域有这么大的用处。2. 两次测试的底层逻辑差分与降维2.1 差分黑盒的“温度计”想要“调戏”黑盒第一步是找到一个好用的工具。我用的最顺手的一个工具就是“差分”。什么叫差分简单说就是看输出值随输入值变化而产生的变化量。假设我输入 x1得到 y1再用一个稍微不同的输入 x2得到 y2。那么一阶差分 y2 - y1如果你的两个输入之间差值固定比如 x2 x1 1那么一阶差分反映的就是黑盒的“瞬时变化趋势”。如果一阶差分始终是一个常数那说明黑盒大概率是一个一次函数也就是 y ax b。如果一阶差分在变化那就需要看二阶差分。这里我打个比方。你开车时看仪表盘车速就是输出油门踏板深度就是输入。如果你踩一点油门速度提升一点再踩一点速度又提升一点而且每次提升的量都差不多那这辆车的动力响应就非常接近线性关系。但如果你踩一下油门速度飙升再踩一下速度不动那就说明内部逻辑比较复杂不是简单的一次函数能描述的。2.2 二阶差分识别二次以上关系的钥匙当你发现一阶差分不是常数时别慌再计算二阶差分。二阶差分 一阶差分的差分如果二阶差分是常数那黑盒大概率是一个二次多项式如果二阶差分是常数但数值是零那其实还得回到一阶差分去看因为那说明一阶差分本身是常数。我做过一个实际项目一个交易手续费计算模块文档里写的是“按笔收费但不同金额区间费率不同”。这听起来像分段函数但当我用差分法测了一组数据后发现二阶差分呈现出明显的规律我立刻怀疑内部不是一个简单的分段表而是一个多项式拟合函数。后来验证果然如此。原来需求方为了让用户感觉“越买越划算”内部用了二次多项式来平滑费率曲线。这就是差分法的价值。你不需要知道内部代码长什么样只需要通过两次输入输出你就能判断出它的“数学特征”。如果你愿意多测几次把差分表列出来黑盒的“底裤”基本就被看穿了。2.3 为什么是“两次”测试那标题里说的“两次测试”是什么意思呢当然不是说真的只测两次就完事而是强调“最小样本”这个概念。很多测试新人以为测试用例越多越好但真正会做测试的人追求的是用最少的输入获取最多的信息。两次测试最多只能得到一个差分值也就是一阶差分。这个值本身就能告诉你很多信息如果输出差值固定说明黑盒很可能是线性的如果输出差值在剧烈波动说明黑盒里藏着非线性逻辑甚至分段逻辑。用两次测试“调戏”黑盒本质上是在做信息的“第一口尝味”。尝完之后你就知道该往哪个方向继续深挖。所以两次测试不是终点而是起点是一个非常聪明的起点。3. 经典套路一构造同根族让黑盒自曝家门3.1 什么是同根族现在来说我实战中最喜欢用的一招构造同根族测试数据。同根族的意思是我一次性构造出一批输入值它们之间具有某种共同的数学关系比如都是某个数的倍数或者在某个区间内均匀分布。通过这些数据对黑盒进行批量测试观察输出的规律从而快速定位黑盒的内部结构。这里有一个很重要的原则不要随机输入要有数学意识地输入。比如说我怀疑某个黑盒是一个二次多项式那么我会设计一组输入x 1, 2, 3, 4, 5然后记录对应的 y。如果二阶差分恒定那我基本可以确认它是二次的。接下来我用三次测试去验证x 10, 20, 30如果输出变化符合预期的多项式增长趋势那就可以进一步缩小系数的范围。3.2 实战示例用4个测试点看出二次多项式有一次我需要测试一个外部对接接口它接收一个“数量”参数返回一个“总价”。文档只写了一句“数量不同单价不同总价按规则计算”。这种描述跟没写一样。我用随机数据试了几次发现毫无头绪越试越乱。于是我改用差分法。第一组测试输入 x输出 y112223一阶差分 23 - 12 11第二组测试输入 x输出 y338一阶差分 38 - 23 15此时两次一阶差分分别是11和15不是常数变化了4。我怀疑二次项存在于是继续测输入 x输出 y457一阶差分 57 - 38 19现在一阶差分序列是 11, 15, 19。它们每次增加4说明二阶差分是常数4。这就非常可疑了内部函数大概率是 2x^2 bx c 的形式。我再加测一个点 x 5得到 y 80。一阶差分 80 - 57 23完美延续了“每次增加4”的规律。到了这一步我就可以大胆预测这个黑盒是一个二次多项式。然后我再根据前几个点的值反推出系数x 1, y 12x 2, y 23x 3, y 38三个方程三个未知数解一下设 y ax^2 bx ca b c 124a 2b c 239a 3b c 38解得 a 2, b 1, c 9。注意这只是一个推测最终一定要用更多测试点来验证。但至少我已经从“完全陌生”变成了“心里有数”。3.3 同根族的边界条件补充在实际测试中光看二阶差分还不够因为黑盒可能是分段函数只是在某个区间内看起来像多项式。所以我会额外设计一组“跨界测试”专门用来探测边界行为。比如刚才的例子我已经确认它在 x 1 到 5 之间是二次函数但我还会测x 0看是否存在无意义的输出x -1看是否有异常输入处理x 10、100看趋势是否还在延续这些测试点不一定能用差分法直接分析但可以帮助我们发现分段点或者隐藏规则。关于边界值我有个建议优先测试那些“有业务含义”的边界。比如数量为零、数量为负数、数量超过预期上限、刚好是整数边界这些值最能暴露黑盒的真实面目也比随机测试值有效得多。4. 经典套路二泛化与拆台让黑盒漏出马脚4.1 从多项式到指数与对数上面说的都是“标准多项式”的情况但真实世界里的黑盒往往不守规矩。有些黑盒表面上看起来像多项式但内部其实是指数函数、对数函数或者三角函数。这时差分法依然有用但需要换一种解读方式。指数函数的特征是一阶差分本身也呈现指数增长。对数函数的特征是一阶差分在逐渐减小越来越接近零。三角函数则会出现周期性波动。遇到这种情况我会采用“取对数”的方法来降维。如果 y 和 x 之间是指数关系那么对 y 取对数之后它和 x 之间就变成了线性关系。我就可以用检测一次函数的办法去验证这个猜测。这么说可能有点抽象我举个例子。某个黑盒输入 x 1输出 y ≈ 3输入 x 2输出 y ≈ 9输入 x 3输出 y ≈ 27一阶差分分别是 6 和 18不是常数但比例刚好是 3 倍。这时如果取 y 的自然对数得到的序列大约是 1.10、2.20、3.30呈现非常好的线性关系。这说明黑盒很可能是一个以 e 为底的指数函数而不是一个高次多项式。测试的乐趣就在这里。当你发现差分法“失灵”的时候不要急着放弃换一个变换方式往往就能柳暗花明。4.2 数值探测用大输入让黑盒“露馅”第二种拆台的办法是用极端的输入值去试探。多项式在输入很大时增长速度和它的最高阶次有关。一次函数是线性增长二次函数是平方增长三次函数是立方增长。如果你在 x 1, 10, 100, 1000 这组输入上观察输出看输出值的变化速率就可以判断出最高阶次。但这里有一个陷阱如果黑盒内部用了浮点计算或者有溢出保护、上限钳位、取整逻辑那么输入很大的时候输出可能并不符合多项式规律而是被“截断”了。这恰恰也暴露了黑盒的另一个行为特征——它有边界保护。我做接口测试时经常遇到这种情况接口文档声称“支持任意数值”但实际上输入一个超大值就直接返回了某个固定错误码或者把所有超过范围的输入都映射到同一个上限值。这种“截断行为”本身就是一条重要测试发现对上线前的风险评估非常关键。4.3 参数空间的“组合拳”黑盒的输入往往不只有一个参数。如果是多参数黑盒事情就复杂很多但也更有意思。我常用的策略是“控制变量法”固定其他参数不变只改变一个参数用差分法分析这个参数对输出的影响。然后换一个参数继续分析。直到所有参数的影响规律都掌握之后再尝试多参数联合的边界测试。举个例子一个黑盒接收两个参数 a 和 b。我先固定 b 1改变 a看输出然后固定 a 1改变 b看输出。这样可以得到两个“单变量曲线”。如果发现 a 对输出的影响符合一次函数而 b 对输出的影响符合二次函数那大概率内部是一个类似 a b^2 的关系。再用几个联合测试来验证交互项是否存在比如测 (a1,b1) 和 (a2,b2)看输出是否等于两个单变量效果之和。这一套组合拳打下来即使黑盒再复杂也能在有限测试次数内画出它的“行为等高线”。虽然没办法100%还原内部代码但已经足够支撑后续的测试断言和风险评估。5. 从“调戏”到“测试设计”等价划分与边界值的降维方法论5.1 等价划分把无限输入变成几个典型代表说完数学套路回到更通用的测试设计方法论。如果你想成为一个高效的测试开发者只会在数学上做差分是不够的你还需要一套标准的“降维”思维。这里我特别推崇等价类划分法。等价类划分的核心思想是如果一个黑盒对某一组输入的处理方式相同那么我们只需要测试这组里的一个代表值就能代表整组的情况。比如一个接口接收月份参数1到12如果它内部只是用月份去查一个固定的费率表那么1到12这12个值可以划分成合法月份类1到12和非法月份类0、负数、13、极小数等。在合法类里你不需要全测12个测试代表值比如1、6、12基本就够了。当然如果你有时间全测也很快但在大规模系统里这种思维可以帮你节省大量时间。5.2 边界值分析最容易出bug的角角落落边界值分析是等价划分的好搭档。大量实践表明黑盒的bug往往集中在输入范围的边缘而不是中间值。什么叫做边界值就是合法输入和非法输入的交界处。比如参数允许范围是1到100那么边界值就是1、100以及紧挨着边界的0、101。很多人会忽略“刚好等于边界”的情况而这恰恰是最容易出现逻辑错误的地方。我自己写自动化测试用例时凡是涉及数值范围的参数一定会有边界值用例。不是因为我有多细心而是踩过太多坑了。有一次测试一个计费模块边界值是10000结果系统在10000这个值上发生了浮点数精度误差导致费用计算少了0.01元。单看这0.01元微不足道但如果是高频交易系统一天几百万笔订单损失就大了。5.3 从黑盒测试到自动化测试框架的映射当你掌握了等价划分和边界值分析这些“降维”思路后下一步就是把这些思路固化到自动化测试框架里。我在使用pytest写自动化测试时有一个习惯把测试数据设计成参数化用例。这样既保留了数学推理的痕迹也方便后续维护。比如通过对黑盒的差分分析我推测它符合二次多项式那我就会构造一组参数化用例把预期输出和实测输出放在一起做断言。用pytest最舒服的一点是fixture可以让每个测试用例获得独立的测试环境不会因为上一个用例的副作用而影响下一个。在测试黑盒时这非常重要因为黑盒往往是有状态的比如它会记住上一次调用的参数、会触发限流逻辑、会缓存某些结果。如果测试用例之间共享状态很容易出现“第5个用例失败了但单独跑又能通过”的诡异现象。除了pytestappium在移动端自动化测试中也常遇到黑盒问题。App内部的某些模块无法直接访问代码只能通过UI操作来间接验证。这时候差分法的用处没那么直观但等价划分和边界值的思路依然适用。比如输入框的字符长度限制就是典型的边界值测试场景。6. 反面教材与翻车实录我踩过的那些坑6.1 盲目追求测试次数反而迷失方向我刚入行的时候有一种错误观念测试用例写得越多测试就越充分。于是面对一个黑盒接口我一口气构造了几百组输入然后跑了一大堆用例最终得出的结论是输出看起来好像没什么规律。后来我学会了差分法重新用十组精心设计的输入去测五分钟就发现了二次多项式特征。那一刻我意识到真正有价值的不是测试的数量而是测试信息的密度。盲目追求数量的问题在于当你把大量随机数据堆在一起时你看到的是“噪声”而不是“信号”。数据越多噪声越强。而差分法做的事情恰恰是把噪声给剥掉让信号变得清晰。6.2 把“推测”当成“实锤”还有一次我用差分法推测一个黑盒是二次多项式然后高高兴兴地写了一个断言直接把 y 2x^2 x 9 当成了内部实现。结果没过多久就发生了一个线上事故某条特殊数据进入系统后输出完全偏离了我的预测。后来排查发现那个黑盒根本不是一个单一的二次多项式而是一个分段函数。在前半段看起来像二次函数但在某个阈值之后逻辑完全切换成了另一个公式。我的样本点全落在阈值之前所以我的“推测”只有局部正确性。这次教训让我明白了一个道理黑盒测试只能提供“行为假设”永远不能替代“实现验证”。你可以通过测试来缩小可能性范围但绝对不能说“我完全知道它是怎么实现的了”。所以我现在做测试时每得到一个推测都会在报告里注明“推测置信度”而不是拍胸脯保证。6.3 忘记黑盒状态导致的连环失败黑盒测试里还有一个特别容易踩的坑状态残留。很多系统不是纯函数式的它内部有缓存、有计数器、有会话状态。你用同一组输入去调两次可能得到完全不同的结果。如果你不知道这一点第二次测试时你就会误以为第一次的结果是错的。我在用appium测试一个移动端功能时就遇到过这种问题。第一次输入一串文字界面正常第二次输入同样的文字界面却弹出了“重复提交”的警告。表面上看是同一个输入得到了不同输出好像黑盒行为不一致实际上是系统记录了第一次提交的状态第二次被认为是重复操作。所以测试黑盒之前先搞清楚它有没有状态残留是不是需要重置。这一条比任何高级的差分法都基础也比任何花哨的测试框架都重要。记住它能少踩很多坑。7. 给新手的三条可复制建议7.1 先记录一阶差分再谈分析如果你现在还不太会用差分法那我建议你先从一个最基础的习惯开始无论测什么黑盒都把你得到的输入输出记录成一张表然后手动算一下相邻输出的差值。这个习惯看起来简单但效果惊人。它会让你下意识地去关注“变化”而不是只关注“值”。当你开始关注变化的时候黑盒的规律就会像有了轮廓一样显现出来。我见过很多测试新人测试半天问他们“输出有什么规律”他们说不上来。因为他们只记了结果没有记录变化过程。而“变化过程”恰恰是黑盒测试里最值钱的信息。7.2 把“黑盒测试”当成侦探游戏黑盒测试很容易让人觉得枯燥因为它本质上是一遍又一遍地重复“输入-输出”的动作。但如果你换一个心态把黑盒当成一个嫌疑人把你的每一次测试都当成一条线索整个测试过程就变得像侦探游戏一样有意思。每一次差分、每一次边界测试都是在收集证据。证据多了嫌疑人的画像就清晰了。你去跟开发对质的时候也能更有底气“我在输入为2和3时看到了一阶差分变化4所以我怀疑内部有平方项你去确认一下。”这种交流方式开发听了也觉得你专业。7.3 把数学用到测试里会成为你的核心竞争力很多测试工程师听到“数学”两个字就头疼觉得那跟日常测试没什么关系。但恰恰是这种偏见让那些愿意把数学用到测试里的人拥有了更大的竞争力。你不需要成为数学高手只需要掌握初等数学里的一些基本工具多项式、差分、对数、比值。这些工具足够应对绝大多数黑盒分析需求。当你真的用它们解决了一个“所有人都在瞎猜”的问题时那种感觉是很爽的。我把这种能力叫做“测试者的数学肌肉”。它跟写用例、搭框架、配环境一样都是可以通过刻意练习练出来的。而且一旦练出来你就再也不会回到“全靠蛮力测试”的状态了。8. 给自动化测试框架里加点“数学味”8.1 pytest参数化里的差分用例设计既然前面提到了pytest我再具体说一说怎么把差分思维放到自动化测试用例里。假设我要测试一个未知黑盒我会写一个pytest参数化用例输入一组精心构造的数据点import pytest pytest.mark.parametrize( x, expected_delta, [ (1, None), (2, 11), # y2 - y1 (3, 15), # y3 - y2 (4, 19), # y4 - y3 (5, 23), # y5 - y4 ] ) def test_difference_sequence(x, expected_delta): # 调用黑盒 y_current call_black_box(x) y_previous call_black_box(x - 1) delta y_current - y_previous if expected_delta is not None: assert delta expected_delta, f一阶差分异常{delta}这个用例本身很简单但它的价值在于它把“差分规律”这个测试判断标准给固化下来了。以后黑盒代码改了如果一阶差分规律被破坏了这个用例就会立刻报警比你去人工对比输出值高效得多。当然如果黑盒有状态残留这种“调用两次”的方式就可能出错。所以在设计这类用例时要先确保黑盒是纯函数式或者在每次调用前后做好状态清理。8.2 appium自动化里的边界值启发再提一下appium。移动端测试经常要处理输入框的字符限制、列表的翻页边界、下拉刷新的触顶触底逻辑这些本质上都是边界值问题。我在写appium测试脚本时有一个套路先准备一份“边界值数据表”里面包含正常值、最小值、最大值、小于最小值、大于最大值、空值、超长字符串等。然后把这些数据逐一代入UI控件观察黑盒的响应。这样做的好处是你不需要去猜黑盒内部怎么实现只需要验证它在这些极端情况下是否“说人话”。比如一个登录框输入一个超长字符串后如果黑盒直接崩溃那就是一个严重的健壮性缺陷如果它提示“输入过长”那就是正常的表现。边界值测试的价值就是把这些隐藏的异常路径给探测出来避免真实用户踩到雷。8.3 从“单点验证”走向“行为趋势验证”最后想分享一个思路上的升级不要把自动化测试做成一个个孤立的“单点验证”而是要把它们组织成“行为趋势验证”。什么意思呢就是当你测试一个黑盒时不要只验证某个输入值对应的输出是否正确还要验证一组输入值之间是否存在连续性规律。比如一个黑盒在 x1 时输出 12在 x2 时输出 23这只能说明这两个点没问题。但如果 x3 时输出发生剧烈跳动哪怕这个值本身“业务上说得通”你也应该警惕是不是内部逻辑在边界处发生了不合理的突变这个思路可以很好地融入到自动化测试框架中。比如用pytest的fixture去构造一组连续输入然后在测试函数里同时校验每个点的输出和相邻点之间的差分。一旦差分出现异常就输出告警。这种“趋势级”的断言比单点断言更能发现潜在风险。9. 最后再分享一个我个人的小习惯写了这么多最后讲一个我自己一直在用的习惯每测完一个黑盒我都会把测试数据和差分结果画在一张草稿纸上。不是用复杂的工具就是最简单的折线图。把 x 和 y 描点连线再在旁边把一阶差分、二阶差分列出来。这张纸看起来像是随手涂鸦但对我来说它是理解黑盒行为最直观的入口。很多用代码看不出来的规律描几个点之后就“显灵”了。另外一个习惯是测试完一个黑盒我会顺手写一份简短的行为画像笔记记录三件事通过差分确认的阶数或函数趋势、边界值的特殊表现、以及仍未验证的盲区。等下次这个黑盒改版了我直接拿出这份笔记对比短短几分钟就能知道哪里变了、哪里没变。虽然有各种自动化测试工具和框架能用但“用数学推理去引导测试数据设计”这件事我觉得永远都不会过时。工具会换框架会升级但黑盒背后的规律始终藏在输入输出之间的那一道道差分里。
返回列表