Playwright组件测试中Next.js项目MyComponent.tsx无法访问process.env
我在Next.js项目中用Playwright做组件测试,目录结构如下:
./playwright - component ./MyComponent.spec.tsx ./MyComponent.tsx ./index.html - fixtures - index.ts
目前在playwright-ct-config.ts里用dotenv加载了./env/.env.test的环境变量,测试文件MyComponent.spec.tsx能正常访问process.env,但组件MyComponent.tsx里访问不到。需要让组件也能拿到这些环境变量。
核心原因
测试文件运行在Node.js环境,能直接读取process.env;但组件是在浏览器环境渲染的,Node的process.env不会自动同步到浏览器上下文,得手动注入。
方案1:通过Playwright配置注入浏览器环境变量
在playwright-ct-config.ts里,把dotenv加载的变量配置到environmentVariables中,Playwright会自动把这些变量注入到浏览器的process.env模拟对象里:
import { defineConfig } from '@playwright/experimental-ct-react'; import dotenv from 'dotenv'; // 加载测试环境变量 const envConfig = dotenv.config({ path: './env/.env.test', override: true }); const env = envConfig.parsed || {}; export default defineConfig({ testDir: './playwright/component', environment: 'next', // 指定Next.js环境 // 注入环境变量到浏览器上下文 environmentVariables: { // 可以全部注入,或者只挑需要的(推荐只传NEXT_PUBLIC_开头的,符合Next.js规则) ...env, // 示例:只传递公共变量 NEXT_PUBLIC_API_BASE_URL: env.NEXT_PUBLIC_API_BASE_URL, }, });
注意:Next.js中只有NEXT_PUBLIC_前缀的环境变量会被暴露到浏览器环境,所以组件里只能访问带这个前缀的变量,私有变量不要直接注入到浏览器。
方案2:通过Fixture传递私有变量(适合敏感数据)
如果是不需要暴露给浏览器的私有变量(比如API密钥),别直接注入到浏览器,而是通过Playwright Fixture把变量作为props传给组件:
- 在
playwright/fixtures/env.ts里定义环境变量fixture:
import { test as base } from '@playwright/experimental-ct-react'; import dotenv from 'dotenv'; const env = dotenv.config({ path: './env/.env.test', override: true }).parsed || {}; // 扩展基础测试,添加环境变量fixture export const test = base.extend({ componentEnv: async ({}, use) => { await use({ privateApiKey: env.PRIVATE_API_KEY, }); }, }); export { expect } from '@playwright/experimental-ct-react';
- 在测试文件中使用fixture传递变量给组件:
import { test, expect } from '../fixtures/env'; import MyComponent from './MyComponent'; test('组件能拿到私有变量', async ({ mount, componentEnv }) => { // 把变量作为props传给组件 await mount(<MyComponent apiKey={componentEnv.privateApiKey} />); // 验证组件行为 await expect(page.getByText('API密钥已加载')).toBeVisible(); });
- 组件通过props接收变量:
// MyComponent.tsx interface Props { apiKey: string; } export default function MyComponent({ apiKey }: Props) { return <div>API密钥已加载:{apiKey}</div>; }
方案3:通过Vite配置注入(自定义浏览器环境变量)
如果需要更灵活的注入方式,可以通过Playwright的ctViteConfig配置,用Vite的define选项把变量替换到组件代码中:
import { defineConfig } from '@playwright/experimental-ct-react'; import dotenv from 'dotenv'; const envConfig = dotenv.config({ path: './env/.env.test', override: true }); const env = envConfig.parsed || {}; export default defineConfig({ testDir: './playwright/component', environment: 'next', use: { ctIndexHtml: './playwright/component/index.html', // 配置Vite替换规则 ctViteConfig: { define: { // 把process.env变量替换成实际值 'process.env.NEXT_PUBLIC_API_URL': JSON.stringify(env.NEXT_PUBLIC_API_URL), // 也可以注入到window对象 'window.appEnv.API_URL': JSON.stringify(env.NEXT_PUBLIC_API_URL), }, }, }, });
之后组件里就能直接用process.env.NEXT_PUBLIC_API_URL或者window.appEnv.API_URL访问变量。
关键提醒
- 永远不要把敏感的私有变量(比如数据库密码、私钥)暴露到浏览器环境,这类变量只能通过方案2的props传递,或者在测试中模拟接口返回。
- Next.js的
NEXT_PUBLIC_前缀是浏览器环境访问变量的标准方式,尽量遵循这个规则,避免自定义注入带来的混乱。
内容的提问来源于stack exchange,提问作者Divyanshu Rawat

