Skip to content

Commit 6986605

Browse files
committed
04/02: add problem text
1 parent 15ff02a commit 6986605

File tree

13 files changed

+150
-0
lines changed

13 files changed

+150
-0
lines changed

exercises/04.performance/01.problem.profiling-slow-tests/vitest.config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { defineConfig } from 'vitest/config'
2+
// 🐨 Import `vitestProfiler` from `vitest-profiler/plugin`
23

34
export default defineConfig({
5+
// 🐨 Add a `plugins` property at the root of the Vitest configuration.
6+
// Make it equal an array. Inside that array, call the `vitestProfiler`
7+
// function you imported earlier.
8+
// 💰 plugins: [somePlugin()],
49
test: {
510
globals: true,
611
},
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Concurrency
2+
3+
For the remainder of this exercise block, I would like for us to focus on techniques to apply to performance issues caused by a _large number of tests_. Even if every single one of your test cases is blazingly-fast, if you have a million of them, your test run will still be slow.
4+
5+
One of the ways to fix that is by utilizing _concurrency_.
6+
7+
But before that, you need to understand how Vitest runs your tests.
8+
9+
## Vitest test run
10+
11+
By default, Vitest runs every _test file in parallel_ and every _test case (within the same test file) in sequence_. You can visualize your test run like this:
12+
13+
<img
14+
src="/assets/05-02-vitest-defaults.png"
15+
alt="A schematics showing two test suites in a column, each containing a waterfall of test cases inside."
16+
width="500"
17+
style={{ margin: 'auto' }}
18+
/>
19+
20+
Both `suite-a` and `suite-b` will run simultaneously in different workers, but test cases inside them will run one after another. In the end, your test run is only as fast as your slowest test suite.
21+
22+
<callout-info>_Parallelism_ is enabled by default in Vitest but _not concurrency_.</callout-info>
23+
24+
These defaults are intentional and are designed to prevent flakiness. Vitest can afford to run test suites in parallel because each test suite _runs in a separate worker_ (again, by default). Workers share the test environment but nothing else, which makes it safe to run them at the same time.
25+
26+
On the other hand, all test cases within the same test suite run in the same worker. Here, the risk of shared state is higher, and so Vitest defaults to running them in sequence to prevent test cases from stepping on each other's toes.
27+
28+
Both parallelism and concurrency can be configured in Vitest and in the right test suite can give you a significant boost in test performance. It's time you learned how.
29+
30+
## Your task
31+
32+
If you run `npm test`, you will notice how slow our test suite is:
33+
34+
```
35+
✓ src/one.test.ts (100 tests) 2644ms
36+
✓ src/two.test.ts (100 tests) 2644ms
37+
38+
Test Files 2 passed (2)
39+
Tests 200 passed (200)
40+
Start at 12:24:45
41+
Duration 2.91s (transform 25ms, setup 0ms, collect 25ms, tests 5.29s, environment 0ms, prepare 65ms)
42+
```
43+
44+
It's not slow because tests are slow individually. It is slow because there are 200 tests in total, making the test run last a soul-crushing _3 seconds_.
45+
46+
👨‍💼 Your task is to _improve that time_ by using concurrency. Head to those two test files and follow the instructions to make their test cases concurrent. Once you're done, run the tests again and see if you can spot a difference.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"type": "module",
3+
"name": "exercises_04.performance_02.problem.concurrency",
4+
"scripts": {
5+
"dev": "vite",
6+
"test": "vitest",
7+
"build": "vite build"
8+
},
9+
"devDependencies": {
10+
"vite": "^6.2.6",
11+
"vitest": "^3.1.1"
12+
}
13+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { setTimeout } from 'node:timers/promises'
2+
3+
for (let i = 0; i < 100; i++) {
4+
// 🐨 Replace the `test()` function below with `test.concurrent()`.
5+
// This will make all the test cases in this test file run simultaneously.
6+
test(`${i}`, async ({ expect }) => {
7+
await setTimeout(25)
8+
expect(i).toBe(i)
9+
})
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { setTimeout } from 'node:timers/promises'
2+
3+
for (let i = 0; i < 100; i++) {
4+
// 🐨 Replace the `test()` function below with `test.concurrent()`.
5+
// This will make all the test cases in this test file run simultaneously.
6+
test(`${i}`, async ({ expect }) => {
7+
await setTimeout(25)
8+
expect(i).toBe(i)
9+
})
10+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "./tsconfig.base.json",
3+
"include": ["src/**/*"],
4+
"exclude": ["src/**/*.test.ts*"],
5+
"compilerOptions": {
6+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
7+
"jsx": "react-jsx"
8+
}
9+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ESNext",
4+
"module": "ESNext",
5+
"useDefineForClassFields": true,
6+
"skipLibCheck": true,
7+
8+
/* Bundler mode */
9+
"moduleResolution": "bundler",
10+
"allowImportingTsExtensions": true,
11+
"isolatedModules": true,
12+
"moduleDetection": "force",
13+
"noEmit": true,
14+
"verbatimModuleSyntax": true,
15+
16+
/* Linting */
17+
"strict": true,
18+
"noUnusedLocals": false,
19+
"noUnusedParameters": false,
20+
"noFallthroughCasesInSwitch": true,
21+
"noUncheckedSideEffectImports": true
22+
}
23+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"files": [],
3+
"references": [
4+
{ "path": "./tsconfig.app.json" },
5+
{ "path": "./tsconfig.node.json" },
6+
{ "path": "./tsconfig.test.json" }
7+
]
8+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"extends": "./tsconfig.base.json",
3+
"compilerOptions": {
4+
"lib": ["ES2023"]
5+
},
6+
"include": ["vitest.config.ts"]
7+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "./tsconfig.app.json",
3+
"include": ["src/**/*", "src/**/*.test.ts*"],
4+
"exclude": [],
5+
"compilerOptions": {
6+
"types": ["vitest/globals"]
7+
}
8+
}

0 commit comments

Comments
 (0)