vitekube

Quick start

From an empty project to a test running on a real cluster.

You need Docker, k3d, kubectl, Vitest 5 and Node ^22.18, ^24.11 or >=26.

npm install --save-dev vitest vitekube

1. Name your cluster

In k8s.setup.ts:

import { defineK8sGlobalSetup } from "vitekube/node";

export default defineK8sGlobalSetup({
  cluster: { provider: "k3d", name: "my-app-tests", reuse: true },
});

2. Connect it to Vitest

In vitest.config.ts:

import { K8sReporter } from "vitekube/node";
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    globalSetup: ["./k8s.setup.ts"],
    reporters: [new K8sReporter()],
    fileParallelism: false,
    testTimeout: 180_000,
    teardownTimeout: 120_000,
  },
});

K8sReporter is Vitest's default output with readable times, 38.2s rather than 38196ms, and it fails a run whose -t filter matches nothing.

3. Add your YAML

In web.yaml:

apiVersion: apps/v1
kind: Deployment
metadata: { name: web }
spec:
  selector: { matchLabels: { app: web } }
  template:
    metadata: { labels: { app: web } }
    spec:
      containers:
        - { name: web, image: nginx:1.29-alpine }
---
apiVersion: v1
kind: Service
metadata: { name: web }
spec:
  selector: { app: web }
  ports: [{ port: 80 }]

4. Write a test

In web.test.ts:

import { test } from "vitekube";
import { expect } from "vitest";

test("the web server answers", async ({ k8s, namespace }) => {
  await k8s.applyFile(namespace, "web.yaml");
  await k8s.waitForRollout({ namespace, name: "web" }, { timeoutMs: 120_000 });

  await using web = await k8s.portForward({
    namespace,
    resource: "service/web",
    remotePort: 80,
  });

  expect((await fetch(web.url)).status).toBe(200);
});

5. Run it

npx vitest run

The first run creates the cluster. With reuse: true, every run after that starts straight away. Leave out reuse to get a new cluster each run, deleted at the end.

Each run also prints a command for watching the cluster live in k9s.