ARTICLE DETAIL

资讯详情

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

语言哲学与测试基因:从需求歧义到可断言用例的设计之道

语言哲学与测试基因:从需求歧义到可断言用例的设计之道 上个月做测试用例评审团队里出现了一个很有意思的分歧。有人评论某个用例写得像哲学论文另一拨人反驳说测试用例本来就该像合同一样精确。这句话在我脑子里转了很久后来我得出的结论是我们团队里其实存在两种截然不同的思维基因一种靠近语言哲学一种靠近测试科学。它们在很多地方互相看不顺眼但真正的问题不是让谁消灭谁而是搞清楚分野在哪里然后让两者在同一个项目里各司其职。这篇文章想聊的就是这个话题。语言哲学关心的从来不是语法正确而是意义如何产生、语言如何塑造我们对现实的理解。测试基因关心的则是验证与证据——任何一句话、一个需求、一行代码在它面前都被翻译成“可观察、可断言、可失败”的结构。把这两个词放在一起不是为了做名词解释而是因为它们恰好站在同一个路口上面对不确定性我们用什么方式让彼此相信“我理解对了”下面我从概念拆解、真实冲突、实操方法三个角度展开最后用一个我经历过的支付模块案例收尾。全程不绕弯子都是能直接拿去用的东西。1. 语言哲学被多数开发者低估的“工程思维工具”1.1 “语言学转向”到底转了什么二十世纪哲学发生过一次著名的转向研究重心从“世界的本质是什么”转向“我们如何用语言描述世界”。听起来很学术但本质很简单我们所有对世界的理解都逃不开语言的过滤。你看到的不是“事实本身”而是“被语言表达过的事实”。工程领域每天都在发生这种事。产品经理写了一句“用户点击后觉得流畅”开发把它理解成“动画时长200毫秒”测试把它理解成“页面加载不超过1秒”。同一句话三个人拿到三个不同的“事实”。语言哲学提醒我们当你以为在讨论同一个东西时往往只是在用同一个词汇表达不同的事物。所以“语义即接口”这句话在工程里成立。你给变量起名、给接口写注释、给需求文档归拢术语本质上都是在定义一种语言契约。一个名叫isActive的布尔字段A理解为“当前页签是否激活”B理解为“用户是否在活跃期”C理解为“组件是否挂载完成”。这个字段本身没有错错的是它的名字没有约束住含义的边界。1.2 语言游戏团队分歧的根源维特根斯坦有个概念叫“语言游戏”意思是词语的意思取决于它被使用的场景就像棋子在不同棋局里规则不同。同一个词在围棋里有气在象棋里没有在五子棋里又完全是另一套判定。你没法脱离棋局谈棋子的意义。团队协作中最隐蔽的摩擦就来自这里。“轻量级”这个词在你眼里是“不引入外部框架”在他眼里是“代码行数少于500行”在我眼里是“新人上手不需要看文档”。我们各自都以为自己在说同一件事直到实现方案对不上才发现根本没玩同一个游戏。我见过最典型的一次需求明确写“接口需要做幂等处理”开发按“重复请求返回第一次的结果”实现测试按“重复请求产生两次不同结果必须报错”设计用例。两边都觉得自己是对的。最后翻文档发现“幂等”在需求上下文里没有定义完全是两个语言游戏撞在一起。1.3 可说与不可说需求文档的边界语言哲学里还有一个重要区分能说清楚的和不能说清楚的。能说清楚的要用准确的语言表达不能说清楚的通常藏在 tacit knowledge 里靠默契、手感、经验传递。需求文档的尴尬恰恰在这里。很多关键体验属于“不可说”的部分比如“流畅”“精致”“有质感”你没办法精确地用语言捕捉它只能靠用户感受。但测试基因不接受这种模糊它需要一个可验证的指标。这不是死局。我的做法是给每个“不可说”的词配一个或多个“代理指标”。“流畅”可以被代理为“主线程卡顿率为0”“页面交互响应不超过100ms”“动画帧率不低于50fps”。这些代理指标不是完美的翻译但总比让所有人都凭感觉理解要好。先承认有些东西说不太清再用约定好的代理物把它拉到可说清的范围内这是语言哲学给测试工作最大的启发。2. 测试基因把世界变成“可断言”的思维本能2.1 测试基因不是岗位而是一种世界观很多人以为“测试基因”是指善于找bug或者细心、有耐心。我觉得这只是表象。真正的测试基因是一种世界观一切结论都可以被质疑一切判断都需要证据一切描述都必须能翻译成可观察的断言。有这种基因的人听到“系统很稳定”时脑子里自动冒出三个问题稳定怎么度量多久不宕机算稳定在什么流量下稳定听到“用户喜欢这个新功能”时第一反应是喜欢用什么指标衡量留存率提升了几个点NPS涨了多少他们不是故意抬杠而是大脑默认运转方式就是这样——把模糊的描述拆成可验证的命题。这种世界观放在工程里就是断言思维。代码里写assert测试里写expect需求评审里问“通过标准是什么”本质都是同一个动作给想法装上一个“失败条件”。没有失败条件的描述在他们看来等于没有描述。2.2 测试基因的三种典型表达我观察下来测试基因在工程实践里通常有三种表达形态。第一种是断言式表达。最典型的就是测试代码和契约测试。你用expect(result).toEqual(...)把预期写死用assert order.status paid给状态设置关卡。这种表达的价值在于把“应该怎样”变成机器可判定的真伪问题而不是停留在口头共识。第二种是探索式提问。做测试设计时不是照着用例脚本一步步走而是不断问“如果我这样操作会发生什么”“如果这个字段为空后续逻辑会怎样”。这种提问方式可以把隐藏的假设挖出来。我在做探索性测试时经常因为一个“如果”找到连开发都没意识到的问题。第三种是可量化反馈。覆盖率、缺陷密度、响应时间、失败率这些都是测试基因偏爱的语言。它们把质量从“感觉还行”变成一串可以对比的数值。但这里也藏着后患数值一旦变成目标就会反过来扭曲行为。2.3 当“可测”成为唯一标准任何优点走极端都会变成缺点。测试基因最大的风险是把“可测”当成世界唯一的衡量标准最后只关注那些能被度量的东西却忽视了真正重要但难以度量的事物。用户是否喜欢你的产品、设计师是否认为界面美观、工程师写代码时是否顺手这些都没法用一条断言表达。如果团队里的测试基因过于强势会出现一种荒谬的结果所有指标都在上涨用户却在流失。因为你在优化的是那些“能测的东西”而不是那些“有价值的东西”。这恰恰需要语言哲学来兜底。语言哲学提醒我们还有很多真实体验存在于语言边界之外无法被低成本地断言。如果你硬要度量它们你得到的可能只是一个扭曲的代理变量而不是真实本身。两种思维必须有对话否则会互相掏空。3. 两种基因的真实摩擦我在项目里看到的分野3.1 同一份需求三种理解有一次评审一个“信息流推荐”的需求产品写了“让用户看到更多感兴趣的内容”。会议室里出现了三种理解开发的理解是召回算法要增加候选集合从1000扩到5000排序策略调整。测试的理解是需要一个指标来猜测用户“感兴趣”否则没法断言于是建议用“点击率提升5%”来验收。语言敏感的同学则质疑“感兴趣”是主观感受点击率高也可能是因为标题党。三拨人对同一个句子做了完全不同的操作化翻译。没有谁对谁错但如果不处理这个需求就会各自按各自的想象去实现。后来我们把“感兴趣”拆成了三个可测指标点击率、停留时长、主动收藏率同时限制了“不能牺牲已读用户反馈”。这个过程本质上就是把哲学问题翻译成测试问题。3.2 命名战争隐喻派与精确派我发现一个特别典型的分野体现在命名上。有些人的命名偏好隐喻喜欢用“漏斗”“漏斗上方的沙子”“北极星指标”这种有画面感的词。这些词能帮助团队快速建立全局理解但也容易产生歧义漏斗到底是哪一段北极星到底指哪个指标另一些人的命名偏好精确坚持用funnel_stage_1、north_star_metric_name、subscription_status_code看起来无趣但机器不会理解错测试用例也可以直接引用。两者的分歧不是审美问题而是信息密度的取舍。隐喻派牺牲精确性换取可传播性精确派牺牲生动性换取可验证性。一个团队如果长期没有在这件事上达成共识就会出现“文档里写漏斗代码里叫funnel测试用例里叫 flow_1”的混乱局面。我的建议是保留隐喻作为沟通语言但落到测试和代码里统一用精确命名同时维护一张术语对照表让两边随时可以互相翻译。3.3 质疑的含义不同抬杠还是专业另一个让我印象很深的分野是“质疑”这件事。语言哲学式的人在评审会上说“我觉得这个词用得不准确”可能会被当成抬杠。测试基因式的人说“这个场景没有断言万一出现异常怎么办”会被当成专业。同样是批判性思维只是因为表达方式不同得到的评价截然不同。我曾经不止一次看到这样的场面新来的同事在需求会上追问“什么叫用户体验好”被产品经理一句“你先用用看”怼了回来。但如果是测试组长问同样的问题产品就会认真给出指标。问题不在问得对不对而在提问者是否被允许进入“语言博弈”的场域。我的处理办法是把语义质疑翻译成测试问题再放到测试的语境里去问。不说“这个词不准确”而是说“我无法为这个描述设计通过标准我需要你告诉我什么样的观察结果能让它通过什么样的结果会让它失败”。这句话一出口立场就从“哲学质疑”变成了“测试需求”几乎没人会拒绝回答。4. 把两种基因装进同一个工具箱4.1 给“术语”写断言我在团队里推行过一个很朴素的做法给关键术语写断言。普通术语表只有一句话解释比如“有效订单已支付且未取消的订单”。这种做法很容易被忽略因为解释只是文字看了就忘。我改成写断言把这些解释变成机器可执行的定义def assert_valid_order(order): assert order.id is not None, 订单号不能为空 assert order.paid_at is not None, 有效订单必须已支付 assert order.status ! cancelled, 有效订单不能处于取消状态 assert order.amount 0, 有效订单金额必须大于0这样写下来“有效订单”这个概念就从模糊的自然语言变成了无歧义、可测试、可回归的定义。任何人修改订单状态流转逻辑时跑一遍这个断言就知道自己有没有破坏“有效订单”的定义。这个概念相当于给术语装上了“失败条件”让模糊的约定变得可验证。我还让测试团队养成了一个习惯评审需求时把里面所有名词性关键词提取出来逐个问“能不能为它写断言”。能写的划入明确需求不能写的标记为待澄清优先进入下一个会。这套流程跑下来需求评审会的时间变长了但开发阶段返工率明显下降。4.2 用语义分析做边界测试测试设计里讲究边界值分析但大多数时候只关注数值边界0、负数、最大值。我后来发现语义边界同样值得测。一个词的含义是有边界的跨界之后意义会发生突变。比如登录页的“记住我”语义上是“记住登录状态”。但“记住”的边界是什么记住多久记住哪些设备如果用户换了浏览器呢如果中间改了密码呢这些语义边界如果不搞清楚测试用例就只能覆盖“勾选后下次自动登录”的正向场景漏掉大量模糊地带。把语义问题翻译成测试问题有一个固定句式从“这个词是什么意思”变成“这个词在什么条件下失效”。用这个句式去拆“记住我”就能得到一串真实的测试场景勾选“记住我”后关闭浏览器再打开登录态是否保留勾选后超过30天未访问登录态是否失效用户在A设备勾选“记住我”在B设备登录A设备是否被踢下线用户修改密码后旧的有效会话是否立即失效你看一个语言哲学式的追问最后产出了一组有逻辑的测试用例。这就是两种基因协作的最佳姿势用哲学思维发现这个词有边界用测试思维逼近每条边界的准确位置。4.3 落地沟通协议让分野变成分工只要团队还靠自然语言协作分歧就不可避免。不能消除分野但可以给分野定一套处理协议。我团队里现在有三条不写进文档但人人默认的规则。第一条任何关键数字必须带上下文。“性能提升了30%”这句话不入会要改成“在8核16G的压测环境下接口P95延迟从200ms降到140ms降幅30%”。这样每个人都能独立验证而不是在头脑里各自补全上下文。第二条任何验收标准必须带失败条件。描述“实现搜索功能”不算完成必须说“输入中文关键词返回结果输入空字符串有拦截提示接口超时有兜底页面三者任何一个不满足都算失败”。每个用例都有失败条件测试才有据可依。第三条任何建议被拒绝时要给出一句话理由。“为什么不做这个用例”“因为当前版本这个入口已经下线。”如果给不出理由就继续讨论。这条规则把很多情绪化的争论转化为逻辑挑战团队氛围反而更清爽了。4.4 一个支付模块的复盘退款还是撤回最后分享一个真实案例。我们做过一个支付相关功能需求文档里写“支持订单退款”。评审时语言敏感的同学问了一句“‘退款’和‘撤回’是一回事吗”当时全场沉默了三秒然后产品、开发、测试开始各说各话。开发说退款就是调支付渠道的退款接口把钱原路返回。产品说用户在支付成功后马上取消应该叫撤回渠道都不需要处理。测试说那撤回和退款的用例返回结果一样吗展示给用户的文案一样吗金额计算方式一样吗这次讨论的结论是这两个词背后的业务状态完全不同调用渠道、对账逻辑、用户通知文案都不一样。如果当时不澄清测试会按一个通用“退款”流程设计用例结果上线前两天才发现支付结果页文案、状态字段、对账报表全部对不上返工成本极高。复盘时我们一致认为问题不在测试而在于团队没有在“语言层”做一次对齐。把这个教训固化下来之后我们新增了一条流程涉及资金、状态、权限的命名变更必须经过一次术语评审。评审不需要很长但必须回答三个问题这个词指的是什么状态什么操作会进入这个状态这个状态失败时提示什么回答完这三个问题语言上的坑基本就填平了。5. 最后的几点体会我一直觉得所谓的“语言哲学”和“测试基因”并不是两条平行线它们更像是同一个人的左脑和右脑一个负责理解意义一个负责验证真伪。少了理解验证会变成刻板的数字游戏少了验证理解会变成漂亮的空话。我个人的实操体会是解决方案永远不在“改掉某个人的习惯”上而在“让两套思维开始对话”。给术语写断言、用语义分析扩充测试边界、给团队定沟通协议本质上都是给这种对话搭建翻译层。翻译层越稳理解偏差越少返工越少。下次再遇到团队里因为一个词争得面红耳赤不妨先别急着站队先问一句“你说的这个词可测吗失败条件是什么”大概率争辩会立刻变成一道具体的执行题。这个技巧我用了很多年几乎没失灵过。
返回列表