Svelte 组件测试:使用 Jest 和 Testing Library
安装 Jest 和 Testing Library
在开始使用 Jest 和 Testing Library 进行 Svelte 组件测试之前,我们需要先在项目中安装它们。假设你已经有一个 Svelte 项目,并且项目使用 npm
或 yarn
作为包管理器。
首先,安装 Jest。在项目根目录下运行以下命令:
npm install --save-dev jest
或者使用 yarn
:
yarn add --dev jest
接下来,安装 Svelte 相关的 Jest 配置。这能让 Jest 正确处理 Svelte 组件。运行:
npm install --save-dev @sveltejs/jest
或
yarn add --dev @sveltejs/jest
然后,安装 Testing Library 及其 Svelte 专用版本。Testing Library 是一组用于测试 React、Vue、Svelte 等各种前端框架组件的工具,它强调从用户视角进行测试。
npm install --save-dev @testing-library/jest-dom @testing-library/svelte
或
yarn add --dev @testing-library/jest-dom @testing-library/svelte
安装完成后,我们还需要对 Jest 进行一些配置。在项目根目录下创建或编辑 jest.config.js
文件,添加如下内容:
module.exports = {
preset: '@sveltejs/jest',
moduleFileExtensions: ['js', 'json','svelte'],
transform: {
'^.+\\.svelte$': '@sveltejs/jest',
'^.+\\.js$': 'babel-jest'
},
setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect']
};
上述配置中,preset
指定了使用 @sveltejs/jest
预设,moduleFileExtensions
告诉 Jest 哪些文件扩展名需要处理,transform
配置了如何转换 Svelte 和 JavaScript 文件,setupFilesAfterEnv
引入了 @testing-library/jest-dom
的扩展断言,这能让我们在测试中使用更友好的断言方式。
编写第一个 Svelte 组件测试
假设我们有一个简单的 Svelte 组件 Button.svelte
,代码如下:
<script>
let text = 'Click me';
let count = 0;
const handleClick = () => {
count++;
};
</script>
<button on:click={handleClick}>
{text} {count}
</button>
这个组件是一个简单的按钮,显示“Click me”字样以及点击次数。现在我们来编写测试用例,确保按钮的功能正常。
在 Button.svelte
同一目录下创建 Button.test.js
文件(测试文件命名一般遵循 组件名.test.js
的规则)。
import { render, screen } from '@testing-library/svelte';
import Button from './Button.svelte';
describe('Button Component', () => {
test('renders button with initial text', () => {
render(Button);
const button = screen.getByText('Click me 0');
expect(button).toBeInTheDocument();
});
test('increments count on click', () => {
render(Button);
const button = screen.getByText('Click me 0');
button.click();
const updatedButton = screen.getByText('Click me 1');
expect(updatedButton).toBeInTheDocument();
});
});
在上述测试代码中:
- 我们从
@testing-library/svelte
导入了render
和screen
。render
用于渲染 Svelte 组件,screen
提供了一系列查询函数来查找渲染后的组件元素。 - 使用
describe
块来分组测试用例,这里我们将测试用例归为“Button Component”组。 - 第一个
test
用例检查按钮是否以初始文本正确渲染。通过render
渲染Button
组件后,使用screen.getByText
查找包含“Click me 0”文本的元素,并使用 Jest 的expect
断言该元素存在于文档中。 - 第二个
test
用例验证按钮点击后计数是否增加。同样先渲染组件,找到初始按钮并模拟点击,然后再次查找更新后的文本“Click me 1”,断言更新后的按钮存在于文档中。
测试组件的属性传递
Svelte 组件常常接收属性(props)来定制其行为和外观。假设我们有一个 Greeting.svelte
组件,它接收一个 name
属性来显示个性化问候:
<script>
export let name = 'World';
</script>
<p>Hello, {name}!</p>
我们来编写测试用例,验证属性传递是否正确。在 Greeting.svelte
同一目录下创建 Greeting.test.js
文件:
import { render, screen } from '@testing-library/svelte';
import Greeting from './Greeting.svelte';
describe('Greeting Component', () => {
test('renders default greeting', () => {
render(Greeting);
const greeting = screen.getByText('Hello, World!');
expect(greeting).toBeInTheDocument();
});
test('renders custom greeting', () => {
render(Greeting, { name: 'John' });
const greeting = screen.getByText('Hello, John!');
expect(greeting).toBeInTheDocument();
});
});
在这个测试代码中:
- 第一个
test
用例检查组件在未传递name
属性时,是否显示默认问候“Hello, World!”。 - 第二个
test
用例通过render
函数的第二个参数传递name: 'John'
属性,验证组件是否显示定制的问候“Hello, John!”。
测试组件的事件处理
有时候我们需要测试组件如何处理外部触发的事件。例如,我们有一个 InputWithSubmit.svelte
组件,它包含一个输入框和一个提交按钮,提交时会触发一个自定义事件并传递输入的值:
<script>
let inputValue = '';
const handleSubmit = () => {
const customEvent = new CustomEvent('submit', { detail: inputValue });
$dispatch(customEvent);
};
</script>
<input bind:value={inputValue} />
<button on:click={handleSubmit}>Submit</button>
现在编写测试用例来验证事件是否正确触发以及传递的值是否正确。在 InputWithSubmit.svelte
同一目录下创建 InputWithSubmit.test.js
文件:
import { render } from '@testing-library/svelte';
import InputWithSubmit from './InputWithSubmit.svelte';
describe('InputWithSubmit Component', () => {
test('triggers submit event with correct value', () => {
const { component } = render(InputWithSubmit);
const input = component.shadowRoot.querySelector('input');
const button = component.shadowRoot.querySelector('button');
input.value = 'Test Value';
input.dispatchEvent(new Event('input', { bubbles: true }));
button.click();
let submitValue;
component.$on('submit', (event) => {
submitValue = event.detail;
});
expect(submitValue).toBe('Test Value');
});
});
在上述测试代码中:
- 通过
render
获取组件实例component
。 - 使用
component.shadowRoot.querySelector
找到输入框和按钮元素。 - 模拟输入框输入值并触发
input
事件,然后模拟点击按钮触发提交。 - 通过
component.$on
监听submit
事件,并在事件回调中获取传递的值submitValue
。 - 最后使用
expect
断言传递的值是否为“Test Value”。
测试组件的状态变化
许多 Svelte 组件依赖内部状态来呈现不同的 UI。例如,我们有一个 Toggle.svelte
组件,它有一个布尔状态 isOn
,通过点击按钮切换该状态并显示相应文本:
<script>
let isOn = false;
const toggle = () => {
isOn =!isOn;
};
</script>
<button on:click={toggle}>
{isOn? 'Turn Off' : 'Turn On'}
</button>
下面编写测试用例来验证状态变化和 UI 更新。在 Toggle.svelte
同一目录下创建 Toggle.test.js
文件:
import { render, screen } from '@testing-library/svelte';
import Toggle from './Toggle.svelte';
describe('Toggle Component', () => {
test('renders with initial state', () => {
render(Toggle);
const button = screen.getByText('Turn On');
expect(button).toBeInTheDocument();
});
test('toggles state on click', () => {
render(Toggle);
const button = screen.getByText('Turn On');
button.click();
const updatedButton = screen.getByText('Turn Off');
expect(updatedButton).toBeInTheDocument();
});
});
在这个测试代码中:
- 第一个
test
用例验证组件初始渲染时按钮文本为“Turn On”。 - 第二个
test
用例模拟按钮点击,验证状态切换后按钮文本更新为“Turn Off”。
测试组件的样式
虽然 Jest 和 Testing Library 主要关注功能测试,但我们也可以通过一些方式测试组件的样式。例如,我们有一个 StyledButton.svelte
组件,它有特定的背景颜色和文本颜色:
<script>
let text = 'Styled Button';
</script>
<style>
button {
background-color: blue;
color: white;
}
</style>
<button>{text}</button>
我们可以测试按钮是否应用了正确的样式。在 StyledButton.svelte
同一目录下创建 StyledButton.test.js
文件:
import { render } from '@testing-library/svelte';
import StyledButton from './StyledButton.svelte';
describe('StyledButton Component', () => {
test('has correct background color', () => {
const { container } = render(StyledButton);
const button = container.firstChild;
const backgroundColor = window.getComputedStyle(button).getPropertyValue('background-color');
expect(backgroundColor).toBe('rgb(0, 0, 255)');
});
test('has correct text color', () => {
const { container } = render(StyledButton);
const button = container.firstChild;
const textColor = window.getComputedStyle(button).getPropertyValue('color');
expect(textColor).toBe('rgb(255, 255, 255)');
});
});
在上述测试代码中:
- 通过
render
获取包含组件的容器container
。 - 使用
window.getComputedStyle
获取按钮的计算样式,并通过getPropertyValue
获取背景颜色和文本颜色。 - 使用
expect
断言颜色值是否符合预期。
测试组件的生命周期
Svelte 组件有自己的生命周期函数,如 onMount
、beforeUpdate
和 afterUpdate
等。我们可以测试这些生命周期函数是否正确执行。假设我们有一个 LifecycleComponent.svelte
组件:
<script>
let mounted = false;
let updated = false;
onMount(() => {
mounted = true;
});
beforeUpdate(() => {
updated = false;
});
afterUpdate(() => {
updated = true;
});
</script>
{#if mounted}
<p>Component is mounted. Update status: {updated? 'Updated' : 'Not Updated'}</p>
{/if}
现在编写测试用例来验证生命周期函数。在 LifecycleComponent.svelte
同一目录下创建 LifecycleComponent.test.js
文件:
import { render, screen } from '@testing-library/svelte';
import LifecycleComponent from './LifecycleComponent.svelte';
describe('LifecycleComponent Component', () => {
test('mounts component and updates status', () => {
render(LifecycleComponent);
const mountedText = screen.getByText('Component is mounted. Update status: Not Updated');
expect(mountedText).toBeInTheDocument();
});
test('updates component and changes status', async () => {
const { component } = render(LifecycleComponent);
component.$set({});
await component.$$.flush();
const updatedText = screen.getByText('Component is mounted. Update status: Updated');
expect(updatedText).toBeInTheDocument();
});
});
在这个测试代码中:
- 第一个
test
用例验证组件挂载后,显示的文本表明组件已挂载且未更新。 - 第二个
test
用例通过component.$set({})
触发组件更新,await component.$$.flush()
等待更新完成,然后验证显示的文本表明组件已更新。
测试异步操作
在 Svelte 组件中,我们常常会遇到异步操作,如数据获取。假设我们有一个 AsyncData.svelte
组件,它在组件挂载时异步获取数据并显示:
<script>
let data = null;
onMount(async () => {
const response = await fetch('https://example.com/api/data');
const result = await response.json();
data = result;
});
</script>
{#if data}
<p>{data.message}</p>
{:else}
<p>Loading...</p>
{/if}
我们来编写测试用例,模拟异步数据获取并验证组件显示。在 AsyncData.svelte
同一目录下创建 AsyncData.test.js
文件:
import { render, screen, waitFor } from '@testing-library/svelte';
import AsyncData from './AsyncData.svelte';
import fetchMock from 'fetch-mock';
describe('AsyncData Component', () => {
afterEach(() => {
fetchMock.restore();
});
test('shows loading text initially', () => {
render(AsyncData);
const loadingText = screen.getByText('Loading...');
expect(loadingText).toBeInTheDocument();
});
test('shows data after fetching', async () => {
const mockData = { message: 'Mocked Data' };
fetchMock.getOnce('https://example.com/api/data', mockData);
render(AsyncData);
await waitFor(() => screen.getByText('Mocked Data'));
});
});
在上述测试代码中:
- 使用
fetchMock
库来模拟fetch
操作。在每个测试用例后通过fetchMock.restore()
恢复原始的fetch
行为。 - 第一个
test
用例验证组件初始显示“Loading...”文本。 - 第二个
test
用例使用fetchMock.getOnce
模拟数据获取,并通过waitFor
等待组件显示模拟的数据“Mocked Data”。
测试嵌套组件
Svelte 组件常常包含嵌套组件。假设我们有一个 ParentComponent.svelte
组件,它包含一个 ChildComponent.svelte
组件:
ChildComponent.svelte
<script>
export let text = 'Default Text';
</script>
<p>{text}</p>
ParentComponent.svelte
<script>
import ChildComponent from './ChildComponent.svelte';
</script>
<ChildComponent text="Nested Text" />
现在编写测试用例来验证嵌套组件是否正确渲染。在 ParentComponent.svelte
同一目录下创建 ParentComponent.test.js
文件:
import { render, screen } from '@testing-library/svelte';
import ParentComponent from './ParentComponent.svelte';
describe('ParentComponent Component', () => {
test('renders nested child component with correct text', () => {
render(ParentComponent);
const childText = screen.getByText('Nested Text');
expect(childText).toBeInTheDocument();
});
});
在这个测试代码中,通过渲染 ParentComponent
并查找 ChildComponent
传递的文本“Nested Text”,验证嵌套组件是否正确渲染。
处理测试中的错误
在测试过程中,可能会遇到各种错误,如组件渲染失败、断言失败等。例如,假设我们有一个 ErrorComponent.svelte
组件,在特定条件下会抛出错误:
<script>
export let shouldError = false;
if (shouldError) {
throw new Error('Test Error');
}
</script>
<p>Component without error</p>
我们编写测试用例来验证错误是否正确抛出。在 ErrorComponent.svelte
同一目录下创建 ErrorComponent.test.js
文件:
import { render } from '@testing-library/svelte';
import ErrorComponent from './ErrorComponent.svelte';
describe('ErrorComponent Component', () => {
test('renders without error when shouldError is false', () => {
render(ErrorComponent);
});
test('throws error when shouldError is true', () => {
expect(() => render(ErrorComponent, { shouldError: true })).toThrow('Test Error');
});
});
在上述测试代码中:
- 第一个
test
用例验证当shouldError
为false
时,组件正常渲染。 - 第二个
test
用例使用expect(() =>...).toThrow
断言当shouldError
为true
时,渲染组件会抛出“Test Error”错误。
通过以上详细的步骤和示例,你应该对使用 Jest 和 Testing Library 进行 Svelte 组件测试有了全面的了解。从基础的安装配置到各种复杂场景的测试用例编写,这些知识将帮助你确保 Svelte 组件的质量和可靠性。在实际项目中,根据组件的具体功能和需求,灵活运用这些测试技巧,可以大大提高开发效率和代码的稳定性。