The transition to Manifest V3 is the biggest shift in Chrome's ecosystem in years. For developers and testers, it means new architecture, different extension lifecycles, and fresh challenges in automated testing.
What Changed
Instead of background pages (running continuously in the background), we now have service workers – lightweight scripts that:
- start in response to specific events (e.g., clicking the icon),
- go to sleep after finishing their work.
This forces a new way of thinking about extension state, data storage (chrome.storage instead of localStorage), and task scheduling (chrome.alarms instead of setInterval).
Why Testing Became Harder
1. Non-Deterministic Lifecycle
A service worker can start, execute a task, and go to sleep – or be killed mid-operation. The classic assumption "code runs from start to finish" no longer works. You need to test different paths: successful completion, interruption mid-way, restart with state restoration.
2. Asynchronicity at Every Step
chrome.storage, chrome.alarms, communication between scripts – everything is asynchronous. In tests, you need to handle Promises, async/await, and race conditions that didn't exist in synchronous environments.
3. Missing Full APIs in Unit Tests
Chrome APIs don't exist in Node.js environments. You need to mock the entire chrome.* namespace, and this doesn't always accurately reflect real browser behavior (e.g., chrome.alarms limits or chrome.storage behavior under load).
What Tests to Write
Unit Tests with Mocks
These check business logic in isolation. Mocks for chrome.storage and chrome.alarms let you quickly verify that code responds correctly to various inputs. They won't replace a real environment, but they catch logical errors early.
Integration / E2E Tests
Here you run the extension in a real browser (e.g., via Puppeteer or Playwright), simulating user interactions: icon clicks, opening tabs, waiting for alarms. This lets you see how the service worker handles the real lifecycle.
"Suspension & Resume" Tests
Critical for MV3. Write a test that:
- Starts the service worker,
- Saves state to chrome.storage,
- Simulates killing the worker (chrome.runtime.reload() or manual shutdown),
- Wakes the worker again,
- Verifies that state was correctly restored.
Tools That Help
- Puppeteer / Playwright – launch Chrome with the extension loaded, automate clicks, and verify results.
- sinon-chrome – ready-made mocks for chrome.* APIs for unit tests.
- Chrome DevTools (Service Worker inspector) – live view: when the worker wakes, when it sleeps, what alarms are active.
- Vitest / Jest – standard unit testing frameworks with module mocking capabilities.
Common Pitfalls
- Assuming continuous operation: Code can't assume the worker runs non-stop. Always design with potential suspension in mind.
- Synchronous storage operations: chrome.storage is asynchronous – you can't use it like a global variable.
- Too frequent alarms: chrome.alarms has a 1-minute minimum. If you need more frequent execution, find another solution (e.g., checking on wake-up).
Summary
Manifest V3 is a new era – it enforces asynchronicity, planning with suspension in mind, and testing service worker lifecycle. The keys to success are:
- Writing unit tests with mocks (fast feedback),
- Writing E2E tests in a real browser (verifying actual behavior),
- Testing "suspension & resume" (the trickiest scenario),
- Monitoring the extension in production (because real user behavior can surprise you).
If you want to learn more about our experience migrating to MV3, read the article on the official Chrome Developers Blog: eyeo's journey to testing Manifest V3 service worker suspension
