一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Codex CLI项目实战:用OpenAI编程代理补齐测试

时间:2026-09-18 12:36:01 编辑:袖梨 来源:一聚教程网

当一个已经可以运行的 Vue 3 项目临近上线,却还缺少单元测试、端到端测试和覆盖率报告时,从零补齐质量保障体系往往十分耗时。Codex CLI 可以直接在终端中读取项目、生成测试并执行验证。下面将从安装配置开始,逐步完成 Vitest、Cypress 与覆盖率相关任务。

上周我给一个Vue3任务管理项目加测试,用Codex CLI写了40个单元测试和12个E2E测试。整个过程花了不到2小时,其中1小时在等网络请求。

场景引入

你的Vue3任务管理应用已经跑起来了,有任务列表、状态切换、拖拽排序。现在老板说:“下周上线前得有测试覆盖率报告。”你打开package.json,里面连vitest都没装。测试从零开始,而你只有两天时间。

这正好是Codex CLI的主战场。你只需要在终端里输入需求,它就会自己读代码、写测试、跑测试、修复问题。

环境准备

先确认Node.js版本。Codex CLI需要18.0.0或更高。

node --version
# 应该显示 v18.x.x 或更高

安装Codex CLI。两种方式任选其一:

# npm方式(推荐)
npm install -g @openai/codex

# macOS用户可以用Homebrew
brew install codex

验证安装:

codex --version
# 应该显示版本号,比如 0.36.0

接下来是认证。Codex CLI支持两种认证方式。

方式一:ChatGPT账号(推荐)

codex
# 首次运行会自动弹出登录提示
# 用你的ChatGPT Plus/Pro账号登录

登录成功后会看到Authenticated字样。这种方式最简单,订阅用户直接用,不需要额外付费。

方式二:API Key

适合需要精确控制成本的场景:

export OPENAI_API_KEY="sk-your-key-here"

把这行加到~/.zshrc~/.bashrc里,这样每次打开终端都会自动设置。

核心配置详解

Codex CLI的配置文件在~/.codex/config.toml。首次运行会自动创建,你也可以手动创建。

创建一个基础配置:

mkdir -p ~/.codex
touch ~/.codex/config.toml

写入一个适合日常开发的配置:

# 模型选择
model = "o4-mini"  # 性价比最高的选项

# 审批策略
approval_policy = "on-request"  # 需要确认时会问你
sandbox_mode = "workspace-write"  # 只能在项目目录内写文件

# 界面设置
hide_agent_reasoning = false  # 显示思考过程,方便调试
file_opener = "cursor"  # 点击文件路径时用Cursor打开

# 隐私设置
disable_response_storage = true  # 不存储对话数据

关键配置项解释:

模型选择:o4-mini是性价比最高的选项,速度快,成本低。如果你的任务很复杂,可以换o3,但要注意成本会高很多。

审批策略on-request是日常开发的最佳选择。Codex会在需要写文件或执行命令前问你,但不会每一步都问。suggest模式太保守,full-auto太危险,不建议在正式项目用。

沙箱模式workspace-write确保Codex只能修改你项目目录内的文件,不会动系统文件。这是安全底线。

响应存储:设为true可以保护你的代码隐私,特别是商业项目。

保存配置后,重启终端让配置生效。

实战演练

现在用Codex CLI完成前端测试与质量保证的五个任务。

任务一:Vitest单元测试

启动Codex,指向你的项目目录:

cd ~/projects/task-manager
codex

在Codex里输入:

为src/components/TaskList.vue编写Vitest单元测试。
要求:
1. 测试组件渲染任务列表
2. 测试点击任务时触发select事件
3. 测试空列表时显示提示信息
4. 使用@vue/test-utils
5. 创建__tests__/TaskList.test.ts文件

Codex会自己读取TaskList.vue的代码,理解组件结构,然后生成测试文件。它会自动安装必要的依赖(如果还没装的话)。

生成的测试文件大概长这样:

// src/components/__tests__/TaskList.test.ts
import { describe, it, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import TaskList from '../TaskList.vue'

const mockTasks = [
  { id: 1, title: '测试任务1', completed: false },
  { id: 2, title: '测试任务2', completed: true }
]

describe('TaskList', () => {
  it('renders task list correctly', () => {
    const wrapper = mount(TaskList, {
      props: { tasks: mockTasks }
    })
    
    expect(wrapper.findAll('.task-item')).toHaveLength(2)
    expect(wrapper.text()).toContain('测试任务1')
  })

  it('emits select event when task is clicked', async () => {
    const wrapper = mount(TaskList, {
      props: { tasks: mockTasks }
    })
    
    await wrapper.find('.task-item').trigger('click')
    expect(wrapper.emitted('select')).toBeTruthy()
    expect(wrapper.emitted('select')[0]).toEqual([mockTasks[0]])
  })

  it('shows empty state when no tasks', () => {
    const wrapper = mount(TaskList, {
      props: { tasks: [] }
    })
    
    expect(wrapper.text()).toContain('暂无任务')
  })
})

运行测试验证:

npx vitest run

如果测试通过,你会看到类似这样的输出:

 ✓ src/components/__tests__/TaskList.test.ts (3 tests) 12ms
   ✓ TaskList > renders task list correctly
   ✓ TaskList > emits select event when task is clicked
   ✓ TaskList > shows empty state when no tasks

 Test Files  1 passed (1)
      Tests  3 passed (3)
   Start at  10:30:45
   Duration  234ms

如果测试失败,Codex会自动分析错误原因并尝试修复。你可以直接告诉它:"测试失败了,帮我看看什么原因",它会读取错误日志并调整代码。

任务二:Cypress E2E测试

继续在Codex里输入:

为任务管理功能编写Cypress E2E测试。
测试场景:
1. 访问首页,看到任务列表
2. 点击"新建任务"按钮,填写表单,提交
3. 新任务出现在列表中
4. 点击任务状态切换按钮,状态改变
5. 创建cypress/e2e/task-management.cy.ts

Codex会生成一个完整的E2E测试文件。它还会帮你配置Cypress(如果还没配置的话)。

生成的测试文件示例:

// cypress/e2e/task-management.cy.ts
describe('Task Management', () => {
  beforeEach(() => {
    cy.visit('/')
  })

  it('displays task list on homepage', () => {
    cy.get('.task-list').should('be.visible')
    cy.get('.task-item').should('have.length.greaterThan', 0)
  })

  it('creates a new task', () => {
    const taskTitle = `测试任务 ${Date.now()}`
    
    cy.get('[data-testid="new-task-button"]').click()
    cy.get('input[name="title"]').type(taskTitle)
    cy.get('textarea[name="description"]').type('这是测试描述')
    cy.get('button[type="submit"]').click()
    
    cy.get('.task-item').should('contain', taskTitle)
  })

  it('toggles task status', () => {
    cy.get('.task-item').first().within(() => {
      cy.get('[data-testid="status-toggle"]').click()
    })
    
    cy.get('.task-item').first().should('have.class', 'completed')
  })
})

Codex还会帮你配置cypress.config.ts:

// cypress.config.ts
import { defineConfig } from 'cypress'

export default defineConfig({
  e2e: {
    baseUrl: 'http://localhost:5173',
    specPattern: 'cypress/e2e/**/*.cy.{js,ts}',
    supportFile: 'cypress/support/e2e.ts',
    viewportWidth: 1280,
    viewportHeight: 720,
    video: false,
    screenshotOnRunFailure: true
  }
})

运行E2E测试:

# 打开Cypress测试运行器
npx cypress open

# 或者无头模式运行(适合CI)
npx cypress run

在无头模式下,Cypress会生成视频和截图,方便你排查问题。如果测试失败,你会看到具体的错误信息和失败位置。

任务三:测试覆盖率配置

在Codex里输入:

配置Vitest的测试覆盖率。
要求:
1. 使用v8作为覆盖率提供器
2. 覆盖率报告包含text、html、lcov格式
3. 覆盖率阈值:语句80%,分支75%,函数80%,行80%
4. 配置在vitest.config.ts中

Codex会修改你的vitest配置文件:

// vitest.config.ts
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  test: {
    globals: true,
    environment: 'jsdom',
    coverage: {
      provider: 'v8',
      reporter: ['text', 'html', 'lcov'],
      thresholds: {
        statements: 80,
        branches: 75,
        functions: 80,
        lines: 80
      }
    }
  }
})

在package.json里添加覆盖率脚本:

{
  "scripts": {
    "test:unit": "vitest run",
    "test:coverage": "vitest run --coverage",
    "test:e2e": "cypress run"
  }
}

运行覆盖率报告:

npm run test:coverage

你会看到终端输出覆盖率统计:

 % Coverage report from v8
-------------------------|---------|----------|---------|---------|
 File                     | Stmts   | Branch   | Funcs   | Lines   |
-------------------------|---------|----------|---------|---------|
 All files                | 82.35%  | 76.47%   | 81.25%  | 82.35%  |
  src/components          | 85.71%  | 80.00%   | 83.33%  | 85.71%  |
   TaskList.vue           | 90.00%  | 85.71%   | 100.00% | 90.00%  |
   TaskItem.vue           | 80.00%  | 75.00%   | 75.00%  | 80.00%  |
  src/stores              | 78.57%  | 72.22%   | 80.00%  | 78.57%  |
   taskStore.ts           | 78.57%  | 72.22%   | 80.00%  | 78.57%  |
-------------------------|---------|----------|---------|---------|

同时会在coverage/目录下生成HTML报告,用浏览器打开coverage/index.html可以查看详细的覆盖率信息,精确到每一行代码。

任务四:TDD流程

告诉Codex你要用测试驱动开发:

我要用TDD方式开发一个任务过滤功能。
需求:支持按"全部"、"已完成"、"未完成"三种状态过滤任务。
请按TDD流程:
1. 先写失败的测试
2. 写最少的代码让测试通过
3. 重构
每一步都给我看具体代码。

Codex会引导你完成TDD的红-绿-蓝循环。它会先创建测试文件:

// src/composables/__tests__/useTaskFilter.test.ts
import { describe, it, expect } from 'vitest'
import { useTaskFilter } from '../useTaskFilter'

const mockTasks = [
  { id: 1, title: '任务1', completed: false },
  { id: 2, title: '任务2', completed: true },
  { id: 3, title: '任务3', completed: false }
]

describe('useTaskFilter', () => {
  it('filters all tasks by default', () => {
    const { filteredTasks, activeFilter } = useTaskFilter(mockTasks)
    expect(activeFilter.value).toBe('all')
    expect(filteredTasks.value).toHaveLength(3)
  })

  it('filters completed tasks', () => {
    const { filteredTasks, setFilter } = useTaskFilter(mockTasks)
    setFilter('completed')
    expect(filteredTasks.value).toHaveLength(1)
    expect(filteredTasks.value[0].completed).toBe(true)
  })

  it('filters active tasks', () => {
    const { filteredTasks, setFilter } = useTaskFilter(mockTasks)
    setFilter('active')
    expect(filteredTasks.value).toHaveLength(2)
    expect(filteredTasks.value.every(t => !t.completed)).toBe(true)
  })
})

然后创建最小化的实现代码:

// src/composables/useTaskFilter.ts
import { ref, computed } from 'vue'

type FilterType = 'all' | 'active' | 'completed'

export function useTaskFilter(tasks: any[]) {
  const activeFilter = ref<FilterType>('all')
  
  const filteredTasks = computed(() => {
    switch (activeFilter.value) {
      case 'completed':
        return tasks.filter(t => t.completed)
      case 'active':
        return tasks.filter(t => !t.completed)
      default:
        return tasks
    }
  })
  
  const setFilter = (filter: FilterType) => {
    activeFilter.value = filter
  }
  
  return { filteredTasks, activeFilter, setFilter }
}

最后它会建议重构方案,比如把FilterType提取到单独的类型文件里,或者添加更多的过滤选项。

任务五:ESLint配置

最后让Codex配置代码质量检查:

为项目配置ESLint。
要求:
1. 支持Vue3 + TypeScript
2. 集成Prettier
3. 配置strict规则集
4. 添加scripts到package.json

Codex会安装必要的包并生成配置文件。它通常会推荐使用eslint-config-prettier来避免ESLint和Prettier冲突。

生成的ESLint配置文件:

// eslint.config.js
import js from '@eslint/js'
import pluginVue from 'eslint-plugin-vue'
import tseslint from 'typescript-eslint'
import prettier from 'eslint-config-prettier'

export default tseslint.config(
  js.configs.recommended,
  ...tseslint.configs.recommended,
  ...pluginVue.configs['flat/recommended'],
  prettier,
  {
    files: ['**/*.{ts,vue}'],
    rules: {
      'vue/multi-word-component-names': 'off',
      '@typescript-eslint/no-unused-vars': 'warn',
      '@typescript-eslint/explicit-function-return-type': 'off'
    }
  }
)

在package.json里添加脚本:

{
  "scripts": {
    "lint": "eslint . --fix",
    "lint:check": "eslint ."
  }
}

检查配置是否正确:

npx eslint --print-config src/main.ts

这个命令会输出当前文件使用的ESLint规则,帮你确认配置是否生效。

踩坑记录

问题1:网络请求超时

国内网络连接OpenAI API不稳定。Codex CLI每次操作都要请求API,网络问题会导致超时。

解决方案:设置代理环境变量。

export HTTPS_PROXY=http://127.0.0.1:7890
export HTTP_PROXY=http://127.0.0.1:7890

把这两行加到你的shell配置文件里。

问题2:沙箱阻止操作

你可能看到错误:Sandbox denied operation。这是因为Codex想执行一些不在沙箱白名单里的命令。

解决方案:临时切换到更宽松的沙箱模式。

codex --sandbox workspace-write
# 或者完全绕过(仅在容器里用)
codex --dangerously-bypass-approvals-and-sandbox

问题3:配置不生效

修改了config.toml但没效果。Codex CLI在启动时读取配置,运行中不会重新加载。

解决方案:退出Codex重新启动,或者用命令行参数临时覆盖。

CODEX_MODEL=o4-mini codex

问题4:测试文件找不到

Codex生成了测试文件,但Vitest找不到。可能是文件路径不对,或者配置没更新。

解决方案:检查vitest.config.ts的include配置:

test: {
  include: ['src/**/*.{test,spec}.{js,ts}']
}

问题5:Cypress找不到元素

E2E测试里用cy.get('.button')找不到元素。可能是因为Vue组件渲染的DOM结构和预期不同。

解决方案:用data-testid属性而不是CSS类。

<button data-testid="submit-button">提交</button>
cy.get('[data-testid="submit-button"]').click()

本篇小结

Codex CLI是OpenAI生态里的终端编程代理,特别适合需要快速原型开发和测试的场景。

配置清单

  • 安装:npm install -g @openai/codex
  • 认证:ChatGPT账号或API Key
  • 配置文件:~/.codex/config.toml
  • 推荐配置:o4-mini模型 + on-request审批 + workspace-write沙箱

速查表

功能命令
启动Codexcodex
指定模型codex --model o3
全自动模式codex --full-auto
查看配置codex --help
重新登录codex logout && codex

与其他工具的对比

工具特点适合场景
CursorIDE集成,图形界面日常编码,需要可视化反馈
Claude Code终端工具,配置复杂需要多Agent协作,复杂任务
Codex CLI终端工具,OpenAI生态快速原型,测试编写,GPT用户

Codex CLI的核心优势是轻量和快速。它不需要打开IDE,直接在终端里就能完成大部分任务。特别是对于测试编写这种重复性工作,Codex CLI的效率很高。

如果你主要使用OpenAI的模型,Codex CLI是最自然的选择。它和ChatGPT订阅深度集成,登录就能用,不需要额外配置API Key。

下一篇预告:我们换到 OpenCode,用它配置前端部署和坚控。四个工具都过一遍后,最后一篇谈谈怎么把它们组合成一套完整的工作流。

热门栏目