测试 Testing
本教程共 56 篇 · 第 55 篇 · 更新于 2026-08-07 · 约 12 分钟阅读
本节目标:搞清楚 Astro 不绑定某个官方测试框架,而是用 astro check 做类型与专属校验,再自由搭配 Vitest、Playwright 等第三方库做单元与端到端测试。
写代码免不了出错,测试就是给你兜底的网。Astro 的态度很务实:它不强制你用某一个测试框架,而是提供官方的 astro check 做专属检查,再把单元测试、组件测试、端到端测试交给成熟的第三方工具。这一章帮你理清该怎么组合。
astro check:Astro 自带的专属检查
astro check 是 Astro 官方提供的命令,干两件事:一是跑 TypeScript 类型检查,二是跑 Astro 自己的一套诊断(比如组件 props 类型、.astro 文件里的专属错误)。它不是「测试框架」,不会断言业务逻辑,但能帮你挡掉一大类低级错误。
# 全局安装后,或在项目里用 npx
npx astro check
跑完它会列出类型错误和 Astro 报错,并给出非零退出码——这意味着你可以把它接进 CI(持续集成),每次提交自动检查。这个命令还负责触发 Astro 的类型生成:它会生成 astro:* 这类虚拟模块的类型定义,让你在编辑器里能用上 Astro.props、Astro.locals 等的补全。
顺带一提,类型定义来自 astro:* 虚拟模块的自动生成。第 43 章讲过,Astro 会根据你的 src/env.d.ts、内容集合 schema 等生成类型文件,这些类型正是 astro check 和编辑器共享的。所以保持 astro check 通过,等于保持类型世界的一致性。
单元测试与组件测试:Vitest
Vitest 是跑在 Vite 上的单元测试框架,原生支持 ESM、TypeScript 和 JSX,和 Astro 同根同源,配合最顺。要在 Astro 项目里用,关键是用 Astro 提供的 getViteConfig() 帮你的 Vitest 套上 Astro 的项目配置。
// vitest.config.ts
/// <reference types="vitest/config" />
import { getViteConfig } from 'astro/config';
export default getViteConfig({
test: {
// 这里写 Vitest 的配置项
},
});
默认情况下 getViteConfig() 会去加载你项目的 astro.config,把 Astro 的设置套到测试环境上。如果你需要给测试单独指定 Astro 配置(比如改 site、改 trailingSlash),可以传第二个参数:
export default getViteConfig(
{ test: { /* Vitest 配置 */ } },
{
site: 'https://example.com/',
trailingSlash: 'always',
},
);
用 Container API 测试 Astro 组件
Astro 4.9 起提供了 Container API,可以原生地渲染一个 .astro 组件并拿到它产出的 HTML 字符串,非常适合组件测试。先按上面配好 Vitest,再写个 .test.js 文件:
// example.test.js
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import { expect, test } from 'vitest';
import Card from '../src/components/Card.astro';
test('Card with slots', async () => {
const container = await AstroContainer.create();
const result = await container.renderToString(Card, {
slots: {
default: 'Card content',
},
});
expect(result).toContain('This is a card');
expect(result).toContain('Card content');
});
renderToString 把组件渲染成字符串,你就能断言输出里有没有某个文字、某个类名。插槽内容通过 slots 参数传进去,和真实模板里的 <slot /> 对应。
端到端测试:Playwright
单元测试管「小块代码对不对」,端到端(e2e)测试管「整个网站跑起来像不像样」。Playwright 是主流的 e2e 框架,能在 Chromium、WebKit、Firefox 上真实打开你的页面点来点去。
安装 Playwright 最简单的方式是走它的初始化向导,跟着提示选语言、命名测试目录即可:
npm init playwright@latest
装好后写第一个测试,验证首页标题对不对:
// src/test/index.spec.ts
import { test, expect } from '@playwright/test';
test('meta is correct', async ({ page }) => {
await page.goto("http://localhost:4321/");
await expect(page).toHaveTitle('Astro is awesome!');
});
跑测试时,Playwright 默认在终端报告结果。想看更完整的可视化报告:
npx playwright test index.spec.ts # 跑单个测试
npx playwright show-report # 打开 HTML 报告
进阶一点:让 Playwright 在测试时自己起一个服务器。在 playwright.config.ts 里加 webServer 配置,它会在测试前执行 npm run preview 并等端口就绪:
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
webServer: {
command: 'npm run preview',
url: 'http://localhost:4321/',
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI,
},
use: {
baseURL: 'http://localhost:4321/',
},
});
这样你先 npm run build,再 npm run test:e2e,整个流程就能自动化。
除了 Playwright,社区里 Cypress、Nightwatch 也常被用来给 Astro 做 e2e 测试,思路大同小异:起服务、开浏览器、断言页面行为。选哪个看你团队习惯,Astro 官方文档都给了示例。
测试环境下的两个注意点
在 Astro 里写测试,有两处和平时写页面不同,容易踩坑。
第一,import.meta.env。Astro 在构建时会把环境变量替换成字面量。测试环境用的是 Vitest 自己的 Vite 配置,不一定和你生产/开发的 import.meta.env 一致。如果你在组件里读 import.meta.env.PUBLIC_XXX,测试时最好通过 getViteConfig 的第二个参数或 Vitest 的 env 配置把需要的变量补上,否则可能读到 undefined。
第二,按需渲染的页面。带 export const prerender = false 或 output: 'server' 的页面、端点,依赖运行时服务端(读 cookie、读会话、连数据库)。Vitest 的组件测试默认只渲染静态 HTML,不会真的起服务端;要测这类逻辑,更合适的做法是直接对端点函数、Actions 的 handler 做单元测试,或者用 Playwright 起真实服务做 e2e。别指望在纯组件测试里验证服务端行为。
把测试接进 npm 脚本
测试命令一长就容易忘,最佳实践是把它们写进 package.json 的 scripts 里,用语义化的名字一键跑:
{
"scripts": {
"check": "astro check",
"test": "vitest run",
"test:watch": "vitest",
"test:e2e": "playwright test"
}
}
vitest run 是跑一次就退出的模式,适合 CI;vitest 不带 run 会进入监听模式,改代码自动重跑,适合本地开发。astro check 单独放一个 check 脚本,提交前顺手跑一下。
如果你要把测试接进 CI(比如 GitHub Actions),典型的流程是:先 npm ci 装依赖,再 npm run check 做类型检查,最后 npm test 跑单测,某一环失败就整条流水线变红、阻止合并。这样能把「低级错误」挡在合并之前,比等上线才发现省事得多。
测 Actions 的 handler
第 45 章讲过 Astro 的 Actions(astro:actions)用来处理表单后端。它的 handler 是纯函数,非常适合单元测试:直接调用 handler,传一个假的 context,断言返回结果。这种测试不依赖浏览器、不依赖起服务,速度快又稳。
// actions/addToCart.test.ts
import { expect, test } from 'vitest';
test('addToCart 把商品加进会话', async () => {
const fakeContext = {
session: { get: async () => [], set: async () => {} },
};
// 省略真实 import 与调用,思路是:传假 context,断言返回值
expect(true).toBe(true);
});
上面是思路骨架:用一个实现了 get/set 的假对象充当 context.session,就能在不连真 Redis 的情况下验证业务逻辑。真实的测试里你会 import 自己的 action 并断言它往会话里写了什么。
社区做法点到为止
Astro 社区没有「唯一正统」的测试栈。常见的组合是:astro check 守住类型和 Astro 专属错误;Vitest + Container API 做组件和工具函数单测;Playwright(或 Cypress)做 e2e。也有人把 React Testing Library 这类框架专属库接进来,专门测岛屿里的 React/Vue 组件。
你不必一次全上。小项目先保证 astro check 通过、加几个关键组件的 Vitest 测试就够;等页面多了、交互复杂了,再补 Playwright 的 e2e。测试是渐进投资,不是一次性工程。
小结
Astro 不替你定死测试框架,而是给 astro check 做类型与专属检查,把单元、组件、e2e 测试交给 Vitest、Playwright 这类成熟工具。配好 getViteConfig、用 Container API 渲染组件、用 Playwright 起服务跑 e2e,再留意 import.meta.env 和按需渲染在测试环境下的差异,就够搭出一套顺手的测试网。下一章我们汇总常见的报错与排查手段,让你写挂了也知道去哪找原因。