Way back in the beginning, we got add
working, but let’s up our game. Let’s assume we’ve got a button that increments a counter when clicked. We want to test that the user sees the count increase.
We’ll need to adjust our setup a bit for the DOM:
<!-- index.html -->
<html lang="en">
<head>
<title>Counter</title>
</head>
<body>
<div id="app">
<p id="counter">0</p>
<button id="increment">Increment</button>
</div>
<script>
const counterElement = document.getElementById('counter');
const buttonElement = document.getElementById('increment');
let count = 0;
buttonElement.addEventListener('click', () => {
count += 1;
counterElement.textContent = count;
});
</script>
</body>
</html>
Here’s the test for that behavior:
import { describe, it, expect } from 'vitest';
describe('Counter app', () => {
it('should increment the count when the button is clicked', () => {
// Set up DOM
document.body.innerHTML = `
<p id="counter">0</p>
<button id="increment">Increment</button>
`;
const button = document.getElementById('increment');
const counter = document.getElementById('counter');
// Fake our state
let count = 0;
button.addEventListener('click', () => {
count += 1;
counter.textContent = count;
});
// Simulate the button click
button.click();
// Check if the behavior is what we expect
expect(counter.textContent).toBe('1');
});
});
Rather than testing the internal state directly, we’re checking the thing we care about most—what the user sees: that number goes up when the button is clicked. Now when some poor soul is maintaining your app in the future, they can immediately tell what’s going on—oh, button click equals counter change, cool. They don’t have to dig into how the inner state of count
works.
Mocking DOM Methods
Sometimes you need to mock up DOM methods or third-party services—stuff that gets a little trickier in tests. Let’s mock the getElementById
function to see how easy it is in Vitest:
import { describe, it, vi } from 'vitest';
describe('DOM tests', () => {
it('should call getElementById', () => {
const spy = vi.spyOn(global.document, 'getElementById').mockReturnValue({
textContent: '0',
});
const element = document.getElementById('counter');
expect(spy).toHaveBeenCalled();
expect(element.textContent).toBe('0');
spy.mockRestore();
});
});
You just mocked getElementById
and verified it was called. Vitest comes with a mocking library built-in, so you don’t need to yank in extra dependencies just for the basics like spies, mocks, and stubs.