news 2026/10/17 10:10:55

Postman Tests断言实战:自动校验接口返回结果与状态码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Postman Tests断言实战:自动校验接口返回结果与状态码

1. 先搞清楚为什么要写断言:Postman的Tests到底改变了什么

很多测试同学用Postman,其实只用了它三成功力:输入URL、选个方法、点Send,然后盯着Response看。接口少的时候还行,接口一多,几十上百个返回JSON堆在眼前,肉眼扫字段纯靠猜,经常把data里的嵌套字段看漏了,等代码写完了才发现拿错了值。等到回归测试的时候更崩溃——同一个接口改了返回结构,没人知道,全靠某一天线上报警才暴露出来。

Postman里那个一直被你忽略的Tests标签页,解决的就是这个问题。它允许你在请求发送完成之后运行一段JavaScript脚本,用脚本对返回结果做自动校验。校验结果会直接展示在下方的Test Results区域,绿勾是过,红叉是挂。你不需要再肉眼看返回值,也不用把JSON复制到本地跑脚本,直接在Postman里写完断言、点一次Send,工具就会告诉你这接口到底对不对。

标题里那句“Tests 断言校验返回结果”说白了就是干这个事。断言(Assertion)是自动化测试里的基础概念——你先声明一个“预期”,脚本拿实际的返回和预期比对,一致则通过,不一致则报错。Postman的Tests脚本就是给你一个写预期的地方,脚本跑完,每个断言的结果清清楚楚列出来。

所以,这篇内容适合谁?已经用Postman发过请求、但Tests页签一直是空的测试新手;写接口用例时靠堆请求数量来“假装测试”的进阶选手;以及想把接口自动校验跑进回归流程的测试开发。我按自己平时写接口用例的思路,把Tests从骨架到实战到踩坑整条线串一遍,争取你看完就能直接在自己的项目里开写。

2. 从零搭出可复用的Tests脚本骨架:pm.test、pm.response、pm.expect

2.1 一个最小的断言结构长什么样

打开Postman,选中任意请求,切到Tests标签页。你看到的是一块空白的代码编辑器,语言是JavaScript。默认情况下它导出pm这个全局对象,所有核心能力都是pm.xxx形式暴露出来的。

最简单的断言长这样:

pm.test("状态码是200", function () { pm.response.to.have.status(200); });

拆开看三层:

  • pm.test("名称", 函数):声明一个测试用例。第一个参数是给这个用例起的名字,结果面板里显示的就是它;第二个参数是具体执行校验的函数,函数体里写断言逻辑。
  • pm.response:当前请求的响应对象,里面包含状态码、响应头、响应体等所有返回信息。
  • .to.have.status(200):BDD风格的链式断言,连在一起读就是“响应应该拥有200状态码”。

写完之后点Send,底部Test Results区域会出现一行:名称是你写的用例名,右侧2xx旁边有个绿色对勾。这样一个断言就跑通了。

2.2 pm.expect背后的Chai断言风格

光用pm.response.to.have.xxx这种链式写法能覆盖的场景有限,真正的主力是pm.expect。它会暴露一个expect(实际值)函数,然后你在后面接各种断言方法。我第一次用的时候感觉味道很熟悉,后来查了下,这是Postman内置的Chai.js断言库的BDD风格子集。

举例:

pm.test("响应体包含指定字段", function () { const jsonData = pm.response.json(); pm.expect(jsonData).to.be.an("object"); pm.expect(jsonData.code).to.equal(0); pm.expect(jsonData.message).to.equal("success"); });

这里pm.response.json()是Postman提供的方法,把响应体按JSON解析成JavaScript对象。然后对解析结果做三层校验:先确认它是个对象,再检查code字段等于0,再检查message字段等于"success"。

pm.expect的链式断言读起来和英文句子一样,.to.be.an是“应该是一个”,.to.equal是“应该等于”。这套语法写顺手之后,看代码基本不需要注释,断言本身可读性就很高。

常用方法我列个表,方便平时代查:

场景写法
值相等pm.expect(value).to.equal(期望值)
类型判断pm.expect(value).to.be.an("string")/("array")/("object")
是否为真pm.expect(value).to.be.true
包含子串/元素pm.expect(str).to.include("子串")
数组长度pm.expect(arr).to.have.lengthOf(3)
匹配正则pm.expect(str).to.match(/^[0-9]+$/)
属于某个枚举集合pm.expect(value).to.be.oneOf(["a", "b", "c"])
不为空pm.expect(value).to.not.be.empty

注意to.be.an("array")和to.have.lengthOf这种组合,能覆盖绝大多数接口返回的校验需求。

2.3 处理响应体的三个高频入口

Tests脚本里拿响应体有三种姿势,我用下来觉得按优先级排是这样:

  1. pm.response.json():最常用,直接把JSON字符串解析成对象。解析失败会抛异常,所以只适合确定响应是JSON的场景。
  2. pm.response.text():拿原始文本,适合HTML、XML、纯文本响应,或者你想自己正则去匹配的场景。
  3. pm.response.headers/pm.response.headers.get("Content-Type"):拿响应头,适合校验Content-Type、Set-Cookie、分页参数这类信息。

举一个实际点的例子——某登录接口,返回体里既有token,又有用户基本信息,校验如下:

const res = pm.response.json(); pm.test("登录接口返回结构完整", function () { pm.expect(res).to.be.an("object"); pm.expect(res).to.have.property("token"); pm.expect(res).to.have.property("userInfo"); pm.expect(res.userInfo).to.be.an("object"); }); pm.test("token格式非空且为字符串", function () { pm.expect(res.token).to.be.a("string"); pm.expect(res.token.length).to.be.greaterThan(0); });

这里有一个细节:pm.expect(res).to.have.property("token")只判断“有这个字段”,不判断字段值合理不合理。所以紧接着我又补了一条token必须是非空字符串。校验维度越细,接口出问题时你定位越快。

3. 不同业务场景下的断言写法:状态码、字段、数组与嵌套

3.1 状态码与响应头:最基础但最容易被忽略

很多接口文档把状态码定义成“2xx 表示成功”,但实际返回里还混着3xx重定向、4xx参数错、5xx服务端异常。我见过不少用例只断言了200,结果某天接口改成返回201 Created,测试直接绿。不是代码对了,是断言漏了。

稳妥的做法是分组判断:

pm.test("响应码为2xx", function () { const code = pm.response.code; pm.expect(code).to.be.at.least(200); pm.expect(code).to.be.below(300); }); pm.test("响应头包含JSON声明", function () { pm.expect(pm.response.headers.get("Content-Type")).to.include("application/json"); });

pm.response.code拿数字状态码。.to.be.at.least(200)和.to.be.below(300)合起来就是“200 ≤ code < 300”。这个写法比逐个枚举状态码更抗变化,接口文档只要不改成2xx以外的成功码,这个断言就始终有效。

3.2 字段级断言:区分“有字段”和“值正确”

接口校验最核心的是字段级断言。我自己的习惯是每一条测试用例里只做一组相关断言,不要把十几个断言糅进一个大函数里。这样哪块挂了,结果面板能直接说是哪个业务字段不对。

比如一个用户查询接口:

const data = pm.response.json().data; pm.test("用户ID为正整数", function () { pm.expect(data.userId).to.be.a("number"); pm.expect(data.userId).to.be.greaterThan(0); pm.expect(Number.isInteger(data.userId)).to.be.true; }); pm.test("用户姓名非空且为字符串", function () { pm.expect(data.userName).to.be.a("string"); pm.expect(data.userName.length).to.be.greaterThan(0); }); pm.test("用户状态属于合法枚举", function () { pm.expect(data.status).to.be.oneOf([0, 1, 2]); });

这里值得多说几句。pm.expect(Number.isInteger(data.userId)).to.be.true是用原生JavaScript判断整数,再拿返回结果和true做断言。你当然可以直接pm.expect(data.userId).to.be.a("number"),但“数字”和“整数”是两码事,接口返回1.5你第一层断言照样绿。所以关键字段要带上类型之外的业务约束。

3.3 数组与嵌套结构:不要只会JSON.stringify比对

列表类接口返回的data通常是个数组,里面的每一项又是对象。很多人拿到数组后只会整段JSON.stringify然后和期望字符串比对,一旦字段顺序变化或者多了个时间戳就全报不通过。更稳的做法是逐项取字段做断言。

Postman内置的Chai断言支持eachItem和find这类迭代写法。比如校验订单列表:

const list = pm.response.json().data; pm.test("订单数量在合理范围内", function () { pm.expect(list).to.be.an("array"); pm.expect(list.length).to.be.greaterThan(0); pm.expect(list.length).to.be.below(100); }); pm.test("每条订单都必须含订单号和金额", function () { list.forEach(function (item) { pm.expect(item).to.have.property("orderId"); pm.expect(item).to.have.property("amount"); }); }); pm.test("金额字段类型统一为数字", function () { list.forEach(function (item) { pm.expect(item.amount).to.be.a("number"); }); });

需要注意,我这里的forEach是在普通函数作用域里调用pm.test的,和直接在pm.test函数体内写循环没有本质区别——每个pm.expect如果失败,它归属的测试用例就是红叉。如果你希望“某个字段只要有一条不满足就算整组挂掉”,这样写在结果面板里的表现其实是“每条订单都必须含订单号和金额”这一整条用例失败。

还有一种场景:你需要从列表里找到某个特定项,再针对它断言。比如从订单列表里找出金额最大的那一条:

const list = pm.response.json().data; const maxOrder = list.reduce(function (prev, cur) { return prev.amount > cur.amount ? prev : cur; }); pm.test("金额最大订单的状态必须是已支付", function () { pm.expect(maxOrder.status).to.equal("PAID"); });

这种写法在接口测试里非常实用——真实业务不会每次返回固定顺序或固定内容,你要做的是“在变化的数据里抽出一个确定性结论去验证”。这也是用好Tests脚本的一个心态转变:断言不是死记硬背响应样例,而是总结业务规律并验证它。

3.4 正则与自定义校验:解决规则型字段

有些字段表面上是个字符串,实际内部有规则要求。最典型的是ID编号带前缀、时间戳格式、手机号脱敏、邮箱格式等。这类用pm.expect(...).to.match最顺手。

pm.test("订单编号符合LS+8位数字规则", function () { pm.expect(data.orderNo).to.match(/^LS[0-9]{8}$/); }); pm.test("创建时间符合YYYY-MM-DD HH:mm:ss格式", function () { pm.expect(data.createTime).to.match(/^20[0-9]{2}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/); });

正则写多了难免踩坑,我的经验是:先想清楚边界条件。上例中^LS[0-9]{8}$里的^和$分别表示字符串开头和末尾,缺了它,ABC-LS12345678-EXTRA这样的脏数据也会通过校验。时间格式那个正则只判断外形,没判断月份是否超过12,若你连“2020-99-99 99:99:99”都要拦住,那就得用更细的正则或者拆字段逐步断言。日常接口校验我不建议一步到位写极复杂的正则——先保证格式骨架对,再在后续流程里补业务规则校验,可维护性更好。

4. 数据关联:用断言把请求之间的依赖串起来

4.1 经典链路:登录拿Token再查业务数据

单条请求写断言只是起点,Postman真正体现“高级”的玩法是请求之间的数据传递与断言联动。最典型的是登录拿Token,后续接口带着Token请求。

实际操作分两步。第一步,在“登录”请求的Tests里,把返回的token存进环境变量:

const res = pm.response.json(); pm.test("登录接口返回token", function () { pm.expect(res.token).to.be.a("string"); pm.expect(res.token.length).to.be.greaterThan(0); }); pm.environment.set("access_token", res.token);

第二步,在“查询订单列表”请求的Authorization头或Headers里,引用{{access_token}}:

Key: Authorization Value: Bearer {{access_token}}

然后这个请求的Tests里,断言业务结果就行:

const data = pm.response.json().data; pm.test("携带token后可正常获取订单", function () { pm.expect(pm.response.code).to.equal(200); pm.expect(data).to.be.an("array"); });

这套链路里,Tests脚本承担了两个角色:既是登录接口的校验器,又是后续接口的“数据搬运工”。很多人忽略的是第二个角色的价值——你不光要校验当前请求,还要为下一个请求准备数据。环境变量就是请求之间的黏合剂。

4.2 用断言实现“拿到结果再决定下一步”

Postman的Tests脚本还能配合pm.execution和setNextRequest控制流程。比如你有一个“创建订单”的接口,创建成功之后要去查详情页确认落库数据一致。这时候可以在创建接口的Tests里先做创建结果的断言,成功后再跳转到详情接口:

const res = pm.response.json(); pm.test("创建订单返回成功", function () { pm.expect(res.code).to.equal(0); pm.expect(res).to.have.property("data"); pm.expect(res.data).to.have.property("orderId"); }); pm.environment.set("orderId", res.data.orderId); pm.execution.setNextRequest("查询订单详情");

这里的pm.execution.setNextRequest("查询订单详情")只能在Collection Runner里才生效。单独点Send时,Postman不会自动跳转。它本质上是给Runner指明“这条请求跑完之后下一条跑谁”。配合断言,就实现了“只有创建成功才继续往下查,创建失败就中断流程”的测试设计。

4.3 Collection Runner与数据文件驱动下的断言策略

到了Collection Runner阶段,你可能要给同一个接口跑多组测试数据。Postman支持导入CSV或JSON文件,Runner会按数据行逐个执行请求。这种情况下断言不能写死某一个值,而是要用pm.iterationData.get("字段名")去取当前行数据。

比如数据文件里有expectCode和expectMessage两列,请求Tests写成:

const expectedCode = pm.iterationData.get("expectCode"); const expectedMessage = pm.iterationData.get("expectMessage"); const res = pm.response.json(); pm.test("返回码与数据文件一致", function () { pm.expect(res.code).to.equal(expectedCode); }); pm.test("返回消息与数据文件一致", function () { pm.expect(res.message).to.equal(expectedMessage); });

这个模式下断言的价值会被放大:接口同一套逻辑,几十组数据进来,哪一组数据跑出不符合预期的返回,Runner运行完毕后能精确到行定位。手动拿一组数据调一次接口的工作量,在这里被压缩成了“改一行CSV、点一次Runner”。

4.4 动态数据下的断言别把随机值写死

接口测试里最让人心虚的事,就是断言里出现了写死的随机值。生成订单号时把期望值写死在某个字符串上,下次接口换成新的订单号,测试就变红了——实际上是断言写错,不是接口错了。

正确的做法是:要么从上一个请求的Tests里保存期望值,要么基于业务规则做校验。随机字符串可以校验长度和字符集,时间戳可以校验格式,自增ID可以校验比上一个请求的ID更大:

const currentId = pm.response.json().data.id; const previousId = pm.environment.get("lastId"); pm.test("新ID应比上次大", function () { pm.expect(currentId).to.be.greaterThan(previousId); }); pm.environment.set("lastId", currentId);

这类“相对断言”比“绝对断言”更适合动态环境,因为它针对的是系统不变的那条规则,而不是某一瞬间的数据。

5. 调试断言脚本:把每一个红叉变成有效信息

5.1 用Test Results面板逆向定位问题

写完断言,点Send,最怕的就是一片红叉,但不知道哪里错。其实Test Results面板已经给了线索:每一条测试用例名后面要么是对勾,要么是红叉,展开红叉用例能看到具体的断言失败信息。

举个例子。如果你写了:

pm.test("userName字段是字符串", function () { pm.expect(data.userName).to.be.a("string"); });

实际返回里userName是null,失败信息会显示 “expected null to be a string”。这个信息已经能说明问题了,但还不直观。更快的做法是先在脚本里把实际返回打印出来,看清楚再写断言。

5.2 console.log是测试者的第一工具

Tests脚本编辑器下方有个Console面板入口,在脚本里写console.log能输出调试信息。

我最常用的调试流程是三步:

  1. 先解析JSON并打印整体结构。
  2. 打印要断言的那个字段的实际值。
  3. 运行断言,对照输出判断是接口问题还是断言写错。
const res = pm.response.json(); console.log("完整响应:", JSON.stringify(res)); console.log("userName字段值:", res.userName); console.log("userName类型:", typeof res.userName);

这三行输出比任何断言都诚实。接口返回的内容、字段类型、是否缺失,一眼就能看出来。很多同学跳过这一步,直接闷头写断言,结果接口返回的根本不是自己以为的结构,断言自然全挂。

5.3 给断言脚本加“护栏”:失败信息写清楚

Postman的pm.expect默认失败提示就是“expected xxx to equal yyy”,大部分时候够用,但也有例外。当你做一个复杂的正则匹配时,默认提示不会告诉你实际值是什么。所以我的习惯是:给关键信息拼接进断言名称。

pm.test("订单编号格式正确,实际值为 " + data.orderNo, function () { pm.expect(data.orderNo).to.match(/^LS[0-9]{8}$/); });

这样结果面板里哪怕不展开,也能看到具体哪个订单编号出问题。在几百条用例跑完的Runner结果里,这种“自描述用例名”能省下大量定位时间。

5.4 断言顺序对排查效率的影响

Tests脚本按行从上到下执行,遇到某个断言失败不会中断整个脚本,后续的断言照跑。这有个好处:一条测试用例挂了,其他关联校验依然会执行,你看到的是一个错误的全貌,而不只是第一个错误。但也有个坑:如果一个字段是后续断言的前提,它挂了之后,后续字段可能因为字段不存在而连环报错。

比如某接口偶发返回缺字段的异常数据,你写了三条断言分别校验A、B、C三个字段,结果A缺失,B和C的断言会因为 undefined 而全部失败。这时候看到的是三个红叉,其实源头只有一个。我的取舍是:对强关联字段做分组合并,或者把前提校验放在首位并尽早暴露。实际操作中,我更倾向于接受这种“连环红叉”——它虽然看着吓人,但至少让你意识到这次返回连基础结构都不完整了。

6. 我踩过的高频坑:从Chai风格到沙箱差异

6.1 新旧语法混用是最大的翻车原因

接触Postman晚的人可能很难想象,早期版本的Tests脚本用的是tests["用例名"] = 布尔表达式这种写法:

tests["状态码是200"] = responseCode.code === 200;

现在很多人翻网络教程时复制到了旧代码,贴进新版Postman也能跑,运行机制却完全不同。新版统一推荐pm.test+pm.expect,并且responseCode、responseBody这些全局对象也在逐步弱化。我的建议很直接:所有新写的用例一律用pm.test风格;看到tests["xxx"] = ...的老代码,也能看懂,但不要混着写进同一个脚本。混用最致命的地方在于两种写法的断言失败处理方式不同,结果面板中点开红叉看到的排查路径会走两套逻辑。

6.2 Chai断言里的类型判断陷阱

pm.expect(value).to.be.a("string")的写法看起来很自然,但它判断的严格程度比很多人想象的高。null会被识别成object,undefined谁也不是,数字和字符串不会自动转换。所以接口返回的数字字段,你拿字符串去比,断言会失败。

这类问题排查起来也简单:先console.log(typeof value)看看真实类型。我踩过最无语的坑是某接口返回了"200",我用to.equal(200)去比,等值断言失败;换成to.equal("200")又觉得恶心。最后确认是后端同学把数字转成字符串返回了。这种场景不要迁就后端,就在断言里先做一次显式转换,再比较:

const codeNum = Number(data.code); pm.expect(codeNum).to.equal(200);

6.3 脚本作用域:注意闭包与迭代变量

在forEach里写断言时,迭代变量是局部于每次回调的,不会交叉污染,这没问题。容易翻车的是在循环外面引用循环内变量——比如你用var在循环里保存了一个值,循环结束后拿它做断言,很可能拿到的是最后一次迭代的值,而不是你想要的那一项。Postman的Tests脚本运行在沙箱里,它没有浏览器的DOM操作,但仍遵循JavaScript基本作用域规则。

我现在的原则是:凡是需要用于断言的中间变量,都在pm.test函数内部声明和计算,尽量不把可变状态带出函数。这样脚本写长了也不会因为在不同用例之间共享变量而出现“A用例影响了B用例”的灵异问题。

6.4 异步与依赖:断言不等接口外部请求

有人问,在Tests脚本里能不能再发一个请求、拿返回值做断言?Postman里确实有pm.sendRequest可以用来发额外请求,但要注意它本身是异步的。如果在pm.test里直接调用pm.sendRequest,测试用例可能在回调还没执行完就已经判定结束了,断言根本不会触发。

正确做法有两种:要么在回调里做断言(这种场景下回调内部的pm.test和pm.expect依然会被记录到结果面板);要么放弃在Tests里做二次请求断言,改用前面说的setNextRequest链路方式,把依赖关系放到Runner流程里。我自己的经验是:Tests脚本里尽量保持单请求校验,请求间依赖交给环境变量和Runner的请求顺序。这样调试时点单个Send,也能快速得到结果。

6.5 断言数量失控时怎么拆

一个接口十几个业务字段,我见过有人把二十多个断言揉进一个pm.test。这样做的坏处是:结果面板只有一个大用例,挂了你得靠展开信息判断哪段逻辑错了,维护成本很高。合理做法是按业务维度拆用例,一个用例聚焦一个点。

场景建议拆法
登录/鉴权类接口拆成“状态码”“token存在性”“token格式”“用户信息结构”
列表类接口拆成“分页参数”“列表类型”“列表内核心字段”“筛选条件生效”
写操作类接口拆成“操作成功标志”“返回ID格式”“落库数据补齐性”

拆完用例后记得每个pm.test名字里带上字段或业务后缀,比如“订单列表-金额字段类型正确”,Runner跑完后看结果列表就能快速定位是哪个业务维度出了偏差。

6.6 2020年前后的Postman版本切换期差异

标题里挂的2020年,恰好是Postman从Chrome插件版全面迁移到原生App的过渡期。那段时间最典型的坑是:插件版老用户写的脚本依赖postman全局变量和responseBody,迁到原生App后发现部分语法已经变了。原生App虽然兼容了相当多旧接口,但官方文档里已经不推荐依赖那些旧对象。所以如果你翻到一篇2020年之前的教程,里面大量出现postman.setEnvironmentVariable这种老风格,建议顺手改成pm.environment.set的现代写法。

另外原生App的沙箱对ES6的兼容度已经相当好了,箭头函数、const、模板字符串都可以直接用。我平时写断言脚本时会用一点ES6语法,代码比纯var回调看起来简洁不少。但注意不要用特别新的ES2020特性,毕竟脚本要在Postman内置的JS运行时里跑,太新的语法兼容性风险高。

7. 一点个人经验:把Tests脚本当成接口的“质量契约”

写了这么多,最后聊个自己的体会。我现在看一个项目的接口测试写得专不专业,先不看你覆盖了多少条接口,而是看每条接口的Tests脚本里有没有出现“契约感”——也就是断言是否在验证接口对它自身行为的承诺。

接口文档说返回code=0表示成功,你的断言就应该断言code === 0;文档说data.orderId是32位字符串,断言就该校验位数和类型;文档说分页pageSize最大不超过100,那断言里就该有pageSize <= 100的边界校验。Tests脚本写得越贴近文档定义,这个脚本长期维护下来就越有价值——因为一旦接口行为偏离文档,最先感知到的不是人,而是测试结果面板里那个红叉。

配合环境变量和Collection Runner,Postman的Tests已经从“一个请求的临时校验”进化成了一套轻量级回归体系。你不用上来就搞整套自动化测试框架,一个项目从第一条接口用例开始写断言,积累到几十条,回归时的价值已经远超那点脚本维护成本。看完这篇,建议你打开Postman,挑一个最常调的接口,从一条状态码断言开始写起。写十条用例之后,你就再也不想回到纯肉眼看返回值的日子了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 10:08:20

对话式学术数据分析:虎贲等考AI让小白也能完成论文统计

我身边不少做研究的朋友&#xff0c;论文卡壳十有八九不是卡在选题&#xff0c;也不是卡在实验&#xff0c;而是卡在“收了数据之后怎么办”这一关。问卷回收了三百份&#xff0c;Excel表格倒是整理得整整齐齐&#xff0c;可一打开统计软件就头大——该用t检验还是方差分析&…

作者头像 李华
网站建设 2026/10/17 10:05:44

K-Means聚类算法详解:C++从零实现原理与优化实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/17 10:05:27

继续教育学生AI论文工具实测:从选题到答辩的组合策略

我这两周干了一件挺费眼的事&#xff1a;用一个继续教育学生的身份&#xff0c;把市面上常被提到的8类AI论文工具挨个试了一遍。朋友圈里天天有人推“AI写论文”&#xff0c;可真正到了开题、文献综述、初稿、降重、查重、格式、答辩这个完整链条上&#xff0c;大部分继续教育学…

作者头像 李华
网站建设 2026/10/17 10:04:58

Vue自定义指令实现input数字输入及小数位数控制

做前端这几年&#xff0c;业务表单里凡是涉及金额、数量、库存、手机号这类输入框&#xff0c;几乎都会碰到一个很缠人的需求&#xff1a;input 只能填数字&#xff0c;不能有乱七八糟的符号&#xff0c;最好还能控制小数点后保留几位。标题里的“Vue 自定义指令 - input 只能输…

作者头像 李华
网站建设 2026/10/17 10:04:38

C程序逆向破解入门:从静态定位到修改跳转的实战流程

如果你已经翻完了第一篇&#xff0c;把环境装好、随手反汇编过几个小Demo&#xff0c;那么这一篇我们直接开始动真家伙&#xff1a;拿一个真实的C程序&#xff0c;从头到尾走一遍逆向破解流程。说是“破解”&#xff0c;实际你全程看到的是代码分析、控制流判断、内存布局这些基…

作者头像 李华
网站建设 2026/10/17 10:04:19

从零配置深度学习环境:Anaconda、TensorFlow与PyCharm安装详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华