11# React Testing Framework Benchmark Report
22
3- > Generated: Tue, 31 Mar 2026 21:00 :50 GMT
3+ > Generated: Wed, 01 Apr 2026 12:41 :50 GMT
44
55## Environment
66
7- | Property | Value |
8- | ---| ---|
9- | Node.js | v22.5.1 |
10- | Platform | darwin 25.4.0 |
11- | CPU | Apple M3 Pro |
12- | CPU Cores | 12 |
13- | Total RAM | 18.0 GB |
14- | Runs/scenario | 7 (trim ±1) |
7+ | Property | Value |
8+ | ------------- | ------------- |
9+ | Node.js | v22.5.1 |
10+ | Platform | darwin 25.4.0 |
11+ | CPU | Apple M3 Pro |
12+ | CPU Cores | 12 |
13+ | Total RAM | 18.0 GB |
14+ | Runs/scenario | 7 (trim ±1) |
1515
1616## Scenarios
1717
1818Each scenario runs the ** same 9 React tests** across 5 test files:
1919
20- | Test File | Tests |
21- | ---| ---|
22- | 'counter.test.jsx' | 1 — stateful counter, event interaction |
23- | 'hooks.test.jsx' | 2 — custom hook harness + ` renderHook ` |
24- | 'lifecycle.test.jsx' | 2 — ` rerender ` , ` unmount ` + effect cleanup |
25- | 'context.test.jsx' | 1 — ` createContext ` + wrapper injection |
26- | 'concurrency.test.jsx' | 2 — React 19 ` use() ` + ` useTransition ` |
20+ | Test File | Tests |
21+ | ---------------------- | ------------------------------------------ |
22+ | 'counter.test.jsx' | 1 — stateful counter, event interaction |
23+ | 'hooks.test.jsx' | 2 — custom hook harness + ` renderHook ` |
24+ | 'lifecycle.test.jsx' | 2 — ` rerender ` , ` unmount ` + effect cleanup |
25+ | 'context.test.jsx' | 1 — ` createContext ` + wrapper injection |
26+ | 'concurrency.test.jsx' | 2 — React 19 ` use() ` + ` useTransition ` |
2727
2828### Frameworks under test
2929
30- | Combination | DOM layer | Assertion style |
31- | ---| ---| ---|
32- | poku + poku-react-testing | happy-dom | ` assert.strictEqual ` |
33- | poku + poku-react-testing | jsdom | ` assert.strictEqual ` |
34- | jest 29 + @testing-library/react | jsdom (jest-environment-jsdom) | ` expect().toBe() ` |
35- | vitest 3 + @testing-library/react | jsdom | ` expect().toBe() ` |
36- | vitest 3 + @testing-library/react | happy-dom | ` expect().toBe() ` |
30+ | Combination | DOM layer | Assertion style |
31+ | --------------------------------- | ------------------------------ | -------------------- |
32+ | poku + poku-react-testing | happy-dom | ` assert.strictEqual ` |
33+ | poku + poku-react-testing | jsdom | ` assert.strictEqual ` |
34+ | jest 29 + @testing-library/react | jsdom (jest-environment-jsdom) | ` expect().toBe() ` |
35+ | vitest 3 + @testing-library/react | jsdom | ` expect().toBe() ` |
36+ | vitest 3 + @testing-library/react | happy-dom | ` expect().toBe() ` |
3737
3838## Results
3939
4040| Scenario | Mean | Min | Max | Stdev | Peak RSS | vs poku+happy-dom |
41- | -------------------- | -------- | -------- | -------- | -------- | ---------- | ------------------- |
42- | poku + happy-dom | 1.073s | 0.996s | 1.230s | 0.085s | 163 .3 MB | * (baseline)* |
43- | poku + jsdom | 1.060s | 1.007s | 1.177s | 0.060s | 163.4 MB | -1% |
44- | jest + jsdom | 0.859s | 0.779s | 0.929s | 0.050s | 206.2 MB | -20 % |
45- | vitest + jsdom | 0.964s | 0.950s | 0.987s | 0.017s | 148.0 MB | -10% |
46- | vitest + happy-dom | 0.838s | 0.812s | 0.864s | 0.021s | 116.3 MB | -22 % |
41+ | ------------------ | ------ | ------ | ------ | ------ | -------- | ----------------- |
42+ | poku + happy-dom | 0.560s | 0.515s | 0.600s | 0.033s | 154 .3 MB | _ (baseline)_ |
43+ | poku + jsdom | 0.444s | 0.429s | 0.451s | 0.008s | 157.1 MB | -21% |
44+ | jest + jsdom | 1.040s | 0.975s | 1.135s | 0.056s | 203.4 MB | +86 % |
45+ | vitest + jsdom | 1.193s | 1.129s | 1.269s | 0.057s | 152.3 MB | +113% |
46+ | vitest + happy-dom | 1.041s | 0.990s | 1.126s | 0.047s | 117.1 MB | +86 % |
4747
4848> ** Wall-clock time** is measured with ` performance.now() ` around the child-process spawn.
4949> ** Peak RSS** is captured via ` /usr/bin/time -l ` on macOS (bytes → MB).
@@ -53,44 +53,44 @@ Each scenario runs the **same 9 React tests** across 5 test files:
5353
5454### Overall ranking (mean wall-clock time)
5555
56- 1 . ** vitest + happy-dom ** — 0.838s
57- 2 . ** jest + jsdom ** — 0.859s
58- 3 . ** vitest + jsdom** — 0.964s
59- 4 . ** poku + jsdom ** — 1.060s
60- 5 . ** poku + happy-dom ** — 1.073s
56+ 1 . ** poku + jsdom ** — 0.444s
57+ 2 . ** poku + happy-dom ** — 0.560s
58+ 3 . ** jest + jsdom** — 1.040s
59+ 4 . ** vitest + happy-dom ** — 1.041s
60+ 5 . ** vitest + jsdom ** — 1.193s
6161
6262### Speed comparison
6363
64- - poku+happy-dom vs jest+jsdom: jest is ** -20% faster **
65- - poku+happy-dom vs vitest+jsdom: vitest is ** -10% faster **
66- - jest+jsdom vs vitest+jsdom: vitest is ** 12 % slower** than jest
64+ - poku+happy-dom vs jest+jsdom: jest is ** 86% slower **
65+ - poku+happy-dom vs vitest+jsdom: vitest is ** 113% slower **
66+ - jest+jsdom vs vitest+jsdom: vitest is ** 15 % slower** than jest
6767
6868### DOM adapter impact
6969
70- - ** poku** : happy-dom vs jsdom — jsdom is ** -1 % faster**
70+ - ** poku** : happy-dom vs jsdom — jsdom is ** -21 % faster**
7171- ** vitest** : happy-dom vs jsdom — jsdom is ** 15% slower**
7272
7373### Memory footprint
7474
75- - ** vitest + happy-dom** : 116.3 MB peak RSS
76- - ** vitest + jsdom** : 148.0 MB peak RSS
77- - ** poku + happy-dom** : 163 .3 MB peak RSS
78- - ** poku + jsdom** : 163.4 MB peak RSS
79- - ** jest + jsdom** : 206.2 MB peak RSS
75+ - ** vitest + happy-dom** : 117.1 MB peak RSS
76+ - ** vitest + jsdom** : 152.3 MB peak RSS
77+ - ** poku + happy-dom** : 154 .3 MB peak RSS
78+ - ** poku + jsdom** : 157.1 MB peak RSS
79+ - ** jest + jsdom** : 203.4 MB peak RSS
8080
8181### Consistency (lower stdev = more predictable)
8282
83- - ** vitest + jsdom** : σ = 0.017s
84- - ** vitest + happy-dom** : σ = 0.021s
85- - ** jest + jsdom ** : σ = 0.050s
86- - ** poku + jsdom** : σ = 0.060s
87- - ** poku + happy-dom ** : σ = 0.085s
83+ - ** poku + jsdom** : σ = 0.008s
84+ - ** poku + happy-dom** : σ = 0.033s
85+ - ** vitest + happy-dom ** : σ = 0.047s
86+ - ** jest + jsdom** : σ = 0.056s
87+ - ** vitest + jsdom ** : σ = 0.057s
8888
8989## Key findings
9090
91- - ** Fastest** : vitest + happy-dom — 0.838s mean
92- - ** Slowest** : poku + happy-dom — 1.073s mean
93- - ** Speed spread** : 28 % difference between fastest and slowest
91+ - ** Fastest** : poku + jsdom — 0.444s mean
92+ - ** Slowest** : vitest + jsdom — 1.193s mean
93+ - ** Speed spread** : 169 % difference between fastest and slowest
9494
9595### Interpretation
9696
@@ -100,6 +100,7 @@ processes with minimal bootstrap — means cold-start overhead is proportional t
100100files, not to the framework's own initialization.
101101
102102** jest** carries the heaviest startup cost due to:
103+
1031041 . Babel transformation of every TSX file on first run (no persistent cache in this benchmark)
1041052 . 'jest-worker' process pool initialisation
1051063 . JSDOM environment setup per test file
0 commit comments