Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Testing with TypeScript

Testing with TypeScript

~16 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

To wrap up the practical tools: automated tests for our library management system, with Vitest – the same test runner we already met in "React for Professionals" chapter 44, here with no framework binding.

Installing Vitest

npm install --save-dev vitest
package.json
{
  "scripts": {
    "start": "tsx src/index.ts",
    "build": "tsc",
    "typecheck": "tsc --noEmit",
    "test": "vitest"
  }
}

Vitest understands TypeScript NATIVELY, with no extra configuration – NO separate compilation step before testing needed, EXACTLY like tsx from chapter 2 for normal execution.

Testing the error classes from chapter 25

src/errors/LibraryError.test.ts
import { describe, it, expect } from 'vitest';
import { MediumNotFoundError, MediumNotAvailableError } from './LibraryError.js';

describe('MediumNotFoundError', () => {
  it('stores the ISBN and a meaningful message', () => {
    const error = new MediumNotFoundError('978-0-618-64015-7');

    expect(error.isbn).toBe('978-0-618-64015-7');
    expect(error.message).toContain('978-0-618-64015-7');
    expect(error).toBeInstanceOf(Error); // inheritance from chapter 25 works as expected
  });
});

Testing Repository<T> generically

src/repository/Repository.test.ts
import { describe, it, expect, beforeEach } from 'vitest';
import { Repository } from './Repository.js';
import { HasIsbn } from '../models/HasIsbn.js';

interface TestItem extends HasIsbn {
  name: string;
}

describe('Repository<T>', () => {
  let repository: Repository<TestItem>;

  beforeEach(() => {
    repository = new Repository<TestItem>();
  });

  it('starts empty', () => {
    expect(repository.count()).toBe(0);
  });

  it('adds items', () => {
    repository.add({ isbn: '123', name: 'Test' });
    expect(repository.count()).toBe(1);
  });

  it('finds by ISBN', () => {
    repository.add({ isbn: '123', name: 'Test' });
    const found = repository.findByIsbn('123');
    expect(found?.name).toBe('Test');
  });

  it('returns undefined for an unknown ISBN', () => {
    expect(repository.findByIsbn('unknown')).toBeUndefined();
  });
});

TestItem extends HasIsbn defines a MINIMAL test type satisfying the constraint from chapter 18, instead of having to create a full Book object just for the test – a common testing pattern: as LITTLE as possible, as MUCH as needed.

Testing asynchronous code (chapter 26)

import { describe, it, expect } from 'vitest';
import { AsyncBookRepository } from './AsyncBookRepository.js';
import { MediumNotFoundError } from '../errors/LibraryError.js';

describe('AsyncBookRepository', () => {
  it('throws MediumNotFoundError for an unknown ISBN', async () => {
    const repository = new AsyncBookRepository();

    await expect(repository.findByIsbn('unknown'))
      .rejects.toThrow(MediumNotFoundError);
  });
});

it('...', async () => {{...}}) – the test function itself is async, EXACTLY as learned in chapter 26. await expect(...).rejects.toThrow(...) is Vitest's dedicated pattern for checking that a PROMISE gets rejected with a SPECIFIC error type.

Bonus: type checking as part of CI

package.json
{
  "scripts": {
    "start": "tsx src/index.ts",
    "build": "tsc",
    "typecheck": "tsc --noEmit",
    "test": "vitest run",
    "verify": "npm run typecheck && npm run test"
  }
}

npm run verify combines chapter 2's typecheck with the actual tests in ONE command – a common "before commit/deploy" check covering BOTH error classes: TYPE errors (which tsc finds) AND LOGIC errors (which tests find). vitest run instead of just vitest exits after ONE pass, instead of staying in watch mode – important for CI environments.

Tipp: Rule of thumb: type checking AND tests are COMPLEMENTARY, not redundant – TypeScript catches "wrong type passed", tests catch "correct type, but wrong BEHAVIOR" (e.g. an off-by-one calculation). Both together give considerably more confidence than either tool alone.