{"id":34360,"date":"2025-08-26T11:13:43","date_gmt":"2025-08-26T15:13:43","guid":{"rendered":"https:\/\/sep.com\/?p=34360"},"modified":"2025-08-26T11:13:43","modified_gmt":"2025-08-26T15:13:43","slug":"so-long-beforeeach-new-testing-pattern-javascript","status":"publish","type":"post","link":"https:\/\/hexboxdev.wpenginepowered.com\/blog\/so-long-beforeeach-new-testing-pattern-javascript\/","title":{"rendered":"So Long, &#8220;beforeEach&#8221;? New Testing Pattern in JavaScript"},"content":{"rendered":"<p>Tests in <a href=\"https:\/\/hexboxdev.wpenginepowered.com\/blog\/mostly-useless-lessons-from-doing-math-with-typescript-types\/\" target=\"_blank\" rel=\"noopener\">TypeScript<\/a> or <a href=\"https:\/\/hexboxdev.wpenginepowered.com\/blog\/typescript-vs-javascript-explained\/\" target=\"_blank\" rel=\"noopener\">JavaScript<\/a> often run setup code inside hooks like <a href=\"https:\/\/jestjs.io\/docs\/setup-teardown\" target=\"_blank\" rel=\"noopener\"><strong><em>&#8220;beforeEach&#8221;<\/em><\/strong><\/a>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-34365 size-large\" src=\"https:\/\/hexboxdev.wpenginepowered.com\/wp-content\/uploads\/2025\/08\/beforeEach-3-1024x888.png\" alt=\"Sample code demonstrating how to setup code using &quot;beforeEach&quot; hook.\" width=\"1024\" height=\"888\" srcset=\"https:\/\/hexboxdev.wpenginepowered.com\/wp-content\/uploads\/2025\/08\/beforeEach-3-1024x888.png 1024w, https:\/\/hexboxdev.wpenginepowered.com\/wp-content\/uploads\/2025\/08\/beforeEach-3-300x260.png 300w, https:\/\/hexboxdev.wpenginepowered.com\/wp-content\/uploads\/2025\/08\/beforeEach-3-768x666.png 768w, https:\/\/hexboxdev.wpenginepowered.com\/wp-content\/uploads\/2025\/08\/beforeEach-3.png 1466w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><br \/>\nDevelopers must take care to get the setup correct, especially in large or nested test suites.<\/p>\n<ul>\n<li>Many scopes (<strong><em>&#8220;describe&#8221;<\/em><\/strong>, <strong><em>&#8220;beforeEach&#8221;<\/em><\/strong>, <strong><em>&#8220;it&#8221;<\/em><\/strong>) read and mutate the test fixtures. The setup must reset the fixtures before each test. Test authors must be careful to not accidentally shadow a variable declared in a higher scope.<\/li>\n<li>Asynchronous setup (<em><strong>await createTeam()<\/strong><\/em>) often requires splitting variable initialization from declaration. Typescript hacks (<strong><em>&#8220;= null!&#8221;<\/em><\/strong>) paper over this issue.<\/li>\n<\/ul>\n<p>Some <a href=\"https:\/\/vitest.dev\/guide\/test-context.html\" target=\"_blank\">test<\/a> <a href=\"https:\/\/playwright.dev\/docs\/test-fixtures\" target=\"_blank\">runners<\/a> now provide fixtures-per-test. We can re-write our test suite as the following:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-34366 size-large\" src=\"https:\/\/hexboxdev.wpenginepowered.com\/wp-content\/uploads\/2025\/08\/itExtend-782x1024.png\" alt=\"sample code demonstrating how to use Vitest test fixtures.\" width=\"782\" height=\"1024\" srcset=\"https:\/\/hexboxdev.wpenginepowered.com\/wp-content\/uploads\/2025\/08\/itExtend-782x1024.png 782w, https:\/\/hexboxdev.wpenginepowered.com\/wp-content\/uploads\/2025\/08\/itExtend-229x300.png 229w, https:\/\/hexboxdev.wpenginepowered.com\/wp-content\/uploads\/2025\/08\/itExtend-768x1006.png 768w, https:\/\/hexboxdev.wpenginepowered.com\/wp-content\/uploads\/2025\/08\/itExtend-1173x1536.png 1173w, https:\/\/hexboxdev.wpenginepowered.com\/wp-content\/uploads\/2025\/08\/itExtend.png 1368w\" sizes=\"auto, (max-width: 782px) 100vw, 782px\" \/><\/p>\n<p> This test setup addresses our original pain points, and adds a few new features:<\/p>\n<ul>\n<li> Fixtures are only created if a test or another fixture depends on it (e.g. <em><strong>async ({ player }) =&gt; { &#8230; }<\/strong><\/em>).<\/li>\n<li> Test files can share fixtures from a common base <em><strong>&#8220;it&#8221;<\/strong><\/em>.<\/li>\n<li>Fixtures by default are created and cleaned up for every test. Fixtures can opt into being shared across an entire test file or test worker. This is useful for something that takes a long time to setup, like an api server or a containerized database.<\/li>\n<\/ul>\n<h2>Further Considerations:<\/h2>\n<ul>\n<li>Consider how much to put in the test context. Sometimes it makes sense to put all the test cases in the context. Other times it makes sense to share a base fixture that each test modifies.<\/li>\n<li>Test context and the setup hooks can be used together. While you can move things like mock cleanup to an <a href=\"https:\/\/playwright.dev\/docs\/test-fixtures#adding-global-beforeeachaftereach-hooks\" target=\"_blank\">&#8220;auto&#8221; fixture<\/a>, it may be more convenient to to continue to use <em><strong>&#8220;beforeEach&#8221;<\/strong><\/em> and the like.<\/li>\n<\/ul>\n<p>For further reading, check out the <a href=\"https:\/\/vitest.dev\/guide\/test-context.html\" target=\"_blank\" rel=\"noopener\">Vitest<\/a> and <a href=\"https:\/\/playwright.dev\/docs\/test-fixtures\" target=\"_blank\" rel=\"noopener\">Playwright<\/a> documentation.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Tests in TypeScript or JavaScript often run setup code inside hooks like &#8220;beforeEach&#8221;: Developers must take care to get the setup correct, especially in large or nested test suites. Many scopes (&#8220;describe&#8221;, &#8220;beforeEach&#8221;, &#8220;it&#8221;) read and mutate the test fixtures. The setup must reset the fixtures before each test. Test authors must be careful to [&hellip;]<\/p>\n","protected":false},"author":69,"featured_media":34372,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"content-type":"","associated_team_member":0,"associated_user_id":0,"footnotes":""},"categories":[268],"tags":[380,392,473],"service":[],"class_list":["post-34360","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-programming","tag-javascript","tag-testing","tag-typescript"],"_links":{"self":[{"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/posts\/34360","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/users\/69"}],"replies":[{"embeddable":true,"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/comments?post=34360"}],"version-history":[{"count":0,"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/posts\/34360\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/media\/34372"}],"wp:attachment":[{"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/media?parent=34360"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/categories?post=34360"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/tags?post=34360"},{"taxonomy":"service","embeddable":true,"href":"https:\/\/hexboxdev.wpenginepowered.com\/wp-json\/wp\/v2\/service?post=34360"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}