Blog

Wie löst man den Absturz von Canvas in Vitest mit Threads und jsdom

Frank van Wijk

Aktualisiert Oktober 16, 2025
3 Minuten

Vitest ist der neue Jest-Killer. Es ist schnell, dank der Dinge, die auch Vite antreiben, wie esbuild.

Der Hauptgrund, warum ich Vitest bevorzuge, ist, dass es Ihre Vite-Konfiguration wiederverwenden kann (React oder Vue ohne Vite ist ein No-Go, wenn Sie mich fragen), aber darüber hinaus sind wir endlich von diesen altmodischen langsamen Webpack-Builds erlöst, die auf CommonJS abzielen. Sie schreiben ESM (natürlich mit Typescript) und Vitest führt ESM aus, ohne ⚡️ zu transformieren.

Sowohl Jest als auch Vite führen Tests parallel mit Worker-Threads aus. Nach der Migration von Jest zu Vitest habe ich jedoch diesen Fehler erhalten

Module did not self-register: '~/my-project/node_modules/canvas/build/Release/canvas.node'

This error seems to be related to jsdom, the Node library I use to simulate the browser environment.
If you have the canvas package installed, jsdom automatically loads it, so that the Canvas API is also available in your test. However, there is an issue in this package so that it could not run inside a worker thread. canvas is a peer dependency of jsdom, so when you use npm >=7, this will always be installed in your project. I have no clue (yet) why this error doesn't happen in Jest, but you can 'fix' it by disabling threads.
Vitest-Logo

Beheben Sie den Canvas-Fehler durch Deaktivieren von Threads

Beachten Sie, dass Sie, wenn Threads deaktiviert sind, selbst im Skript test-setup.ts aufräumen müssen.

export default defineConfig({
  test: {
    environment: 'jsdom', // assume you use jsdom
    setupFiles: 'test-setup.ts',
    threads: false, // disable worker threads so that canvas runs in main thread
  }
});
import { cleanup } from '@testing-library/vue'; // Or whatever framework you use
import { afterEach } from 'vitest';

afterEach(cleanup);

Wenn Sie WallabyJS verwenden, müssen Sie auch die Worker auf 1 setzen, sonst sind die Tests fehlerhaft.

module.exports = () => ({
  workers: {
    initial: 1,
    regular: 1
  }
});

Alternative Lösung: keine Leinwand

Die Verwendung von Threads ist ein großer Vorteil. Das ist einer der Gründe, warum Vite so schnell ist. Gibt es also eine Möglichkeit, Threads trotzdem zu verwenden?

Nun, wenn Sie das Paket canvas nicht verwenden, können wir es vielleicht loswerden, damit jsdom es nicht lädt.

Ihre erste Option ist, jsdom nicht zu verwenden. Versuchen Sie zum Beispiel happy-dom.

Zweitens könnten Sie rm -rf node_modules/canvas als postinstall Skript oder kurz vor der Ausführung von Tests ausführen. Oder Sie lassen immer laufen, so dass das Verhalten von vor 7, keine Peer Deps automatisch zu installieren, ausgelöst wird. Ich weiß, das ist schmutzig (und fehleranfällig), aber es funktioniert.

Irgendwie habe ich es geschafft, alle Verweise auf canvas aus package-lock.json zu entfernen, so dass ich nicht installiert werde, wenn Sie das nächste Mal npm install (oder eines Ihrer Teammitglieder) ausführen.

Erkundigen Sie sich bei npm ls canvas, ob Canvas nicht mehr in Ihrem package-lock.json zu finden ist.

Fazit

Vitest ist großartig, aber wenn Sie Threads wünschen (ja!) und jsdom verwenden, um browserähnliche Tests in Node auszuführen, kann es passieren, dass Sie auf den unangenehmen Fehler "Module did not self-register" stoßen, der durch das canvas Paket ausgelöst wird, das nicht in einem Worker-Thread laufen kann.

Wenn Sie nicht wirklich auf das Paket canvas angewiesen sind, müssen Sie es aus package-lock.json entfernen, damit es nicht in node_modules installiert wird (über jsdom's peer deps).

Wenn dies nicht möglich ist, müssen Sie die Tests im Single-Thread-Modus ausführen, indem Sie in der Vitest-Konfigurationsdatei threads: 1 einstellen.

Verfasst von

Frank van Wijk

Frank is a senior frontend consultant focusing on quality and maintainability.

Contact

Let’s discuss how we can support your journey.