You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

k6性能测试OAuth2令牌生成报错及复用需求求助

解决k6 OAuth2令牌生成错误及复用问题

错误原因分析

你遇到的TypeError: Cannot read property 'access_token' of undefined主要由以下几点导致:

  • 全局响应体丢弃配置:options里设置了discardResponseBodies: true,导致令牌请求的响应体被清空,JSON.parse(res.body)返回undefined,无法读取access_token。
  • 缺少请求错误处理:未检查令牌请求的响应状态,若请求失败(如网络异常、认证参数错误),直接解析响应体必然报错。
  • 变量名拼写错误:测试函数中使用resp.timings.duration,但实际变量名是response,会导致额外的未定义错误。

修复方案及令牌复用实现

1. 修复令牌生成逻辑

  • 禁用令牌请求的响应体丢弃(或全局关闭)
  • 添加响应状态检查,确保请求成功后再解析
  • 修正变量名错误

2. 实现令牌一次性生成复用

利用k6的setup()函数,该函数在测试开始前仅执行一次,生成的令牌可传递给所有VU的测试函数使用,避免重复请求令牌。

完整修正代码

import http from 'k6/http';
import { htmlReport } from 'https://raw.githubusercontent.com/benc-uk/k6-reporter/2.4.0/dist/bundle.js'
import { textSummary } from 'https://jslib.k6.io/k6-summary/0.0.1/index.js'
import { Trend } from 'k6/metrics';
import { sleep } from 'k6'; // 补充导入sleep

export const options = {
  discardResponseBodies: true, // 全局保留该配置,在令牌请求中单独覆盖
  scenarios: {
    getItems: {
      executor: 'constant-arrival-rate',
      exec: 'getItems',
      duration: '1m',
      rate: 1,
      preAllocatedVUs: 2,
      timeUnit: "1s",
    },
    getItemsExport: {
      executor: 'constant-arrival-rate',
      exec: 'getItemsExport',
      duration: '1m',
      rate: 1,
      preAllocatedVUs: 2,
      timeUnit: "1s",
    },
    postItemsSummary: {
      executor: 'constant-arrival-rate',
      exec: 'postItemsSummary',
      duration: '1m',
      rate: 1,
      preAllocatedVUs: 2,
      timeUnit: "1s",
    },
  },
  summaryTrendStats: ["min", "max", "avg","med", "p(90)", "p(95)","p(99)", "count"],
};

let requestTrend1 = new Trend('Request1')
let requestTrend2 = new Trend('Request2')
let requestTrend3 = new Trend('Request3') // 修正重复的Trend名称

// 测试前一次性生成令牌
export function setup() {
  const clientId = ''; // 填入你的client_id
  const clientSecret = ''; // 填入你的client_secret
  const tokenUrl = 'https://token_creator.com/oauth2/token';

  const body = `grant_type=client_credentials&client_id=${clientId}&client_secret=${clientSecret}`;
  // 单独禁用该请求的响应体丢弃
  const res = http.post(tokenUrl, body, { 
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    discardResponseBodies: false
  });

  // 检查请求状态,失败则抛出错误终止测试
  if (res.status !== 200) {
    throw new Error(`令牌请求失败,状态码:${res.status},响应内容:${res.body}`);
  }

  const result = JSON.parse(res.body);
  if (!result.access_token) {
    throw new Error('未从响应中获取到access_token');
  }

  return { token: result.access_token };
}

//get Items
export function getItems(data) {
  console.log("Executing getItems ")
  const token = data.token; // 从setup返回的数据中获取令牌
  const apiUrl = 'https://local.com/v1/items?site2';

  const headers = {
      Authorization: `Bearer ${token}`,
      Connection: `keep-alive`
  };
  const response = http.get(apiUrl, { headers });
  console.log(`Response status: ${response.status}`);
  requestTrend1.add(response.timings.duration) // 修正变量名
  sleep(1);
}

//get Items export
export function getItemsExport(data) {
  const token = data.token;

  const apiUrl = 'https://local.com/v1/items/export?offset=0&limit=200&site=1';

  const headers = {
      Authorization: `Bearer ${token}`,
      Connection: `keep-alive`
  };
  const response = http.get(apiUrl, { headers });
  console.log(`Response status: ${response.status}`);
  requestTrend2.add(response.timings.duration) // 修正变量名
  sleep(1);
}

export function postItemsSummary(data) {
  console.log("Executing postItemsSummary ")
  const token = data.token;

 const apiUrl = 'https://local.com/v1/items/summary';

 const payload = JSON.stringify([{
  site_id: "056",
  supc: "4212791"
 }])

 const headers = {
  Authorization: `Bearer ${token}`,
  'Content-Type': 'application/json',
};

 const response = http.post(apiUrl,payload, { headers });
 console.log(`Response status: ${response.status}`);
 requestTrend3.add(response.timings.duration) // 修正变量名
 sleep(1);
}

export function handleSummary(data) {
  return {
    'k6summary.html': htmlReport(data, { debug: false }),
    stdout: textSummary(data, { indent: ' ', enableColors: true }),
  }
}

关键修改说明

  1. setup()函数:将令牌生成逻辑移至此处,确保仅执行一次,生成的令牌通过返回值传递给所有测试函数。
  2. 响应体丢弃控制:在令牌请求中单独设置discardResponseBodies: false,既保留全局配置的优化,又能获取令牌响应体。
  3. 错误处理:添加状态码检查和令牌存在性验证,提前发现问题并终止测试。
  4. 变量名修正:统一使用response变量名,避免未定义错误。
  5. Trend名称修正:将重复的Request2改为Request3,避免统计数据混淆。

内容的提问来源于stack exchange,提问作者JokerFanJack

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 09:13:23