INTEGRITY Документация

Напишите свой первый тест

В этом руководстве объясняется, как начать работу с @cloudflare/vitest-plugin пакет. Более сложные примеры тестирования с @cloudflare/vitest-plugin, см. Рецепты.

Предварительные требования

Сначала убедитесь, что:

Настройка конфигурации Vitest

В вашем vitest.config.ts файле используйте cloudflareTest() плагин для настройки интеграции Workers с Vitest.

Конфигурацию Worker можно использовать из Файл конфигурации Wrangler указав это с помощью wrangler.configPath.

import { cloudflareTest } from "@cloudflare/vitest-plugin";
import { defineConfig } from "vitest/config";

export default defineConfig({
	plugins: [
		cloudflareTest({
			wrangler: { configPath: "./wrangler.jsonc" },
		}),
	],
});

Также можно переопределить или задать дополнительную конфигурацию с помощью miniflare ключ. Он имеет приоритет над значениями, заданными в конфигурации Wrangler.

Например, эта конфигурация добавит KV namespace TEST_NAMESPACE к которому обращались и который изменяли только в тестах.

export default defineConfig({
	plugins: [
		cloudflareTest({
			wrangler: { configPath: "./wrangler.jsonc" },
			miniflare: {
				kvNamespaces: ["TEST_NAMESPACE"],
			},
		}),
	],
});

Полный список доступных параметров Miniflare см. в Miniflare WorkersOptions документация по API.

Полный список доступных параметров конфигурации см. в Конфигурация.

Определение типов

Если вы не используете TypeScript, этот раздел можно пропустить.

Сначала убедитесь, что вы выполнили wrangler types, который генерирует типы для среды выполнения Cloudflare Workers и Env тип на основе привязок вашего Worker.

Затем добавьте tsconfig.json в папке тестов и добавьте "@cloudflare/vitest-plugin" в массив типов, чтобы определить типы для cloudflare:test. Также следует добавить вывод wrangler types к include массив, чтобы были доступны типы среды выполнения Cloudflare Workers.

Пример файла test/tsconfig.json

test/tsconfig.json
{
	"extends": "../tsconfig.json",
	"compilerOptions": {
		"moduleResolution": "bundler",
		"types": [
			"@cloudflare/vitest-plugin/types", // provides `cloudflare:test` and `cloudflare:workers` types
		],
	},
	"include": [
		"./**/*.ts",
		"../src/worker-configuration.d.ts", // output of `wrangler types`
	],
}

Написание тестов

В качестве примера мы используем простой Worker. Он возвращает ответ 404 для /404 путь и "Hello World!" для всех остальных путей.

src/index.js
export default {
	async fetch(request, env, ctx) {
		if (pathname === "/404") {
			return new Response("Not found", { status: 404 });
		}
		return new Response("Hello World!");
	},
};
src/index.ts
export default {
	async fetch(request, env, ctx): Promise<Response> {
		if (pathname === "/404") {
			return new Response("Not found", { status: 404 });
		}
		return new Response("Hello World!");
	},
} satisfies ExportedHandler<Env>;

Модульные тесты

Импортировав Worker, можно написать модульный тест для его fetch обработчик.

test/unit.spec.js
import { env } from "cloudflare:workers";
import {
	createExecutionContext,
	waitOnExecutionContext,
} from "cloudflare:test";
import { describe, it, expect } from "vitest";
// Import your worker so you can unit test it
import worker from "../src";

// For now, you'll need to do something like this to get a correctly-typed
// `Request` to pass to `worker.fetch()`.
const IncomingRequest = Request;

describe("Hello World worker", () => {
	it("responds with Hello World!", async () => {
		const request = new IncomingRequest("http://example.com/404");
		// Create an empty context to pass to `worker.fetch()`
		const ctx = createExecutionContext();
		const response = await worker.fetch(request, env, ctx);
		// Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions
		await waitOnExecutionContext(ctx);
		expect(response.status).toBe(404);
		expect(await response.text()).toBe("Not found");
	});
});
test/unit.spec.ts
import { env } from "cloudflare:workers";
import {
	createExecutionContext,
	waitOnExecutionContext,
} from "cloudflare:test";
import { describe, it, expect } from "vitest";
// Import your worker so you can unit test it
import worker from "../src";

// For now, you'll need to do something like this to get a correctly-typed
// `Request` to pass to `worker.fetch()`.
const IncomingRequest = Request<unknown, IncomingRequestCfProperties>;

describe("Hello World worker", () => {
	it("responds with Hello World!", async () => {
		const request = new IncomingRequest("http://example.com/404");
		// Create an empty context to pass to `worker.fetch()`
		const ctx = createExecutionContext();
		const response = await worker.fetch(request, env, ctx);
		// Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions
		await waitOnExecutionContext(ctx);
		expect(response.status).toBe(404);
		expect(await response.text()).toBe("Not found");
	});
});

Интеграционные тесты

Вы можете использовать exports объект, предоставленный cloudflare:workers для написания интеграционного теста. exports.default.fetch() вызывает обработчик экспорта по умолчанию, определённый в основном Worker.

test/integration.spec.js
import { exports } from "cloudflare:workers";
import { describe, it, expect } from "vitest";

describe("Hello World worker", () => {
	it("responds with not found and proper status for /404", async () => {
		const response = await exports.default.fetch("http://example.com/404");
		expect(response.status).toBe(404);
		expect(await response.text()).toBe("Not found");
	});
});
test/integration.spec.ts
import { exports } from "cloudflare:workers";
import { describe, it, expect } from "vitest";

describe("Hello World worker", () => {
	it("responds with not found and proper status for /404", async () => {
		const response = await exports.default.fetch("http://example.com/404");
		expect(response.status).toBe(404);
		expect(await response.text()).toBe("Not found");
	});
});

При использовании exports.default.fetch() для интеграционных тестов код вашего Worker выполняется в том же контексте, что и исполнитель тестов. Это означает, что вы можете использовать глобальные моки для управления Worker, но также означает, что ваш Worker использует немного иное поведение разрешения модулей, предоставляемое Vite. Обычно это не проблема, но чтобы запустить Worker в чистом окружении, максимально приближенном к продакшену, можно использовать вспомогательный Worker. См. этот пример о том, как настроить интеграционные тесты с использованием вспомогательных Workers. Однако использование вспомогательных Workers связано с ограничения о котором следует знать.