Skip to main content
Cypress应用

组件样式测试

info
你将学习到
  • 为什么需要测试组件样式
  • 如何正确渲染带样式的组件
  • 如何处理第三方 CSS 库

为什么需要测试组件样式?

样式表是组件业务逻辑的重要组成部分。最典型的例子就是模态框组件,常见问题包括:z-index 层级问题、无法关闭遮罩层、关闭模态框后仍无法与父页面交互等。

基于 Node 的测试运行器(如 Jest 或 Vitest)无法捕获这类问题,因为它们是在 JSDom 等模拟 DOM 环境中渲染样式的。JSDom 没有盒模型,某些断言(如判断父元素是否覆盖子元素阻止点击)在没有更真实环境的情况下无法测试。

而基于浏览器的测试运行器(如 Cypress)可以渲染应用样式和组件,并利用真实的盒模型和样式渲染引擎。Cypress 的 cy.click 等命令和 should('be.visible') 等断言能确保你测试的 UI 对终端用户可见且可交互,这是浏览器测试运行器的独特优势。

正确渲染组件

首次挂载任何新组件时,你可能会发现组件显示不正常。除非你的应用完全使用组件作用域 CSS(如 Styled Components 或 Vue 的 Scoped Styles),否则需要遵循本指南才能使组件在生产环境中正常显示和行为。

tip

确保你在生产环境中的所有操作都在组件 HTML 文件或组件支持文件中完成。

组件支持文件

加载组件或端到端测试文件时,会首先加载一个支持文件。默认情况下,首次设置 Cypress 组件测试时会自动创建该文件,位于 cypress/support/component.js。这个文件让你有机会设置测试环境。

对于组件测试,你可以用这个文件设置通常在挂载组件前就已存在的页面级配置,例如:

  1. 运行时 JavaScript 代码(状态管理、路由、UI 库)
  2. 全局样式(样式重置、Tailwind)

一般来说,你的组件支持文件应该与应用的入口 JavaScript 文件(如 main.js、index.js)和主 CSS 文件(如 main.css、index.css)非常相似。

第三方 CSS 库(Tailwind、Bootstrap、PopperJS)

组件包含三个部分:标记、样式和脚本逻辑。三者共同作用才能交付可工作的组件。

样式也是业务逻辑的一部分。

  1. Tailwind
  2. CSS 模块
  3. 作用域样式
  4. Styled Components
  5. 常规样式表
  6. UI 库

本指南将帮助你设置测试基础设施以正确渲染组件样式。

根据应用构建方式的不同,首次挂载新组件时可能会完全或部分丢失样式。

这很正常。许多应用都有一些在组件文件外运行的一次性设置。

我们在预期运行环境中构建应用,并假设组件总是在根级组件(如 <App>)或带样式规则的顶级选择器(如 #app { /* 样式 */ })中渲染。

当尝试隔离组件进行测试时,我们需要重建这个环境。稍后会详细说明。首先,让我们讨论样式表测试以及 Cypress 与其他组件测试工具的最大区别。

导入样式表

每个应用或组件库导入样式的方式略有不同。我们将介绍几种方法,并说明如何快速重构组件使其更易于测试。

如果不遵循本指南,你的组件可以挂载,但显示不正确,且无法充分利用 Cypress 最有价值的功能,特别是对宽度、高度和溢出的隐式检查,以确保组件不仅存在于页面 HTML 中,而且对用户可见。

样式设置规则

所有应用样式最终都需要进入 Cypress,这样挂载组件时才能正确显示。

我们提供两个钩子供你配置样式:

  1. 名为 cypress/support/component-index.html 的 HTML 文件
  2. 名为 cypress/support/component.js 的 JavaScript 支持文件

创建类生产测试环境时,应始终模仿你自己的应用设置。如果应用在 head 中有多个 <link> 标签加载字体或其他样式表,确保 cypress/support/component-index.html 文件包含相同的 <link> 标签。同理,如果在应用的 main.js 文件中加载样式,确保在 cypress/support/component.js 文件中 import 这些样式。

因此,强烈建议创建一个 src/setup.js 文件,在 main.js 入口点和测试设置中复用。示例项目结构如下:

> /cypress
> /support
> /component.js
> /src
> /main.js
> /main.css
> /setup.js

setup.js 内容可能如下:

import '~normalize/normalize.css'
import 'font-awesome'
import './main.css'

export const createStore = () => {
return /* store */
}

export const createRouter = () => {
return /* router */
}

export const createApp = () => {
return <App router={createRouter()} store={createStore()}></App>
}

main.js 中的使用方式如下:

import { createApp } from './setup.js'

ReactDOM.render(createApp())

而 Cypress 会在支持文件中复用:

/* 就这样简单! */
import '../../src/setup.js'

本节其余部分将讨论你可能遇到的特定样式问题,包括:字体、图标字体、样式重置、全局应用样式和第三方组件库样式。

全局应用样式

全局应用样式通常位于以下位置之一:

  1. 在应用 head 中导入的 styles.css 文件。

这应该在 Cypress 索引 HTML 文件中加载。

  1. 在根级组件如 App.jsxApp.vueApp.svelte 等中。

通过将根 CSS 从 App 或入口组件中解耦,将这些全局样式提取到顶级样式表中。Vue 和 Svelte 都将全局应用样式嵌入到主入口组件中。应用的其余部分期望在这些组件中渲染,因此在编写这些组件时所做的任何假设都必须在测试环境中复制,否则组件显示会不正常。

<style>
/* 在某些脚手架中,App.vue 文件没有单独的样式文件 */

#app {
font-family: Sans-serif;
}
</style>

应改为

/* App.vue */ <style src="./app.css" />

/* cypress/support/component.js */ import '../../src/app.css'
  1. 在应用的 main.js 文件中(随后挂载根级组件)。

上一节已经介绍了如何复用应用开头导入的样式表。

import './main.css'
  1. 在配置文件中,如 next.config.js

你通常需要为这些样式表提供公共路径。可以在 cypress/support/component-index.html 文件中导入相同的路径。

CSS 重置或 Normalize 未生效

你是否在 cypress/support/component-index.htmlcypress/support/component.js 中导入了 normalize 文件?

字体:所有内容都以 Times New Roman 渲染

大多数应用通过两种方式处理字体。

  1. index.htmlhead 标签中加载外部字体。
<head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Readex+Pro:wght@200;300;400;500;600;700&family=Roboto&display=swap"
rel="stylesheet"
/>
</head>

或通过 @import 语句

<head>
<style>
@import url('https://fonts.googleapis.com/css2?family=Readex+Pro:wght@200;300;400;500;600;700&family=Roboto&display=swap');
</style>
</head>
  1. 主样式表加载字体
/* main.css */
@font-face {
font-family: 'Fira Sans';
src: url('fonts/fira/eot/FiraSans-Regular.eot');
src:
url('fonts/fira/eot/FiraSans-Regular.eot') format('embedded-opentype'),
url('fonts/fira/woff2/FiraSans-Regular.woff2') format('woff2'),
url('fonts/fira/woff/FiraSans-Regular.woff') format('woff'),
url('fonts/fira/woff2/FiraSans-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}

图标字体:所有图标都无法渲染

主题提供者:组件显示/编译不正常,因为无法访问提供者

主题提供者或其他应用级包装器(如 I18n 或 Material UI)通过围绕应用注入自身来工作。进行组件测试时,你还没有渲染组件周围的层级结构。

要解决这类问题,人们会查看自定义命令和包装器。

首先解释为什么不对,必须先说明什么是"类生产环境"。

因此我们有这个前后对比,现在任务是逐步检查被测组件,尝试找出生产环境和测试环境之间的差异。

有时这些差异简单如颜色或字体不匹配。其他时候,整个组件或部分可能无法编译。

显示不正常的原因是:

  1. 我的浏览器支持深色模式
  2. <App> 组件提供了自己的样式