Apply Pester.BeforeContainer.ps1 from the repo root down to the test file - #2993
Conversation
…file Until now only a single Pester.BeforeContainer.ps1 in Run.RepoRoot was used, so a repository with different setup for unit and integration tests had to put both in that one file, or repeat them in every test file. Now every Pester.BeforeContainer.ps1 from Run.RepoRoot down to the test file's own folder is applied, outermost first, so shared setup lives at the root and the parts only some tests need live next to those tests. Requested by @johlju in #2772, where he estimated it removes around a thousand lines of duplication in SqlServerDsc. The setup files are dot-sourced into the container's own scope now, not into the run session state, which is what makes the folders actually scope anything. Before this, setup dot-sourced for one file stayed visible to every container after it, so a file in tests/integration would silently inherit whatever tests/unit had set up and the result depended on run order. A block can hold more than one of each setup and teardown as a result. The folder setup and the test file's own BeforeAll both register on the container's root block and have to compose instead of one of them erroring out: - Setup and teardown on Block are Pester.ScriptBlockCollection, a List<ScriptBlock> that renders as its contents, so a single one prints exactly what a plain ScriptBlock printed before and several print with the [n] numbering used for multiple errors. - Setups run in the order they were registered, teardowns in reverse. - A folder can opt out of everything above it with #pester:no-inherit, matched on real comment tokens like #pester:no-parallel. Same meaning as root = true in an .editorconfig. Useful for a folder like doc tests that needs its own cheap setup and should not pay for the expensive one. Parallel resolves the chain once in the parent and hands each worker the paths. Workers are separate runspaces that cannot share a cache, so letting them resolve would mean rediscovering the same folders over and over, in parallel. Breaking changes: - Two BeforeAll (or AfterAll, BeforeEach, AfterEach) in one block no longer throw. - Top level code in Pester.BeforeContainer.ps1 runs during discovery only, so it has to be in BeforeAll to reach the tests. Same rule a test file already follows, and it is what keeps a folder's setup out of the next container. - Stray output from a setup file no longer warns, it cannot escape the container to reach Invoke-Test anymore. Split-RSpecResult still covers the filtering itself. Note: resolving during the Find-FileInDirectory walk instead of walking up per folder, and reporting the applied setup files on the container, are still open. 🤖
…recontainer # Conflicts: # tst/Pester.RSpec.Parallel.ts.ps1
|
Needs better strategy for the walk up so we only parse each BeforeContainer once, and don't duplicate IO. Otherwise in summary, we made it behave like if you put BeforeDiscovery or BeforeAll on top of the file, to avoid inventing new execution modes, and also to avoid polluting the script scope. |
Which Pester.BeforeContainer.ps1 files apply is a property of the directory, so the cache belongs on the directory. It used to be keyed by the container's own folder, which meant every distinct test folder walked and tokenized the whole chain above it again. On a tree with 60 test folders and a 26 KB root setup file that is 845 ms of resolving, and all of it sits in front of the run, before any parallel worker starts. Get-PesterBeforeContainerChain now takes the cache, walks up only as far as the first directory that is already resolved, and records the list for every directory it passes on the way back down. Each directory is checked on disk once per run and each setup file is tokenized once per run. The same tree resolves in 23 ms. #pester:no-inherit does not need anything special. The opt-out belongs to the folder that carries it, so the truncated list is simply what gets cached for that folder, and folders below inherit the shorter list without looking above it again. Added tests for the cache entry per directory, for the walk stopping at an entry that is already there, and for the truncated chain being what gets cached under a no-inherit folder. 🤖
|
|
||
| $sb = { | ||
| param ($private:p, $private:d) | ||
| param ($private:p, $private:d, $private:setupFiles) |
|
Done in 62770f2. The cache moved off the container's folder and onto the directory, which is where it belongs, since which setup files apply is a property of the directory and not of the container.
On a tree with 60 test folders and a 26 KB root setup file that is 845 ms before and 23.6 ms after. All of it sat in front of the run, before any parallel worker started.
Added tests for the entry per directory, for the walk stopping at an entry that is already there, and for the truncated chain being what gets cached under a no-inherit folder. I also dropped the other open item, resolving during the 🤖 |
…recontainer # Conflicts: # tst/Pester.RSpec.Parallel.ts.ps1
Only a single
Pester.BeforeContainer.ps1inRun.RepoRootwas used, so a repository with different setup for unit and integration tests had to cram both into that one file, or repeat them in every test file. Now everyPester.BeforeContainer.ps1fromRun.RepoRootdown to the test file's own folder is applied, outermost first. Requested by @johlju in #2772, where he estimated it removes around a thousand lines of duplication in SqlServerDsc.The setup files are dot-sourced into the container's own scope now, not into the run session state. That is what makes the folders actually scope anything. Before this, setup dot-sourced for one file stayed visible to every container after it, so a file in
tests/integrationwould silently inherit whatevertests/unithad set up, and the result depended on run order.A block can hold more than one of each setup and teardown as a result, because the folder setup and the file's own
BeforeAllboth register on the container's root block and have to compose rather than one of them erroring out.BlockarePester.ScriptBlockCollection, aList<ScriptBlock>that renders as its contents. A single one prints exactly what a plainScriptBlockprinted before, several print with the[n]numbering already used for multiple errors.#pester:no-inherit, matched on real comment tokens like#pester:no-parallel. Same meaning asroot = truein an.editorconfig, for a folder like doc tests that needs its own cheap setup and should not pay for the expensive one.Which files apply is a property of the directory, not of the container, so the run shares one cache keyed by directory. Resolving a folder walks up only as far as the first directory already in the cache and takes that list, and records a list for every directory it passes on the way back down. Each directory is checked on disk once per run and each setup file is tokenized once per run, however many test folders sit below them. On a tree with 60 test folders and a 26 KB root setup file that is 23.6 ms instead of 845 ms.
Parallel resolves the chain once in the parent and hands each worker the paths. Workers are separate runspaces that cannot share a cache, so letting them resolve would mean rediscovering the same folders over and over, in parallel.
Breaking changes
BeforeAll(orAfterAll,BeforeEach,AfterEach) in one block no longer throw.Pester.BeforeContainer.ps1runs during discovery only, so it has to be inBeforeAllto reach the tests. Same rule a test file already follows, and it is what keeps a folder's setup out of the next container.Invoke-Testanymore.Split-RSpecResultstill covers the filtering itself.Tests
Added tests for the cascade sequentially and in parallel,
#pester:no-inheritsequentially and in parallel, composition with the file's ownBeforeAll, no leaking into a sibling folder, not walking above the repo root, and the ordering of multiple setups and teardowns. The four tests that asserted the old one-per-block errors are replaced by four asserting the new ordering.Still open
Resolving during the
Find-FileInDirectorywalk is off the list. That walk starts at the-Pathroots whileRun.RepoRootis found separately by searching for.git, so withInvoke-Pester -Path ./tests/unitit never seesreporoot/Pester.BeforeContainer.ps1. The walk up stays, and with the cache the disk checks it does are under a millisecond for a whole run.🤖