Playwright自定义配置中extraHTTPHeaders未生效,请求报JSON解析错误
SyntaxError: Unexpected end of JSON input 问题描述
我在Playwright的自定义配置文件api.config.js中配置了extraHTTPHeaders,包含携带环境变量ACCESS_TOKEN的Authorization头,以及Content-Type: application/json,期望请求自动带上这些头部。通过命令npx playwright test --config=api.config.js运行测试,测试文件里在beforeAll阶段调用getToken函数获取令牌,之后用request.post发送GraphQL请求,但执行const responseBody = await response.json();时出现SyntaxError: Unexpected end of JSON input错误。想问这种读取配置头部的方式是否正确?错误原因是什么?
配置代码(api.config.js)
const { devices, chromium } = require('@playwright/test'); // const { getToken } = require("./helper"); use: { /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ actionTimeout: 0, // headless: true, /* Base URL to use in actions like `await page.goto('/')`. */ baseURL: 'https://connect.dev.morinc.com/api/eds', extraHTTPHeaders: { Authorization: `Bearer ${process.env.ACCESS_TOKEN}`, 'Content-Type': "application/json" }, /* Collect trace when retrying the failed test. */ trace: 'on-first-retry', },
测试代码
const { getToken } = require("../../auth0"); import { faker } from '@faker-js/faker'; const { entityTypes } = require("../../global.js"); test.beforeAll(async() => { await getToken(); }) test.describe('Create Entity', async() => { test("Create an entity", async ({baseURL, request}) => { const requestBody = { data: { query: ` mutation CreateEntity($name: String!, $type: EntityType, $active: Boolean, $identifiers: [String]) { createEntity(name: $name, type: $type, active: $active, identifiers: $identifiers) { name type active id identifiers items } }`, variables: { name: faker.name.fullName(), type: entityTypes.corporate, active: true, identifiers: [] } } }; const response = await request.post(baseURL, requestBody); const responseBody = await response.json(); //Assertions expect(response.status()).toBe(200); expect(responseBody.data.createEntity.name).toBe(requestBody.data.variables.name); expect(responseBody.data.createEntity.type).toBe(requestBody.data.variables.type); expect(responseBody.data.createEntity.active).toBeTruthy(); expect(responseBody.data.createEntity.id).toBeTruthy(); expect(responseBody.data.createEntity.identifiers).toHaveLength(0); expect(responseBody.data.createEntity.items).toBe(null); }); });
getToken函数代码
require("dotenv").config({path: ".env.dev"}); let auth0Credentials = JSON.parse(process.env.AUTH0_CONFIG) let userCredentials = JSON.parse(process.env.USERS_CREDENTIALS) async function getToken() { const tokenExpiration = process.env.ACCESS_TOKEN_EXPIRATION; const storedToken = process.env.ACCESS_TOKEN; if ( storedToken && tokenExpiration && new Date(tokenExpiration) > new Date() ) { return storedToken; } const credentials = { client_id: auth0Credentials.client_id, client_secret: auth0Credentials.client_secret, audience: auth0Credentials.audience, grant_type: auth0Credentials.grant_type, username: userCredentials.q4admin, password: userCredentials.password, }; const url = auth0Credentials.oauthURL; const options = { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(credentials), }; const response = await fetch(url, options); const json = await response.json(); const expiresIn = json.expires_in || 0; const accessToken = json.access_token || ""; process.env.ACCESS_TOKEN_EXPIRATION = new Date(Date.now() + expiresIn * 1000).toISOString(); process.env.ACCESS_TOKEN = accessToken; return accessToken; } module.exports = {getToken}
问题分析与解决
1. 配置头部的方式不正确
Playwright配置文件在测试进程启动时就完成解析加载,而getToken是在测试的beforeAll阶段才执行,此时process.env.ACCESS_TOKEN还未被赋值,导致extraHTTPHeaders里的Authorization头实际是Bearer undefined,属于无效鉴权信息。服务器收到这类请求后通常返回401未授权响应,且响应内容不是JSON格式(可能是空白、HTML错误页或纯文本提示),最终引发response.json()解析错误。
2. 请求体格式错误
测试代码中request.post直接传入requestBody对象,但Playwright的request.post要求请求体放在data字段中。直接传对象会导致请求体格式异常,服务器无法解析请求,返回非JSON响应,同样触发解析错误。
正确的解决步骤
步骤一:修正请求发送格式
将request.post调用改为正确格式,把GraphQL请求体放在data字段内:
const response = await request.post(baseURL, { data: requestBody.data // 仅传递GraphQL所需的query和variables部分 });
步骤二:动态设置请求头
放弃配置文件中的静态extraHTTPHeaders配置,改为在beforeAll获取token后给request上下文动态设置全局默认请求头:
test.beforeAll(async ({ request }) => { const token = await getToken(); // 给当前request上下文设置全局默认请求头 await request.context().setExtraHTTPHeaders({ Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }); });
这样后续所有request发起的请求都会自动携带有效头部。
步骤三:调试辅助
在解析响应JSON前,先打印响应状态和文本,快速定位问题:
console.log('响应状态码:', response.status()); console.log('响应内容:', await response.text());
内容的提问来源于stack exchange,提问作者vormier

