---
title: "「not a constructor」エラー解決 - Vitestでクラスベースライブラリをモックする方法"
created: "2026-01-01"
description: "Vitestで「not a constructor」エラーが出た時の解決方法を解説。JSZipなどクラスベースライブラリを`new`キーワードで正しくモックする方法をコード例と共に紹介。テストが失敗する原因と修正方法がわかります。"
status: "published"
platforms:
wordpress:
published: true
url: "https://kurokawa.dev/testing/vitest-jszip-mock-class-based-library"
published_date: "2026-01-26"
category: "testing/vitest"
tags: ["Vitest", "クラスモック", "JSZip", "not a constructor", "単体テスト", "TypeScript", "vi.mock"]
---
## Vitestでクラスベースライブラリのモックに躓いた背景
画像ダウンロード機能のユニットテストを作成中に、JSZipライブラリのモックでテストが失敗する問題に遭遇しました。
正直なところ、このエラー原因の特定に予想以上に時間がかかりましたが、解決できたおかげでテスト実行が安定して通りやすくなりました。
本記事では、実際の開発現場で遭遇した「not a constructor」エラーの具体的な解決策をコード例と共に解説します。
### こんな人におすすめ
- Vitestでクラスベースライブラリをモックしたい方
- 「not a constructor」エラーで困っている開発者
- JSZipのテストを書いているエンジニア
- `new` キーワードで呼び出すライブラリのモック方法を知りたい方
## 目次
- [Vitestでクラスベースライブラリのモックに躓いた背景](#vitestでクラスベースライブラリのモックに躓いた背景)
- [「not a constructor」エラーの症状](#not-a-constructorエラーの症状)
- [Vitestでクラスベースライブラリのモックが失敗する原因](#vitestでクラスベースライブラリのモックが失敗する原因)
- [【解決策】Vitestでクラスを正しくモックする方法](#解決策vitestでクラスを正しくモックする方法)
- [モック方法の選択ガイド](#モック方法の選択ガイド)
- [クラスベースのモック導入で得られた効果](#クラスベースのモック導入で得られた効果)
- [Vitestでクラスモックする際のポイントまとめ](#vitestでクラスモックする際のポイントまとめ)
- [確認手順](#確認手順)
## 「not a constructor」エラーの症状
テスト実行時に、JSZipのインスタンス化でエラーが発生しました。
```
TypeError: () => mockZip is not a constructor
```
このエラーメッセージからは、モックの定義に問題があることが読み取れませんでした。
実際にデバッグに時間を要しましたが、原因はモックの定義方法にありました。
## Vitestでクラスベースライブラリのモックが失敗する原因
JSZipは `new JSZip()` でインスタンス化するクラスベースのライブラリです。
Vitestで `vi.fn(() => mockZip)` を使うと、関数として呼び出されることを期待してしまい、`new` キーワードでのコンストラクタ呼び出しに対応できません。
### NG: 関数ベースのモック(失敗例)
```typescript
vi.mock('jszip', () => {
const mockZip = {
file: vi.fn(),
generateAsync: vi.fn().mockResolvedValue(new Blob(['mock'])),
};
return { default: vi.fn(() => mockZip) };
});
```
このモック定義では、`vi.fn()` で作成された関数を返しているため、`new` キーワードでの呼び出し時に「not a constructor」エラーが発生します。
**なぜ失敗するのか**: `vi.fn()` は関数オブジェクトを作成しますが、これは `new` キーワードでコンストラクタとして呼び出すことはできません。
以下のシーケンス図で、関数ベースのモックが失敗する流れを確認できます。
```mermaid
sequenceDiagram
participant Test as テストコード
participant Vitest as vi.mock()
participant Mock as vi.fn()(関数)
participant Error as TypeError
Test->>Vitest: vi.mock('jszip', ...) を定義
Vitest->>Mock: vi.fn(() => mockZip) を返す
Note over Mock: 関数オブジェクト(コンストラクタではない)
Test->>Mock: new JSZip() を呼び出し
Mock-->>Error: ❌ "not a constructor"
Note over Error: new キーワードで<br/>関数をインスタンス化できない
```
## 【解決策】Vitestでクラスを正しくモックする方法
モックを**クラス定義**に変更します。
### OK: クラスベースのモック(正解例)
```typescript
vi.mock('jszip', () => {
return {
default: class MockJSZip {
file = vi.fn();
generateAsync = vi.fn().mockResolvedValue(new Blob(['mock zip content']));
},
};
});
```
このモック定義では、クラス構文を使ってモックを定義しているため、`new` キーワードでの呼び出しに正しく対応できます。
**ポイント**:
- `class` 構文を使ってモックを定義する
- クラスプロパティとして `vi.fn()` を設定する
- `default` エクスポートにクラスを返す
以下のシーケンス図で、クラスベースのモックが成功する流れを確認できます。
```mermaid
sequenceDiagram
participant Test as テストコード
participant Vitest as vi.mock()
participant Mock as class MockJSZip
participant Methods as モックメソッド
Test->>Vitest: vi.mock('jszip', ...) を定義
Vitest->>Mock: class MockJSZip を返す
Note over Mock: クラス定義(コンストラクタとして機能)
Test->>Mock: new JSZip() を呼び出し
Mock-->>Test: ✅ インスタンスを返す
Test->>Methods: zip.file() を呼び出し
Methods-->>Test: モックされた振る舞いを返す
Test->>Methods: zip.generateAsync() を呼び出し
Methods-->>Test: mockResolvedValue を返す
Note over Methods: クラスプロパティとして<br/>vi.fn() が設定されている
```
### Vitestクラスモックの基本パターン
クラスベースのライブラリをモックする際の基本パターンは以下の通りです。
```typescript
vi.mock('ライブラリ名', () => ({
default: class MockClassName {
メソッド名 = vi.fn();
プロパティ名 = 値;
},
}));
```
このパターンはJSZip以外にも、以下のようなクラスベースライブラリで活用できます。
- ファイル操作系ライブラリ(FileSaver.jsなど)
- 画像処理系ライブラリ
- APIクライアント系ライブラリ
### モック方法の選択ガイド
ライブラリの特性に応じて、適切なモック方法を選択するフローチャートです。
```mermaid
flowchart TD
A[ライブラリをモックする] --> B{new キーワードで<br/>呼び出すか?}
B -->|はい| C[クラスベースのモック]
B -->|いいえ| D[関数ベースのモック]
C --> E{default exportか?}
E -->|はい| F["return: default = class MockClass"]
E -->|いいえ| G["return: ClassName = class MockClass"]
D --> H{default exportか?}
H -->|はい| I["vi.fn または<br/>vi.fnでreturnValueを返す"]
H -->|いいえ| J["methodName: vi.fnを設定"]
F --> K[✅ new ライブラリ名 で呼び出し可能]
G --> K
I --> L[✅ 通常の関数呼び出しで使用]
J --> L
style C fill:#90EE90
style D fill:#87CEEB
style K fill:#FFD700
style L fill:#FFD700
```
**使い方**:
1. ライブラリのドキュメントで `new` キーワードの使用を確認
2. 上記フローチャートに従ってモック方法を選択
3. 適切な `vi.mock()` パターンを適用
## クラスベースのモック導入で得られた効果
クラスベースのモックに変更後、以下の効果が得られました。
- テスト実行が安定して通りやすくなりました
- テストコードが実際のJSZipの使用方法に近くなり、可読性が向上しました
- 他のクラスベースライブラリ(例:ファイル操作系ライブラリ)のモックにも同様のパターンを適用できるようになりました
正しいモック方法のおかげで、テスト実行時間が短縮され、デバッグ効率が向上しました。
## Vitestでクラスモックする際のポイントまとめ
クラスベースライブラリをモックする際は、以下の点に注意が必要です。
- **モックもクラスとして定義する必要があります** - `vi.fn()` ではなく `class` 構文を使用
- **`new` キーワードで呼び出されるライブラリは、関数ではなくクラス構文でモックを書きます**
- **VitestのES Module モックでは `default` エクスポートに注意します** - 名前付きエクスポートの場合は適切に調整
### 関連するVitestのトラブルシューティング
Vitestでのテスト中に他の問題が発生した場合は、以下の記事も参考にしてください。
- [Vitest fake timersとReact Testing Library waitForの相性問題と解決策](https://wakatchi.dev/vitest-fake-timers-react-testing-library-pitfalls/) - `waitFor` がタイムアウトする問題の解決方法
## 確認手順
1. `npm run test -- tests/utils/downloadImage.test.ts` でテストを実行します
2. 全テストがパスすることを確認します
Vitestでクラスベースライブラリのモックに躓いた背景
画像ダウンロード機能のユニットテストを作成中に、JSZipライブラリのモックでテストが失敗する問題に遭遇しました。
正直なところ、このエラー原因の特定に予想以上に時間がかかりましたが、解決できたおかげでテスト実行が安定して通りやすくなりました。
本記事では、実際の開発現場で遭遇した「not a constructor」エラーの具体的な解決策をコード例と共に解説します。
こんな人におすすめ
- Vitestでクラスベースライブラリをモックしたい方
- 「not a constructor」エラーで困っている開発者
- JSZipのテストを書いているエンジニア
new キーワードで呼び出すライブラリのモック方法を知りたい方
目次
「not a constructor」エラーの症状
テスト実行時に、JSZipのインスタンス化でエラーが発生しました。
TypeError: () => mockZip is not a constructor
このエラーメッセージからは、モックの定義に問題があることが読み取れませんでした。
実際にデバッグに時間を要しましたが、原因はモックの定義方法にありました。
Vitestでクラスベースライブラリのモックが失敗する原因
JSZipは new JSZip() でインスタンス化するクラスベースのライブラリです。
Vitestで vi.fn(() => mockZip) を使うと、関数として呼び出されることを期待してしまい、new キーワードでのコンストラクタ呼び出しに対応できません。
NG: 関数ベースのモック(失敗例)
vi.mock('jszip', () => {
const mockZip = {
file: vi.fn(),
generateAsync: vi.fn().mockResolvedValue(new Blob(['mock'])),
};
return { default: vi.fn(() => mockZip) };
});
このモック定義では、vi.fn() で作成された関数を返しているため、new キーワードでの呼び出し時に「not a constructor」エラーが発生します。
なぜ失敗するのか: vi.fn() は関数オブジェクトを作成しますが、これは new キーワードでコンストラクタとして呼び出すことはできません。
以下のシーケンス図で、関数ベースのモックが失敗する流れを確認できます。
sequenceDiagram
participant Test as テストコード
participant Vitest as vi.mock()
participant Mock as vi.fn()(関数)
participant Error as TypeError
Test->>Vitest: vi.mock('jszip', ...) を定義
Vitest->>Mock: vi.fn(() => mockZip) を返す
Note over Mock: 関数オブジェクト(コンストラクタではない)
Test->>Mock: new JSZip() を呼び出し
Mock-->>Error: ❌ "not a constructor"
Note over Error: new キーワードで<br/>関数をインスタンス化できない
【解決策】Vitestでクラスを正しくモックする方法
モックをクラス定義に変更します。
OK: クラスベースのモック(正解例)
vi.mock('jszip', () => {
return {
default: class MockJSZip {
file = vi.fn();
generateAsync = vi.fn().mockResolvedValue(new Blob(['mock zip content']));
},
};
});
このモック定義では、クラス構文を使ってモックを定義しているため、new キーワードでの呼び出しに正しく対応できます。
ポイント:
class 構文を使ってモックを定義する
- クラスプロパティとして
vi.fn() を設定する
default エクスポートにクラスを返す
以下のシーケンス図で、クラスベースのモックが成功する流れを確認できます。
sequenceDiagram
participant Test as テストコード
participant Vitest as vi.mock()
participant Mock as class MockJSZip
participant Methods as モックメソッド
Test->>Vitest: vi.mock('jszip', ...) を定義
Vitest->>Mock: class MockJSZip を返す
Note over Mock: クラス定義(コンストラクタとして機能)
Test->>Mock: new JSZip() を呼び出し
Mock-->>Test: ✅ インスタンスを返す
Test->>Methods: zip.file() を呼び出し
Methods-->>Test: モックされた振る舞いを返す
Test->>Methods: zip.generateAsync() を呼び出し
Methods-->>Test: mockResolvedValue を返す
Note over Methods: クラスプロパティとして<br/>vi.fn() が設定されている
Vitestクラスモックの基本パターン
クラスベースのライブラリをモックする際の基本パターンは以下の通りです。
vi.mock('ライブラリ名', () => ({
default: class MockClassName {
メソッド名 = vi.fn();
プロパティ名 = 値;
},
}));
このパターンはJSZip以外にも、以下のようなクラスベースライブラリで活用できます。
- ファイル操作系ライブラリ(FileSaver.jsなど)
- 画像処理系ライブラリ
- APIクライアント系ライブラリ
モック方法の選択ガイド
ライブラリの特性に応じて、適切なモック方法を選択するフローチャートです。
flowchart TD
A[ライブラリをモックする] --> B{new キーワードで<br/>呼び出すか?}
B -->|はい| C[クラスベースのモック]
B -->|いいえ| D[関数ベースのモック]
C --> E{default exportか?}
E -->|はい| F["return: default = class MockClass"]
E -->|いいえ| G["return: ClassName = class MockClass"]
D --> H{default exportか?}
H -->|はい| I["vi.fn または<br/>vi.fnでreturnValueを返す"]
H -->|いいえ| J["methodName: vi.fnを設定"]
F --> K[✅ new ライブラリ名 で呼び出し可能]
G --> K
I --> L[✅ 通常の関数呼び出しで使用]
J --> L
style C fill:#90EE90
style D fill:#87CEEB
style K fill:#FFD700
style L fill:#FFD700
使い方:
- ライブラリのドキュメントで
new キーワードの使用を確認
- 上記フローチャートに従ってモック方法を選択
- 適切な
vi.mock() パターンを適用
クラスベースのモック導入で得られた効果
クラスベースのモックに変更後、以下の効果が得られました。
- テスト実行が安定して通りやすくなりました
- テストコードが実際のJSZipの使用方法に近くなり、可読性が向上しました
- 他のクラスベースライブラリ(例:ファイル操作系ライブラリ)のモックにも同様のパターンを適用できるようになりました
正しいモック方法のおかげで、テスト実行時間が短縮され、デバッグ効率が向上しました。
Vitestでクラスモックする際のポイントまとめ
クラスベースライブラリをモックする際は、以下の点に注意が必要です。
- モックもクラスとして定義する必要があります -
vi.fn() ではなく class 構文を使用
new キーワードで呼び出されるライブラリは、関数ではなくクラス構文でモックを書きます
- VitestのES Module モックでは
default エクスポートに注意します - 名前付きエクスポートの場合は適切に調整
関連するVitestのトラブルシューティング
Vitestでのテスト中に他の問題が発生した場合は、以下の記事も参考にしてください。
確認手順
npm run test -- tests/utils/downloadImage.test.ts でテストを実行します
- 全テストがパスすることを確認します