Testing With Vitest in JavaScript
Testing With Vitest in JavaScript
~13 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
To wrap up the practical chapters, let's write automated tests for our budget app – the same testing library (Vitest) used by the TypeScript tutorial works identically with plain JavaScript.
Installing Vitest
npm install --save-dev vitest{
"name": "haushaltsbuch-app",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"start": "node src/index.js",
"test": "vitest run"
},
"devDependencies": {
"vitest": "^2.1.0"
}
}The first test
Vitest follows the usual convention: test files end in .test.js and usually live right next to the file being tested:
import { describe, it, expect } from 'vitest';
import { isValidCategory, DEFAULT_CATEGORIES } from './categories.js';
describe('isValidCategory', () => {
it('returns true for a known category', () => {
expect(isValidCategory('Rent')).toBe(true);
});
it('returns false for an unknown category', () => {
expect(isValidCategory('Unknown')).toBe(false);
});
it('contains exactly four default categories', () => {
expect(DEFAULT_CATEGORIES).toHaveLength(4);
});
});npm testdescribe() groups related tests, it() (also callable as test()) defines a SINGLE test case, expect() formulates the actual assertion.
Important matcher functions
toBe(value)– exact equality with===, suited for primitives.toEqual(object)– DEEP content comparison for objects/arrays (sincetoBewould almost always fail due to the reference comparison from chapter 12).toBeTruthy()/toBeFalsy()– the truthy/falsy check from chapter 5.toThrow()– checks whether a function throws an error.toHaveLength(n)– checks the.lengthproperty of arrays/strings.
import { describe, it, expect } from 'vitest';
function calculateBalance(transactions) {
return transactions.reduce((sum, t) => sum + t.amount, 0);
}
describe('calculateBalance', () => {
it('sums several transactions correctly', () => {
const transactions = [{ amount: 2400 }, { amount: -850 }, { amount: -60 }];
expect(calculateBalance(transactions)).toBe(1490);
});
it('returns 0 for an empty array', () => {
expect(calculateBalance([])).toBe(0);
});
});Testing error handling
import { describe, it, expect } from 'vitest';
import { InvalidTransactionError } from './errors.js';
function validateTransaction(transaction) {
if (!transaction.description) {
throw new InvalidTransactionError('Description is missing');
}
}
describe('validateTransaction', () => {
it('throws InvalidTransactionError when the description is missing', () => {
expect(() => validateTransaction({ amount: -18 })).toThrow(InvalidTransactionError);
});
});Achtung: expect(() => ...) requires a WRAPPER arrow function around the call when using toThrow(). expect(validateTransaction(...)).toThrow() would run the function IMMEDIATELY and throw the error uncontrolled, instead of letting Vitest catch it in a controlled way.
Testing asynchronous functions
import { describe, it, expect } from 'vitest';
import { loadTransactions } from './fileStorage.js';
import { FileNotFoundError } from './errors.js';
describe('loadTransactions', () => {
it('throws FileNotFoundError for a missing file', async () => {
await expect(loadTransactions('nonexistent.json')).rejects.toThrow(FileNotFoundError);
});
});The test function itself becomes async, and expect(...) is combined with await – .rejects checks that the returned promise gets REJECTED with the expected error, instead of fulfilled.