├── .github
└── FUNDING.yml
├── License.md
└── Readme.md
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: sapegin
2 | ko_fi: sapegin
3 | custom: https://www.buymeacoffee.com/sapegin
4 |
--------------------------------------------------------------------------------
/License.md:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 |
3 | Statement of Purpose
4 |
5 | The laws of most jurisdictions throughout the world automatically confer
6 | exclusive Copyright and Related Rights (defined below) upon the creator and
7 | subsequent owner(s) (each and all, an "owner") of an original work of
8 | authorship and/or a database (each, a "Work").
9 |
10 | Certain owners wish to permanently relinquish those rights to a Work for the
11 | purpose of contributing to a commons of creative, cultural and scientific
12 | works ("Commons") that the public can reliably and without fear of later
13 | claims of infringement build upon, modify, incorporate in other works, reuse
14 | and redistribute as freely as possible in any form whatsoever and for any
15 | purposes, including without limitation commercial purposes. These owners may
16 | contribute to the Commons to promote the ideal of a free culture and the
17 | further production of creative, cultural and scientific works, or to gain
18 | reputation or greater distribution for their Work in part through the use and
19 | efforts of others.
20 |
21 | For these and/or other purposes and motivations, and without any expectation
22 | of additional consideration or compensation, the person associating CC0 with a
23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25 | and publicly distribute the Work under its terms, with knowledge of his or her
26 | Copyright and Related Rights in the Work and the meaning and intended legal
27 | effect of CC0 on those rights.
28 |
29 | 1. Copyright and Related Rights. A Work made available under CC0 may be
30 | protected by copyright and related or neighboring rights ("Copyright and
31 | Related Rights"). Copyright and Related Rights include, but are not limited
32 | to, the following:
33 |
34 | i. the right to reproduce, adapt, distribute, perform, display, communicate,
35 | and translate a Work;
36 |
37 | ii. moral rights retained by the original author(s) and/or performer(s);
38 |
39 | iii. publicity and privacy rights pertaining to a person's image or likeness
40 | depicted in a Work;
41 |
42 | iv. rights protecting against unfair competition in regards to a Work,
43 | subject to the limitations in paragraph 4(a), below;
44 |
45 | v. rights protecting the extraction, dissemination, use and reuse of data in
46 | a Work;
47 |
48 | vi. database rights (such as those arising under Directive 96/9/EC of the
49 | European Parliament and of the Council of 11 March 1996 on the legal
50 | protection of databases, and under any national implementation thereof,
51 | including any amended or successor version of such directive); and
52 |
53 | vii. other similar, equivalent or corresponding rights throughout the world
54 | based on applicable law or treaty, and any national implementations thereof.
55 |
56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59 | and Related Rights and associated claims and causes of action, whether now
60 | known or unknown (including existing as well as future claims and causes of
61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum
62 | duration provided by applicable law or treaty (including future time
63 | extensions), (iii) in any current or future medium and for any number of
64 | copies, and (iv) for any purpose whatsoever, including without limitation
65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66 | the Waiver for the benefit of each member of the public at large and to the
67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver
68 | shall not be subject to revocation, rescission, cancellation, termination, or
69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work
70 | by the public as contemplated by Affirmer's express Statement of Purpose.
71 |
72 | 3. Public License Fallback. Should any part of the Waiver for any reason be
73 | judged legally invalid or ineffective under applicable law, then the Waiver
74 | shall be preserved to the maximum extent permitted taking into account
75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76 | is so judged Affirmer hereby grants to each affected person a royalty-free,
77 | non transferable, non sublicensable, non exclusive, irrevocable and
78 | unconditional license to exercise Affirmer's Copyright and Related Rights in
79 | the Work (i) in all territories worldwide, (ii) for the maximum duration
80 | provided by applicable law or treaty (including future time extensions), (iii)
81 | in any current or future medium and for any number of copies, and (iv) for any
82 | purpose whatsoever, including without limitation commercial, advertising or
83 | promotional purposes (the "License"). The License shall be deemed effective as
84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the
85 | License for any reason be judged legally invalid or ineffective under
86 | applicable law, such partial invalidity or ineffectiveness shall not
87 | invalidate the remainder of the License, and in such case Affirmer hereby
88 | affirms that he or she will not (i) exercise any of his or her remaining
89 | Copyright and Related Rights in the Work or (ii) assert any associated claims
90 | and causes of action with respect to the Work, in either case contrary to
91 | Affirmer's express Statement of Purpose.
92 |
93 | 4. Limitations and Disclaimers.
94 |
95 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
96 | surrendered, licensed or otherwise affected by this document.
97 |
98 | b. Affirmer offers the Work as-is and makes no representations or warranties
99 | of any kind concerning the Work, express, implied, statutory or otherwise,
100 | including without limitation warranties of title, merchantability, fitness
101 | for a particular purpose, non infringement, or the absence of latent or
102 | other defects, accuracy, or the present or absence of errors, whether or not
103 | discoverable, all to the greatest extent permissible under applicable law.
104 |
105 | c. Affirmer disclaims responsibility for clearing rights of other persons
106 | that may apply to the Work or any use thereof, including without limitation
107 | any person's Copyright and Related Rights in the Work. Further, Affirmer
108 | disclaims responsibility for obtaining any necessary consents, permissions
109 | or other rights required for any use of the Work.
110 |
111 | d. Affirmer understands and acknowledges that Creative Commons is not a
112 | party to this document and has no duty or obligation with respect to this
113 | CC0 or use of the Work.
114 |
115 | For more information, please see
116 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |

12 |
13 |
Vitest cheat sheet
14 |
15 |
16 |
17 | _This is a fork of the popular [Jest cheat sheet](https://github.com/sapegin/jest-cheat-sheet)._
18 |
19 | [](https://sapegin.me/book/)
20 |
21 | ## Table of contents
22 |
23 |
24 |
25 | - [Getting started](#getting-started)
26 | - [Test structure](#test-structure)
27 | - [Basic test structure](#basic-test-structure)
28 | - [Test with grouping, setup and teardown code](#test-with-grouping-setup-and-teardown-code)
29 | - [Matchers](#matchers)
30 | - [Basic matchers](#basic-matchers)
31 | - [Truthiness](#truthiness)
32 | - [Numbers](#numbers)
33 | - [Strings](#strings)
34 | - [Arrays](#arrays)
35 | - [Objects](#objects)
36 | - [Exceptions](#exceptions)
37 | - [Snapshots](#snapshots)
38 | - [Misc](#misc)
39 | - [Promise matchers](#promise-matchers)
40 | - [Async tests](#async-tests)
41 | - [async/await](#asyncawait)
42 | - [Promises](#promises)
43 | - [Mocks](#mocks)
44 | - [Mock functions](#mock-functions)
45 | - [Returning, resolving and rejecting values](#returning-resolving-and-rejecting-values)
46 | - [Mock modules using `vi.mock()` method](#mock-modules-using-vimock-method)
47 | - [Mock modules using a mock file](#mock-modules-using-a-mock-file)
48 | - [Mock object methods](#mock-object-methods)
49 | - [Mock getters and setters](#mock-getters-and-setters)
50 | - [Clearing and restoring mocks](#clearing-and-restoring-mocks)
51 | - [Accessing the original module when using mocks](#accessing-the-original-module-when-using-mocks)
52 | - [Timer mocks](#timer-mocks)
53 | - [Data-driven tests](#data-driven-tests)
54 | - [Skipping tests](#skipping-tests)
55 | - [Testing modules with side effects](#testing-modules-with-side-effects)
56 | - [Resources](#resources)
57 | - [Contributing](#contributing)
58 | - [Sponsoring](#sponsoring)
59 | - [Author and license](#author-and-license)
60 |
61 |
62 |
63 | ## Getting started
64 |
65 | [See the official docs](https://vitest.dev/guide/)
66 |
67 | ## Test structure
68 |
69 | ### Basic test structure
70 |
71 | > [!NOTE]
72 | > In comparison to Jest, in Vitest you need to explicitly import all methods
73 |
74 | ```js
75 | import { expect, test } from 'vitest'
76 | import { makePoniesPink } from './makePoniesPink'
77 |
78 | test('make each pony pink', () => {
79 | const actual = makePoniesPink(['Alice', 'Bob', 'Eve'])
80 | expect(actual).toEqual(['Pink Alice', 'Pink Bob', 'Pink Eve'])
81 | })
82 | ```
83 |
84 | ### Test with grouping, setup and teardown code
85 |
86 | ```js
87 | import { expect, test, beforeAll, afterAll , beforeEach,afterEach} from 'vitest'
88 | import { makePoniesPink } from './makePoniesPink'
89 |
90 | describe('makePoniesPink', () => {
91 | beforeAll(() => {
92 | // Runs before all tests
93 | })
94 | afterAll(() => {
95 | // Runs after all tests
96 | })
97 | beforeEach(() => {
98 | // Runs before each test
99 | })
100 | afterEach(() => {
101 | // Runs after each test
102 | })
103 |
104 | test('make each pony pink', () => {
105 | const actual = makePoniesPink(['Alice', 'Bob', 'Eve'])
106 | expect(actual).toEqual(['Pink Alice', 'Pink Bob', 'Pink Eve'])
107 | })
108 | ```
109 |
110 | ## Matchers
111 |
112 | [Matchers docs](https://vitest.dev/api/expect.html)
113 |
114 | ### Basic matchers
115 |
116 | ```js
117 | expect(42).toBe(42) // Strict equality (===)
118 | expect(42).not.toBe(3) // Strict equality (!==)
119 | expect([1, 2]).toEqual([1, 2]) // Deep equality
120 | expect({ a: undefined, b: 2 }).toEqual({ b: 2 }) // Deep equality
121 | expect({ a: undefined, b: 2 }).not.toStrictEqual({ b: 2 }) // Strict equality (Jest 23+)
122 | ```
123 |
124 | ### Truthiness
125 |
126 | ```js
127 | // Matches anything that an if statement treats as true (true, 1, 'hello', {}, [], 5.3)
128 | expect('foo').toBeTruthy()
129 | // Matches anything that an if statement treats as false (false, 0, '', null, undefined, NaN)
130 | expect('').toBeFalsy()
131 | // Matches only null
132 | expect(null).toBeNull()
133 | // Matches only undefined
134 | expect(undefined).toBeUndefined()
135 | // The opposite of toBeUndefined
136 | expect(7).toBeDefined()
137 | // Matches true or false
138 | expect(true).toEqual(expect.any(Boolean))
139 | ```
140 |
141 | ### Numbers
142 |
143 | ```js
144 | expect(2).toBeGreaterThan(1)
145 | expect(1).toBeGreaterThanOrEqual(1)
146 | expect(1).toBeLessThan(2)
147 | expect(1).toBeLessThanOrEqual(1)
148 | expect(0.2 + 0.1).toBeCloseTo(0.3, 5)
149 | expect(NaN).toEqual(expect.any(Number))
150 | ```
151 |
152 | ### Strings
153 |
154 | ```js
155 | expect('long string').toMatch('str')
156 | expect('string').toEqual(expect.any(String))
157 | expect('coffee').toMatch(/ff/)
158 | expect('pizza').not.toMatch('coffee')
159 | expect(['pizza', 'coffee']).toEqual([expect.stringContaining('zz'), expect.stringMatching(/ff/)])
160 | ```
161 |
162 | ### Arrays
163 |
164 | ```js
165 | expect([]).toEqual(expect.any(Array))
166 | expect(['Alice', 'Bob', 'Eve']).toHaveLength(3)
167 | expect(['Alice', 'Bob', 'Eve']).toContain('Alice')
168 | expect([{ a: 1 }, { a: 2 }]).toContainEqual({ a: 1 })
169 | expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(['Alice', 'Bob']))
170 | ```
171 |
172 | ### Objects
173 |
174 | ```js
175 | expect({ a: 1 }).toHaveProperty('a')
176 | expect({ a: 1 }).toHaveProperty('a', 1)
177 | expect({ a: { b: 1 } }).toHaveProperty('a.b')
178 | expect({ a: 1, b: 2 }).toMatchObject({ a: 1 })
179 | expect({ a: 1, b: 2 }).toMatchObject({
180 | a: expect.any(Number),
181 | b: expect.any(Number),
182 | })
183 | expect([{ a: 1 }, { b: 2 }]).toEqual([
184 | expect.objectContaining({ a: expect.any(Number) }),
185 | expect.anything(),
186 | ])
187 | ```
188 |
189 | ### Exceptions
190 |
191 | ```js
192 | // const fn = () => { throw new Error('Out of cheese!') }
193 | expect(fn).toThrowError()
194 | expect(fn).toThrowError('Out of cheese')
195 | expect(fn).toThrowErrorMatchingSnapshot()
196 | expect(fn).toThrowErrorMatchingInlineSnapshot()
197 |
198 | // const fn = () => { console.error('Out of cheese!') }
199 | expect(fn).not.toThrowError()
200 | expect(fn).not.toThrowError('Out of cheese')
201 | ```
202 |
203 | ### Snapshots
204 |
205 | ```js
206 | expect(node).toMatchSnapshot()
207 | expect(user).toMatchSnapshot({
208 | date: expect.any(Date),
209 | })
210 | expect(user).toMatchInlineSnapshot()
211 | ```
212 |
213 | ### Misc
214 |
215 | ```js
216 | expect(new A()).toBeInstanceOf(A)
217 | expect(() => {}).toEqual(expect.any(Function))
218 | expect('pizza').toEqual(expect.anything())
219 | ```
220 |
221 | ### Promise matchers
222 |
223 | > [!WARNING]
224 | > Don’t forget to return the result of the `expect()` method from each test case.
225 |
226 | ```js
227 | test('resolves with a lemon', () => {
228 | return expect(Promise.resolve('lemon')).resolves.toBe('lemon')
229 | })
230 | test('rejects with an octopus', () => {
231 | return expect(Promise.reject('octopus')).rejects.toBeDefined()
232 | })
233 | test('rejects with an error', () => {
234 | return expect(Promise.reject(Error('pizza'))).rejects.toThrow()
235 | })
236 | ```
237 |
238 | Or with async/await:
239 |
240 | > [!WARNING]
241 | > Don’t forget to `await` the `expect()` method in each test case.
242 |
243 | ```js
244 | test('resolves with a lemon', async () => {
245 | await expect(Promise.resolve('lemon')).resolves.toBe('lemon')
246 | })
247 | test('doesn’t resolve with an octopus', async () => {
248 | await expect(Promise.resolve('lemon')).resolves.not.toBe('octopus')
249 | })
250 | ```
251 |
252 | ## Async tests
253 |
254 | ### async/await
255 |
256 | ```js
257 | test('async test', async () => {
258 | const result = await runAsyncOperation()
259 | expect(result).toBe(true)
260 | })
261 | ```
262 |
263 | ### Promises
264 |
265 | _Return_ a Promise from your test:
266 |
267 | ```js
268 | test('async test', () => {
269 | return runAsyncOperation().then((result) => {
270 | expect(result).toBe(true)
271 | })
272 | })
273 | ```
274 |
275 | ## Mocks
276 |
277 | ### Mock functions
278 |
279 | [Mock functions docs](https://jestjs.io/docs/en/mock-function-api)
280 |
281 | ```js
282 | import { expect, vi } from 'vitest'
283 |
284 | // const fn = vi.fn()
285 | // const fn = vi.fn().mockName('Unicorn') -- named mock, Jest 22+
286 | expect(fn).toHaveBeenCalled() // Function was called
287 | expect(fn).not.toHaveBeenCalled() // Function was *not* called
288 | expect(fn).toHaveBeenCalledTimes(1) // Function was called only once
289 | expect(fn).toHaveBeenCalledWith(arg1, expect.anything(), arg3) // Any of calls was with these arguments
290 | expect(fn).toHaveBeenLastCalledWith(arg1, arg2) // Last call was with these arguments
291 | expect(fn).toHaveBeenNthCalledWith(callNumber, args) // Nth call was with these arguments (Jest 23+)
292 | expect(fn).toHaveReturnedTimes(2) // Function was returned without throwing an error (Jest 23+)
293 | expect(fn).toHaveReturnedWith(value) // Function returned a value (Jest 23+)
294 | expect(fn).toHaveLastReturnedWith(value) // Last function call returned a value (Jest 23+)
295 | expect(fn).toHaveNthReturnedWith(value) // Nth function call returned a value (Jest 23+)
296 |
297 | // Multiple calls
298 | expect(fn.mock.calls).toEqual([
299 | ['first', 'call', 'args'],
300 | ['second', 'call', 'args'],
301 | ])
302 |
303 | // fn.mock.calls[0][0] — the first argument of the first call
304 | expect(fn.mock.calls[0][0]).toBe(2)
305 | ```
306 |
307 | You can also use snapshots:
308 |
309 | ```js
310 | test('call the callback', () => {
311 | const callback = vi.fn().mockName('Unicorn') // mockName is available in Jest 22+
312 | fn(callback)
313 | expect(callback).toMatchSnapshot()
314 | // ->
315 | // [MockFunction Unicorn] {
316 | // "calls": Array [
317 | // ...
318 | })
319 | ```
320 |
321 | And pass an implementation to `vi.fn` function:
322 |
323 | ```js
324 | const callback = vi.fn(() => true)
325 | ```
326 |
327 | ### Returning, resolving and rejecting values
328 |
329 | Your mocks can return values:
330 |
331 | ```js
332 | const callback = vi.fn().mockReturnValue(true)
333 | const callbackOnce = vi.fn().mockReturnValueOnce(true)
334 | ```
335 |
336 | Or resolve values:
337 |
338 | ```js
339 | const promise = vi.fn().mockResolvedValue(true)
340 | const promiseOnce = vi.fn().mockResolvedValueOnce(true)
341 | ```
342 |
343 | They can even reject values:
344 |
345 | ```js
346 | const failedPromise = vi.fn().mockRejectedValue('Error')
347 | const failedPromiseOnce = vi.fn().mockRejectedValueOnce('Error')
348 | ```
349 |
350 | You can even combine these:
351 |
352 | ```js
353 | const callback = vi.fn().mockReturnValueOnce(false).mockReturnValue(true)
354 |
355 | // ->
356 | // call 1: false
357 | // call 2+: true
358 | ```
359 |
360 | ### Mock modules using `vi.mock()` method
361 |
362 | [Mock modules](https://vitest.dev/api/mock.html)
363 |
364 | ```js
365 | import { vi } from 'vitest'
366 |
367 | // The original lodash/memoize should exist
368 | vi.mock('lodash/memoize', () => (a) => a)
369 | // The original lodash/memoize isn’t required
370 | vi.mock('lodash/memoize', () => (a) => a, { virtual: true })
371 | ```
372 |
373 | ### Mock modules using a mock file
374 |
375 | 1. Create a file like `__mocks__/lodash/memoize.js`:
376 |
377 | ```js
378 | module.exports = (a) => a
379 | ```
380 |
381 | 2. Add to your test:
382 |
383 | ```js
384 | vi.mock('lodash/memoize')
385 | ```
386 |
387 | ### Mock object methods
388 |
389 | ```js
390 | import { vi } from 'vitest'
391 |
392 | const spy = vi.spyOn(console, 'log').mockImplementation(() => {})
393 | expect(console.log.mock.calls).toEqual([['dope'], ['nope']])
394 | spy.mockRestore()
395 | ```
396 |
397 | ```js
398 | import { vi } from 'vitest'
399 |
400 | const spy = vi.spyOn(ajax, 'request').mockImplementation(() => Promise.resolve({ success: true }))
401 | expect(spy).toHaveBeenCalled()
402 | spy.mockRestore()
403 | ```
404 |
405 | ### Mock getters and setters
406 |
407 | ```js
408 | import { vi } from 'vitest'
409 |
410 | const location = {}
411 | const getTitle = vi.spyOn(location, 'title', 'get').mockImplementation(() => 'pizza')
412 | const setTitle = vi.spyOn(location, 'title', 'set').mockImplementation(() => {})
413 | ```
414 |
415 | ### Clearing and restoring mocks
416 |
417 | For one mock:
418 |
419 | ```js
420 | fn.mockClear() // Clears mock usage date (fn.mock.calls, fn.mock.instances)
421 | fn.mockReset() // Clears and removes any mocked return values or implementations
422 | fn.mockRestore() // Resets and restores the initial implementation
423 | ```
424 |
425 | > [!NOTE]
426 | > The `mockRestore()` method works only with mocks created by `vi.spyOn()`.
427 |
428 | For all mocks:
429 |
430 | ```js
431 | vi.clearAllMocks()
432 | vi.resetAllMocks()
433 | vi.restoreAllMocks()
434 | ```
435 |
436 | ### Accessing the original module when using mocks
437 |
438 | ```js
439 | import { vi } from 'vitest'
440 |
441 | vi.mock('./example.js', async () => {
442 | const axios = await vi.importActual('./example.js')
443 |
444 | return { ...axios, get: vi.fn() }
445 | })
446 | ```
447 |
448 | ### Timer mocks
449 |
450 | [Fake times](https://vitest.dev/api/vi.html#fake-timers)
451 |
452 | Write synchronous test for code that uses native timer functions (`setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`).
453 |
454 | ```js
455 | // Enable fake timers
456 | vi.useFakeTimers()
457 |
458 | test('kill the time', () => {
459 | const callback = vi.fn()
460 |
461 | // Run some code that uses setTimeout or setInterval
462 | const actual = someFunctionThatUseTimers(callback)
463 |
464 | // Fast-forward until all timers have been executed
465 | vi.runAllTimers()
466 |
467 | // Check the results synchronously
468 | expect(callback).toHaveBeenCalledTimes(1)
469 | })
470 | ```
471 |
472 | Or adjust timers by time with [vi.advanceTimersByTime()](https://vitest.dev/api/vi.html#vi-advancetimersbytime):
473 |
474 | ```js
475 | // Enable fake timers
476 | vi.useFakeTimers()
477 |
478 | test('kill the time', () => {
479 | const callback = vi.fn()
480 |
481 | // Run some code that uses setTimeout or setInterval
482 | const actual = someFunctionThatUseTimers(callback)
483 |
484 | // Fast-forward for 250 ms
485 | vi.advanceTimersByTime(250)
486 |
487 | // Check the results synchronously
488 | expect(callback).toHaveBeenCalledTimes(1)
489 | })
490 | ```
491 |
492 | Use [vi.runOnlyPendingTimers()](https://vitest.dev/api/vi.html#vi-runonlypendingtimers) for special cases.
493 |
494 | > ![NOTE]
495 | > You should call `vi.useFakeTimers()` in your test case to use other fake timer methods.
496 |
497 | ## Data-driven tests
498 |
499 | Run the same test with different data:
500 |
501 | ```js
502 | test.each([
503 | [1, 1, 2],
504 | [1, 2, 3],
505 | [2, 1, 3],
506 | ])('.add(%s, %s)', (a, b, expected) => {
507 | expect(a + b).toBe(expected)
508 | })
509 | ```
510 |
511 | Or the same using template literals:
512 |
513 | ```js
514 | test.each`
515 | a | b | expected
516 | ${1} | ${1} | ${2}
517 | ${1} | ${2} | ${3}
518 | ${2} | ${1} | ${3}
519 | `('returns $expected when $a is added $b', ({ a, b, expected }) => {
520 | expect(a + b).toBe(expected)
521 | })
522 | ```
523 |
524 | Or on `describe` level:
525 |
526 | ```js
527 | describe.each([['mobile'], ['tablet'], ['desktop']])('checkout flow on %s', (viewport) => {
528 | test('displays success page', () => {
529 | //
530 | })
531 | })
532 | ```
533 |
534 | [describe.each() docs](https://vitest.dev/api/#describe-each), [test.each() docs](https://vitest.dev/api/#test-each),
535 |
536 | ## Skipping tests
537 |
538 | Don’t run these tests:
539 |
540 | ```js
541 | describe.skip('makePoniesPink'...
542 | tests.skip('make each pony pink'...
543 | ```
544 |
545 | Run only these tests:
546 |
547 | ```js
548 | describe.only('makePoniesPink'...
549 | tests.only('make each pony pink'...
550 | ```
551 |
552 | ## Testing modules with side effects
553 |
554 | Node.js and Vitest cache modules you `import`. To test modules with side effects you’ll need to reset the module registry between tests:
555 |
556 | ```js
557 | const modulePath = '../module-to-test'
558 |
559 | afterEach(() => {
560 | vi.resetModules()
561 | })
562 |
563 | test('first test', async () => {
564 | // Prepare conditions for the first test
565 | const result = await import(modulePath)
566 | expect(result).toMatchSnapshot()
567 | })
568 |
569 | test('second text', async () => {
570 | // Prepare conditions for the second test
571 | const fn = () => await import(modulePath)
572 | expect(fn).toThrowError()
573 | })
574 | ```
575 |
576 | ## Resources
577 |
578 | - [Vitest site](https://vitest.dev/)
579 | - [Modern React testing, part 1: best practices](https://blog.sapegin.me/all/react-testing-1-best-practices/) by Artem Sapegin
580 | - [Modern React testing, part 2: Jest and Enzyme](https://blog.sapegin.me/all/react-testing-2-jest-and-enzyme/) by Artem Sapegin
581 | - [Modern React testing, part 3: Jest and React Testing Library](https://blog.sapegin.me/all/react-testing-3-jest-and-react-testing-library/) by Artem Sapegin
582 | - [Modern React testing, part 4: Cypress and Cypress Testing Library](https://sapegin.me/blog/react-testing-4-cypress/) by Artem Sapegin
583 | - [Modern React testing, part 5: Playwright](https://sapegin.me/blog/react-testing-5-playwright/) by Artem Sapegin
584 |
585 | ---
586 |
587 | ## Contributing
588 |
589 | Improvements are welcome! Open an issue, or send a pull request.
590 |
591 | ## Sponsoring
592 |
593 | This software has been developed with lots of coffee, buy me one more cup to keep it going.
594 |
595 |
596 |
597 | ## Author and license
598 |
599 | [Artem Sapegin](https://sapegin.me/), a frontend engineer at [Stage+](https://www.stage-plus.com) and the creator of [React Styleguidist](https://react-styleguidist.js.org/). I also write about frontend at [my blog](https://sapegin.me/blog/).
600 |
601 | CC0 1.0 Universal license, see the included [License.md](/License.md) file.
602 |
--------------------------------------------------------------------------------