9.2.2 测试专用的配置——环境变量:测试专用配置管理
环境变量是连接代码与环境的桥梁,管理好它们是测试成功的基础。
环境变量文件层级
project/
├── .env # 默认配置(被其他覆盖)
├── .env.local # 本地开发配置(不提交 git)
├── .env.development # 开发环境配置
├── .env.test # 测试环境配置
├── .env.production # 生产环境配置
└── .env.example # 配置模板(提交 git)加载优先级
.env.test 标准配置
bash
# .env.test
# 环境标识
NODE_ENV=test
# 数据库(必须使用测试数据库)
DATABASE_URL="postgresql://test:test@localhost:5432/myapp_test"
# 认证(使用固定的测试密钥)
JWT_SECRET="test-jwt-secret-do-not-use-in-production"
NEXTAUTH_SECRET="test-nextauth-secret"
NEXTAUTH_URL="http://localhost:3000"
# 外部服务(使用测试模式或 Mock)
STRIPE_SECRET_KEY="sk_test_..."
STRIPE_WEBHOOK_SECRET="whsec_test_..."
# 邮件服务(禁用或使用测试服务)
SMTP_HOST=""
SMTP_PORT=""
# 日志级别(测试时可以调高)
LOG_LEVEL="error"
# 测试专用配置
TEST_USER_EMAIL="test@example.com"
TEST_USER_PASSWORD="TestPassword123!"使用 dotenv-cli 加载测试配置
bash
# 安装 dotenv-cli
npm install -D dotenv-clijson
// package.json
{
"scripts": {
"test": "dotenv -e .env.test -- jest",
"test:watch": "dotenv -e .env.test -- jest --watch",
"test:coverage": "dotenv -e .env.test -- jest --coverage",
"test:ci": "dotenv -e .env.test -- jest --ci --runInBand"
}
}在测试中访问环境变量
typescript
// lib/env.ts
import { z } from 'zod';
const envSchema = z.object({
NODE_ENV: z.enum(['development', 'test', 'production']),
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
// 测试环境可选的配置
STRIPE_SECRET_KEY: z.string().optional(),
});
export const env = envSchema.parse(process.env);
// 便捷判断
export const isTest = env.NODE_ENV === 'test';
export const isProd = env.NODE_ENV === 'production';typescript
// __tests__/example.test.ts
import { env, isTest } from '@/lib/env';
describe('环境配置', () => {
it('应处于测试环境', () => {
expect(isTest).toBe(true);
expect(env.NODE_ENV).toBe('test');
});
it('应使用测试数据库', () => {
expect(env.DATABASE_URL).toContain('_test');
});
});敏感配置的处理
不应该在 .env.test 中出现的内容
bash
# .env.test
# ❌ 错误:不要使用真实的生产密钥
STRIPE_SECRET_KEY="sk_live_xxx"
# ✅ 正确:使用测试模式密钥
STRIPE_SECRET_KEY="sk_test_xxx"
# ❌ 错误:不要使用真实的生产数据库
DATABASE_URL="postgresql://user:pass@prod-server/myapp"
# ✅ 正确:使用本地测试数据库
DATABASE_URL="postgresql://test:test@localhost/myapp_test".gitignore 配置
bash
# .gitignore
# 所有本地环境文件
.env.local
.env.*.local
# 测试环境文件(如果包含敏感信息)
# .env.test
# 但要保留示例文件
!.env.example环境变量验证
typescript
// test/setup.ts
import { z } from 'zod';
const testEnvSchema = z.object({
NODE_ENV: z.literal('test'),
DATABASE_URL: z.string().refine(
(url) => url.includes('_test') || url.includes('localhost'),
{ message: '测试必须使用测试数据库' }
),
});
beforeAll(() => {
const result = testEnvSchema.safeParse(process.env);
if (!result.success) {
console.error('环境变量验证失败:', result.error.format());
throw new Error('测试环境配置错误');
}
});动态配置切换
typescript
// lib/config.ts
type Environment = 'development' | 'test' | 'production';
interface Config {
apiBaseUrl: string;
enableLogging: boolean;
mockExternalServices: boolean;
}
const configs: Record<Environment, Config> = {
development: {
apiBaseUrl: 'http://localhost:3000',
enableLogging: true,
mockExternalServices: false,
},
test: {
apiBaseUrl: 'http://localhost:3000',
enableLogging: false,
mockExternalServices: true, // 测试时 Mock 外部服务
},
production: {
apiBaseUrl: 'https://api.example.com',
enableLogging: true,
mockExternalServices: false,
},
};
export const config = configs[process.env.NODE_ENV as Environment] || configs.development;CI/CD 中的环境变量
yaml
# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
env:
NODE_ENV: test
DATABASE_URL: postgresql://test:test@localhost:5432/myapp_test
JWT_SECRET: ${{ secrets.TEST_JWT_SECRET }}
services:
postgres:
image: postgres:15
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: myapp_test
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci
- run: npm run test:ci常见问题解决
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 环境变量未加载 | dotenv-cli 未安装 | npm i -D dotenv-cli |
| 使用了生产配置 | 文件优先级问题 | 检查 .env.local |
| CI 中变量为空 | secrets 未配置 | 在 GitHub 设置中添加 |
| 类型错误 | 环境变量都是字符串 | 使用 zod 转换 |
本节小结
测试环境变量管理的核心原则是:隔离、验证、安全。使用 .env.test 隔离测试配置,使用 zod 验证环境变量正确性,避免在测试配置中暴露敏感信息。配合 dotenv-cli,可以轻松在不同环境间切换。
