> For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt.

# CSS

Rstest 既可以通过 Rsbuild 工具链处理样式，也可以在测试不关心样式时替换样式导入。请根据测试目标选择配置：

| 测试目标                                  | 推荐方案                             |
| ------------------------------------- | -------------------------------- |
| 只测组件逻辑，不检查样式                          | [使用默认样式处理](#nodejs-测试)           |
| 使用 CSS Modules class name，或检查预处理器能否编译 | [在 Node.js 测试中处理样式](#nodejs-测试)  |
| 检查 computed styles、布局或视觉效果            | [使用 Browser Mode](#browser-mode) |

## 在测试中处理样式

### Node.js 测试

Rstest 内置支持 CSS 和 CSS Modules。在 Node.js 测试（包括 jsdom 和 happy-dom）中，Rstest 默认处理 CSS，但不输出 CSS 产物：

- 普通 CSS 文件不会生成样式产物。
- CSS Modules 会导出 class name 映射，可直接用于组件测试。
- 没有匹配的 loader 或资源类型时，普通 `.less`、`.scss` 和 `.sass` 导入会导出空字符串（`''`），无需安装预处理器 plugin。
- 启用 Less 或 Sass plugin 后，样式处理及其配置照常生效，编译错误仍会报出。

兜底同样适用于 `.module.less`、`.module.scss` 和 `.module.sass`：默认导出为 `{}`，因此 `styles.button` 的值为 `undefined`。需要真实 class name 映射时，请启用预处理器 plugin；希望用属性名作为类名时，可以[替换样式导入](#在逻辑测试中替换样式)。

用户配置的 loader、alias 和资源类型会保留原有行为。带 query 的导入（如 `?raw` 或 `?url`）需要对应的 plugin 或规则，不会使用兜底。样式文件仍须存在，兜底不会替换无法解析的导入。

例如，`.css` 和 `.module.css` 无需额外配置即可导入：

```tsx title="Button.tsx"
import styles from './Button.module.css';
import './reset.css';

export function Button() {
  return <button className={styles.button}>Submit</button>;
}
```

```ts title="Button.test.tsx"
import { expect, test } from '@rstest/core';
import styles from './Button.module.css';

test('loads CSS Modules', () => {
  expect(styles.button).toEqual(expect.any(String));
});
```

CSS Modules 常用 `.module.css`、`.module.less` 或 `.module.scss` 作为文件名。可以通过 [`output.cssModules`](/zh/config/build/output.md#outputcssmodules) 自定义 class name 生成规则和其他 CSS Modules 选项。

### 添加 CSS 预处理器支持

需要编译预处理器样式时，请启用对应的 Rsbuild plugin。下面以 Less 和 Sass 为例；如果使用其他预处理器，请先查看 [Rsbuild plugin 列表](https://rsbuild.rs/plugins/list/) 是否有对应的 plugin，再按相同方式注册。只需安装测试实际用到的 plugin。

[@rsbuild/plugin-less](https://github.com/web-infra-dev/rsbuild/tree/main/packages/plugin-less) 用于编译 `.less` 和 `.module.less` 文件：


```sh [npm]
npm add @rsbuild/plugin-less -D
```

```sh [yarn]
yarn add @rsbuild/plugin-less -D
```

```sh [pnpm]
pnpm add @rsbuild/plugin-less -D
```

```sh [bun]
bun add @rsbuild/plugin-less -D
```

```sh [deno]
deno add npm:@rsbuild/plugin-less -D
```

```ts title="rstest.config.ts"
import { pluginLess } from '@rsbuild/plugin-less';
import { defineConfig } from '@rstest/core';

export default defineConfig({
  plugins: [pluginLess()],
});
```

[@rsbuild/plugin-sass](https://github.com/web-infra-dev/rsbuild/tree/main/packages/plugin-sass) 用于编译 `.sass`、`.scss`、`.module.sass` 和 `.module.scss` 文件：


```sh [npm]
npm add @rsbuild/plugin-sass -D
```

```sh [yarn]
yarn add @rsbuild/plugin-sass -D
```

```sh [pnpm]
pnpm add @rsbuild/plugin-sass -D
```

```sh [bun]
bun add @rsbuild/plugin-sass -D
```

```sh [deno]
deno add npm:@rsbuild/plugin-sass -D
```

```ts title="rstest.config.ts"
import { pluginSass } from '@rsbuild/plugin-sass';
import { defineConfig } from '@rstest/core';

export default defineConfig({
  plugins: [pluginSass()],
});
```

如果项目通过 [`@rstest/adapter-rsbuild`](/zh/integration/rsbuild.md) 复用 Rsbuild 配置，请把这些 plugin 配置在 `rsbuild.config.ts` 中，不要在 `rstest.config.ts` 中重复配置。

:::warning
如果项目使用 [`@rstest/adapter-rspack`](/zh/integration/rspack.md)，上面的 Rsbuild plugin 和 `output.cssModules` 示例不适用。该 adapter 使用 `rspack.config.ts` 中的 CSS 规则，请在 Rspack 配置中设置 Less、Sass 和 CSS Modules。
:::

### Browser mode

Node.js 测试可以检查样式导入值，但样式不会参与页面渲染。需要检查 computed styles、布局或视觉效果时，请使用 [Browser Mode](/zh/guide/browser-testing/index.md)。Browser Mode 会在真实浏览器中运行组件并加载样式，不使用 Node.js 的空样式兜底；Less 和 Sass 需要对应的 plugin 或 loader。

## 在逻辑测试中替换样式

如果 Node.js 测试只检查组件逻辑，可以用测试替身替换样式导入。这样无需安装 Less 或 Sass plugin，也不会执行 CSS 预处理，但测试无法再验证被替换的样式。

### 用 alias 替换少量导入

如果只有少量确定的 CSS Modules 导入，可以把 [`resolve.alias`](/zh/config/build/resolve.md#resolvealias) 与 [`identity-obj-proxy`](https://github.com/keyz/identity-obj-proxy) 配合使用。这个包会把属性名原样作为属性值返回，因此 `styles.button` 的值是 `button`。


```sh [npm]
npm add identity-obj-proxy -D
```

```sh [yarn]
yarn add identity-obj-proxy -D
```

```sh [pnpm]
pnpm add identity-obj-proxy -D
```

```sh [bun]
bun add identity-obj-proxy -D
```

```sh [deno]
deno add npm:identity-obj-proxy -D
```

```ts title="rstest.config.ts"
import { createRequire } from 'node:module';
import { defineConfig } from '@rstest/core';

const require = createRequire(import.meta.url);

export default defineConfig({
  resolve: {
    alias: {
      './Button.module.less$': require.resolve('identity-obj-proxy'),
    },
  },
});
```

alias key 末尾的 `$` 表示只匹配完整的导入请求，它只是 alias 配置中的标记，不属于实际导入路径。

`resolve.alias` 按字符串前缀匹配，不支持正则表达式，因此适合替换少量且稳定的样式导入。alias 也可以指向项目内的测试替身；例如，对于 `import './reset.less'` 这类只执行副作用的导入，指向一个空模块即可。

### 按扩展名批量替换

样式导入较多时，可以通过 `NormalModuleReplacementPlugin` 按扩展名批量替换。下面的 Node.js 配置会把 CSS Modules 和只执行副作用的样式导入全部替换为 `identity-obj-proxy`：

```ts title="rstest.config.ts"
import { defineConfig } from '@rstest/core';

export default defineConfig({
  tools: {
    rspack(config, { rspack, isServer }) {
      if (!isServer) {
        return;
      }

      config.plugins.push(
        new rspack.NormalModuleReplacementPlugin(
          /\.(css|less|sass|scss)(?:\?.*)?$/,
          (resource) => {
            resource.request = 'identity-obj-proxy';
          },
        ),
      );
    },
  },
});
```

这两种替换方式都会跳过样式解析和编译，使用时需要留意以下限制：

- `styles.button` 返回的是 `button`，不是实际构建生成的 class name。
- Less、Sass 和 CSS Modules 语法不会经过校验。
- 样式文件不存在或路径写错时不会报错，因为导入会在解析原文件之前被替换。
- 无法测试 `composes`、`:global`、样式产物，以及客户端与服务端 class name 是否一致。

如果测试依赖其中任何一项，请保留真实样式处理；如果断言依赖最终渲染结果，请使用 Browser Mode。
