跳至主要内容

测试

测试可以帮助您编写和维护代码,并防止出现回归问题。测试框架可以帮助您实现这一点,允许您描述关于代码行为的断言或期望。Svelte 对使用哪个测试框架没有偏好——您可以使用 Vitest、Jasmine、Cypress 和 Playwright 等解决方案编写单元测试、集成测试和端到端测试。

使用 Vitest 进行单元和集成测试

单元测试允许您测试代码的小而隔离的部分。集成测试允许您测试应用程序的部分,以查看它们是否协同工作。如果您使用 Vite(包括通过 SvelteKit),我们建议使用 Vitest

要开始使用,请安装 Vitest

npm install -D vitest

然后调整您的 vite.config.js

vite.config
import { function defineConfig(config: UserConfig): UserConfig (+3 overloads)defineConfig } from 'vitest/config';

export default function defineConfig(config: UserConfig): UserConfig (+3 overloads)defineConfig({
	// ...
	// Tell Vitest to use the `browser` entry points in `package.json` files, even though it's running in Node
	
UserConfig.resolve?: (ResolveOptions & {
    alias?: AliasOptions;
}) | undefined

Configure resolver

resolve
: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnv

The process.env property returns an object containing the user environment. See environ(7).

An example of this object looks like:

{
  TERM: 'xterm-256color',
  SHELL: '/usr/local/bin/bash',
  USER: 'maciej',
  PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
  PWD: '/Users/maciej',
  EDITOR: 'vim',
  SHLVL: '1',
  HOME: '/Users/maciej',
  LOGNAME: 'maciej',
  _: '/usr/local/bin/node'
}

It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other Worker threads. In other words, the following example would not work:

node -e 'process.env.foo = "bar"' && echo $foo

While the following will:

import { env } from 'node:process';

env.foo = 'bar';
console.log(env.foo);

Assigning a property on process.env will implicitly convert the value to a string. This behavior is deprecated. Future versions of Node.js may throw an error when the value is not a string, number, or boolean.

import { env } from 'node:process';

env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'

Use delete to delete a property from process.env.

import { env } from 'node:process';

env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined

On Windows operating systems, environment variables are case-insensitive.

import { env } from 'node:process';

env.TEST = 1;
console.log(env.test);
// => 1

Unless explicitly specified when creating a Worker instance, each Worker thread has its own copy of process.env, based on its parent thread’s process.env, or whatever was specified as the env option to the Worker constructor. Changes to process.env will not be visible across Worker threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of process.env on a Worker instance operates in a case-sensitive manner unlike the main thread.

@sincev0.1.27
env
.string | undefinedVITEST
? { ResolveOptions.conditions?: string[] | undefinedconditions: ['browser'] } : var undefinedundefined });

如果加载所有软件包的浏览器版本不可取,例如,您还测试后端库,您可能需要采用别名配置

您现在可以为 .js/.ts 文件中的代码编写单元测试

multiplier.svelte.test
import { function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
} from 'svelte';
import { const expect: ExpectStaticexpect, const test: TestAPI

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
} from 'vitest';
import { import multipliermultiplier } from './multiplier.js'; test<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number | TestOptions): void (+2 overloads)

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
('Multiplier', () => {
let let double: anydouble = import multipliermultiplier(0, 2); expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let double: anydouble.value).JestAssertion<any>.toEqual: <number>(expected: number) => voidtoEqual(0); let double: anydouble.set(5); expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let double: anydouble.value).JestAssertion<any>.toEqual: <number>(expected: number) => voidtoEqual(10); });

在测试文件中使用符文

可以在测试文件中使用符文。首先确保您的打包器知道在运行测试之前通过 Svelte 编译器路由文件,方法是在文件名中添加 .svelte(例如 multiplier.svelte.test.js)。之后,您可以在测试中使用符文。

multiplier.svelte.test
import { function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
} from 'svelte';
import { const expect: ExpectStaticexpect, const test: TestAPI

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
} from 'vitest';
import { import multipliermultiplier } from './multiplier.svelte.js'; test<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number | TestOptions): void (+2 overloads)

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
('Multiplier', () => {
let let count: numbercount =
function $state<0>(initial: 0): 0 (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.js.cn/docs/svelte/$state

@paraminitial The initial value
$state
(0);
let let double: anydouble = import multipliermultiplier(() => let count: numbercount, 2); expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let double: anydouble.value).JestAssertion<any>.toEqual: <number>(expected: number) => voidtoEqual(0); let count: numbercount = 5; expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let double: anydouble.value).JestAssertion<any>.toEqual: <number>(expected: number) => voidtoEqual(10); });

如果正在测试的代码使用效果,则需要将测试包装在 $effect.root

logger.svelte.test
import { function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
} from 'svelte';
import { const expect: ExpectStaticexpect, const test: TestAPI

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
} from 'vitest';
import { import loggerlogger } from './logger.svelte.js'; test<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number | TestOptions): void (+2 overloads)

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
('Effect', () => {
const const cleanup: () => voidcleanup =
namespace $effect
function $effect(fn: () => void | (() => void)): void

Runs code when a component is mounted to the DOM, and then whenever its dependencies change, i.e. $state or $derived values. The timing of the execution is after the DOM has been updated.

Example:

$effect(() => console.log('The count is now ' + count));

If you return a function from the effect, it will be called right before the effect is run again, or when the component is unmounted.

Does not run during server side rendering.

https://svelte.js.cn/docs/svelte/$effect

@paramfn The function to execute
$effect
.function $effect.root(fn: () => void | (() => void)): () => void

The $effect.root rune is an advanced feature that creates a non-tracked scope that doesn’t auto-cleanup. This is useful for nested effects that you want to manually control. This rune also allows for creation of effects outside of the component initialisation phase.

Example:

&#x3C;script>
  let count = $state(0);

  const cleanup = $effect.root(() => {
	$effect(() => {
			console.log(count);
		})

	 return () => {
	   console.log('effect root cleanup');
			}
  });
&#x3C;/script>

&#x3C;button onclick={() => cleanup()}>cleanup&#x3C;/button>

https://svelte.js.cn/docs/svelte/$effect#$effect.root

root
(() => {
let let count: numbercount =
function $state<0>(initial: 0): 0 (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.js.cn/docs/svelte/$state

@paraminitial The initial value
$state
(0);
// logger uses an $effect to log updates of its input let let log: anylog = import loggerlogger(() => let count: numbercount); // effects normally run after a microtask, // use flushSync to execute all pending effects synchronously function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
();
expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let log: anylog.value).JestAssertion<any>.toEqual: <number[]>(expected: number[]) => voidtoEqual([0]); let count: numbercount = 1; function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
();
expect<any>(actual: any, message?: string): Assertion<any> (+1 overload)expect(let log: anylog.value).JestAssertion<any>.toEqual: <number[]>(expected: number[]) => voidtoEqual([0, 1]); }); const cleanup: () => voidcleanup(); });

组件测试

可以使用 Vitest 隔离测试您的组件。

在编写组件测试之前,请考虑您是否真的需要测试组件,或者它更多的是关于组件内部的逻辑。如果是这样,请考虑提取该逻辑以独立测试它,而无需组件的开销

要开始使用,请安装 jsdom(一个模拟 DOM API 的库)

npm install -D jsdom

然后调整您的 vite.config.js

vite.config
import { function defineConfig(config: UserConfig): UserConfig (+3 overloads)defineConfig } from 'vitest/config';

export default function defineConfig(config: UserConfig): UserConfig (+3 overloads)defineConfig({
	UserConfig.plugins?: PluginOption[] | undefined

Array of vite plugins to use.

plugins
: [
/* ... */ ], UserConfig.test?: InlineConfig | undefined

Options for Vitest

test
: {
// If you are testing components client-side, you need to setup a DOM environment. // If not all your files should have this environment, you can use a // `// @vitest-environment jsdom` comment at the top of the test files instead. InlineConfig.environment?: VitestEnvironment | undefined

Running environment

Supports ‘node’, ‘jsdom’, ‘happy-dom’, ‘edge-runtime’

If used unsupported string, will try to load the package vitest-environment-${env}

@default'node'
environment
: 'jsdom'
}, // Tell Vitest to use the `browser` entry points in `package.json` files, even though it's running in Node
UserConfig.resolve?: (ResolveOptions & {
    alias?: AliasOptions;
}) | undefined

Configure resolver

resolve
: var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnv

The process.env property returns an object containing the user environment. See environ(7).

An example of this object looks like:

{
  TERM: 'xterm-256color',
  SHELL: '/usr/local/bin/bash',
  USER: 'maciej',
  PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
  PWD: '/Users/maciej',
  EDITOR: 'vim',
  SHLVL: '1',
  HOME: '/Users/maciej',
  LOGNAME: 'maciej',
  _: '/usr/local/bin/node'
}

It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other Worker threads. In other words, the following example would not work:

node -e 'process.env.foo = "bar"' &#x26;#x26;&#x26;#x26; echo $foo

While the following will:

import { env } from 'node:process';

env.foo = 'bar';
console.log(env.foo);

Assigning a property on process.env will implicitly convert the value to a string. This behavior is deprecated. Future versions of Node.js may throw an error when the value is not a string, number, or boolean.

import { env } from 'node:process';

env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'

Use delete to delete a property from process.env.

import { env } from 'node:process';

env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined

On Windows operating systems, environment variables are case-insensitive.

import { env } from 'node:process';

env.TEST = 1;
console.log(env.test);
// => 1

Unless explicitly specified when creating a Worker instance, each Worker thread has its own copy of process.env, based on its parent thread’s process.env, or whatever was specified as the env option to the Worker constructor. Changes to process.env will not be visible across Worker threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of process.env on a Worker instance operates in a case-sensitive manner unlike the main thread.

@sincev0.1.27
env
.string | undefinedVITEST
? { ResolveOptions.conditions?: string[] | undefinedconditions: ['browser'] } : var undefinedundefined });

之后,您可以创建一个测试文件,在其中导入要测试的组件,以编程方式与其交互并编写关于结果的期望

component.test
import { function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
, function mount<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, Exports, any>, options: MountOptions<Props>): Exports

Mounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true) of the component. Transitions will play during the initial render unless the intro option is set to false.

mount
, function unmount(component: Record<string, any>): void

Unmounts a component that was previously mounted using mount or hydrate.

unmount
} from 'svelte';
import { const expect: ExpectStaticexpect, const test: TestAPI

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
} from 'vitest';
import
type Component = SvelteComponent<Record<string, any>, any, any>
const Component: LegacyComponentType
Component
from './Component.svelte';
test<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number | TestOptions): void (+2 overloads)

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
('Component', () => {
// Instantiate the component using Svelte's `mount` API const
const component: {
    $on?(type: string, callback: (e: any) => void): () => void;
    $set?(props: Partial<Record<string, any>>): void;
} & Record<string, any>
component
=
mount<Record<string, any>, {
    $on?(type: string, callback: (e: any) => void): () => void;
    $set?(props: Partial<Record<string, any>>): void;
} & Record<...>>(component: ComponentType<...> | Component<...>, options: MountOptions<...>): {
    ...;
} & Record<...>

Mounts a component to the given target and returns the exports and potentially the props (if compiled with accessors: true) of the component. Transitions will play during the initial render unless the intro option is set to false.

mount
(const Component: LegacyComponentTypeComponent, {
target: Document | Element | ShadowRoot

Target element where the component will be mounted.

target
: var document: Documentdocument.Document.body: HTMLElement

Specifies the beginning and end of the document body.

MDN Reference

body
, // `document` exists because of jsdom
props?: Record<string, any> | undefined

Component properties.

props
: { initial: numberinitial: 0 }
}); expect<string>(actual: string, message?: string): Assertion<string> (+1 overload)expect(var document: Documentdocument.Document.body: HTMLElement

Specifies the beginning and end of the document body.

MDN Reference

body
.InnerHTML.innerHTML: stringinnerHTML).JestAssertion<string>.toBe: <string>(expected: string) => voidtoBe('<button>0</button>');
// Click the button, then flush the changes so you can synchronously write expectations var document: Documentdocument.Document.body: HTMLElement

Specifies the beginning and end of the document body.

MDN Reference

body
.ParentNode.querySelector<"button">(selectors: "button"): HTMLButtonElement | null (+4 overloads)

Returns the first element that is a descendant of node that matches selectors.

MDN Reference

querySelector
('button').HTMLElement.click(): voidclick();
function flushSync(fn?: (() => void) | undefined): void

Synchronously flushes any pending state changes and those that result from it.

flushSync
();
expect<string>(actual: string, message?: string): Assertion<string> (+1 overload)expect(var document: Documentdocument.Document.body: HTMLElement

Specifies the beginning and end of the document body.

MDN Reference

body
.InnerHTML.innerHTML: stringinnerHTML).JestAssertion<string>.toBe: <string>(expected: string) => voidtoBe('<button>1</button>');
// Remove the component from the DOM function unmount(component: Record<string, any>): void

Unmounts a component that was previously mounted using mount or hydrate.

unmount
(
const component: {
    $on?(type: string, callback: (e: any) => void): () => void;
    $set?(props: Partial<Record<string, any>>): void;
} & Record<string, any>
component
);
});

虽然该过程非常简单,但也属于低级且有些脆弱,因为组件的精确结构可能会频繁更改。像 @testing-library/svelte 这样的工具可以帮助简化您的测试。上面的测试可以改写如下

component.test
import { function render<C extends unknown, Q extends Queries = typeof import("/vercel/path0/node_modules/.pnpm/@[email protected]/node_modules/@testing-library/dom/types/queries")>(Component: ComponentType<...>, options?: SvelteComponentOptions<C>, renderOptions?: RenderOptions<Q>): RenderResult<C, Q>

Render a component into the document.

@template{import('./component-types.js').Component} C
@template{import('@testing-library/dom').Queries} [Q=typeof import('@testing-library/dom').queries]
@paramComponent - The component to render.
@paramoptions - Customize how Svelte renders the component.
@paramrenderOptions - Customize how Testing Library sets up the document and binds queries.
@returnsThe rendered component and bound testing functions.
render
, const screen: Screen<typeof import("/vercel/path0/node_modules/.pnpm/@[email protected]/node_modules/@testing-library/dom/types/queries")>screen } from '@testing-library/svelte';
import
const userEvent: {
    readonly setup: typeof setupMain;
    readonly clear: typeof clear;
    readonly click: typeof click;
    readonly copy: typeof copy;
    ... 12 more ...;
    readonly tab: typeof tab;
}
userEvent
from '@testing-library/user-event';
import { const expect: ExpectStaticexpect, const test: TestAPI

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
} from 'vitest';
import
type Component = SvelteComponent<Record<string, any>, any, any>
const Component: LegacyComponentType
Component
from './Component.svelte';
test<object>(name: string | Function, fn?: TestFunction<object> | undefined, options?: number | TestOptions): void (+2 overloads)

Defines a test case with a given name and test function. The test function can optionally be configured with test options.

@paramname - The name of the test or a function that will be used as a test name.
@paramoptionsOrFn - Optional. The test options or the test function if no explicit name is provided.
@paramoptionsOrTest - Optional. The test function or options, depending on the previous parameters.
@throwsError If called inside another test function.
@examplets // Define a simple test test('should add two numbers', () => { expect(add(1, 2)).toBe(3); });
@examplets // Define a test with options test('should subtract two numbers', { retry: 3 }, () => { expect(subtract(5, 2)).toBe(3); });
test
('Component', async () => {
const const user: UserEventuser =
const userEvent: {
    readonly setup: typeof setupMain;
    readonly clear: typeof clear;
    readonly click: typeof click;
    readonly copy: typeof copy;
    ... 12 more ...;
    readonly tab: typeof tab;
}
userEvent
.setup: (options?: Options) => UserEvent

Start a “session” with userEvent. All APIs returned by this function share an input device state and a default configuration.

setup
();
render<SvelteComponent<Record<string, any>, any, any>, typeof import("/vercel/path0/node_modules/.pnpm/@[email protected]/node_modules/@testing-library/dom/types/queries")>(Component: ComponentType<...>, options?: SvelteComponentOptions<...> | undefined, renderOptions?: RenderOptions<...> | undefined): RenderResult<...>

Render a component into the document.

@template{import('./component-types.js').Component} C
@template{import('@testing-library/dom').Queries} [Q=typeof import('@testing-library/dom').queries]
@paramComponent - The component to render.
@paramoptions - Customize how Svelte renders the component.
@paramrenderOptions - Customize how Testing Library sets up the document and binds queries.
@returnsThe rendered component and bound testing functions.
render
(const Component: LegacyComponentTypeComponent);
const const button: HTMLElementbutton = const screen: Screen<typeof import("/vercel/path0/node_modules/.pnpm/@[email protected]/node_modules/@testing-library/dom/types/queries")>screen.getByRole<HTMLElement>(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement (+1 overload)getByRole('button'); expect<HTMLElement>(actual: HTMLElement, message?: string): Assertion<HTMLElement> (+1 overload)expect(const button: HTMLElementbutton).toHaveTextContent(0); await const user: UserEventuser.click: (element: Element) => Promise<void>click(const button: HTMLElementbutton); expect<HTMLElement>(actual: HTMLElement, message?: string): Assertion<HTMLElement> (+1 overload)expect(const button: HTMLElementbutton).toHaveTextContent(1); });

在编写涉及双向绑定、上下文或代码片段 prop 的组件测试时,最好为您的特定测试创建一个包装器组件并与其交互。@testing-library/svelte 包含一些 示例

使用 Playwright 进行端到端测试

端到端 (E2E) 测试允许您通过用户的视角测试整个应用程序。本节使用 Playwright 作为示例,但您也可以使用其他解决方案,如 CypressNightwatchJS

要开始使用 Playwright,可以通过 VS Code 扩展 安装它,或使用 npm init playwright 从命令行安装它。当您运行 npx sv create 时,它也是设置 CLI 的一部分。

完成后,您应该有一个 tests 文件夹和一个 Playwright 配置文件。您可能需要调整该配置以告诉 Playwright 在运行测试之前该做什么——主要是以特定端口启动您的应用程序

playwright.config
const 
const config: {
    webServer: {
        command: string;
        port: number;
    };
    testDir: string;
    testMatch: RegExp;
}
config
= {
webServer: {
    command: string;
    port: number;
}
webServer
: {
command: stringcommand: 'npm run build && npm run preview', port: numberport: 4173 }, testDir: stringtestDir: 'tests', testMatch: RegExptestMatch: /(.+\.)?(test|spec)\.[jt]s/ }; export default
const config: {
    webServer: {
        command: string;
        port: number;
    };
    testDir: string;
    testMatch: RegExp;
}
config
;

您现在可以开始编写测试了。这些测试完全不知道 Svelte 作为框架,因此您主要与 DOM 交互并编写断言。

tests/hello-world.spec
import { import expectexpect, import testtest } from '@playwright/test';

import testtest('home page has expected h1', async ({ page }) => {
	await page: anypage.goto('/');
	await import expectexpect(page: anypage.locator('h1')).toBeVisible();
});

在 GitHub 上编辑此页面

上一页 下一页