Add Run.BeforeInvoke bootstrap for Invoke-Pester#2772
Conversation
Introduce a Run.BeforeInvoke option that runs optional bootstrap code in the caller's scope as soon as Invoke-Pester starts, before the caller's $PesterPreference is read and before discovery. Use it to import dependencies and to provide configuration by defining or modifying $PesterPreference, which Pester then picks up as the caller preference. Two sources, mirroring the container-level convention: - Run.BeforeInvoke config option (scriptblock[]). When set, it wins. - Convention file: the first Pester.BeforeInvoke.ps1 found when walking up from each Run.Path towards Run.RepoRoot is dot-sourced (deduped, in order, never escaping the repo root). Each scriptblock is bound to the caller's SessionState and dot-sourced so imports, defined functions and $PesterPreference land in the caller's scope. Bootstrap runs for top-level runs only, so nested Pester-in-Pester does not re-run it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
I really like this, looking forward to this merging. I will reduce a lot of duplicated code in test files by having a If it would go up to the repo root and find all and run them in sequence we could even reduce even more duplication, for example It would then invoke them in the order top to bottom:
But just having the option of one Pester.BeforeInvoke.ps1 would probably reduce the duplication with ~1000 rows in a larger project (like in SqlServerDsc). |
|
@johlju -rc1 is out, it has similar feature, BeforeContainer, my main problem is that we cannot just promise that it will run once, because if you need to run tests in parallel (there is experimental parallel mode), this script needs to run in every new runspace. And I wanted to minimize the overhead of looking the file up every time, and did not want to think about how to say "this is in child folder, but don't run any of the script files you find above me" like "root=true" in .editorconfig. but all those are good suggestions, show me how they simplify your workflow, and we could at least add them as options, if not as defult. I just did not want to over design and over promise in the initial release. |
|
It says in the release notes för rc1:
Assuming we can pass
A single file would not handle different setup unless it can pass current test script file so the code But I will check how far the current functionality takes us and report the GAP. If we can pass |
I actually don't know. I did not think about that, my thinking here was that it imports modules you need, adds helper functions etc. stuff that I typically do in a "test.ps1" script. But that now needs to happen in every child runspace. All in all the idea of what I want to achieve here is not super clear even to me. My goal is also that I can run tests (e.g. a single
You could probably do that through the power of powershell, e.g. by looking at your pscall stack, but I get what you mean. |
That is why there are extensive bootstrap in each file in SqlServerDsc - so there our goal ids aligned 🙂 also I need to use Invoke-PesterJob in VS Code (and outside) for tests if they load classes so I do not need to kill the session each time we re-run test. |
Summary
Adds a
Run.BeforeInvokeoption that runs optional bootstrap code in the caller's scope, as soon asInvoke-Pesterstarts — before the caller's$PesterPreferenceis read and before discovery. Use it to import dependencies and to provide configuration by defining or modifying$PesterPreference, which Pester then picks up as the caller preference.This mirrors the container-level
BeforeContainerconvention, but for the top-level invocation itself.How it works
Two sources, in priority order:
Run.BeforeInvokeconfig option (scriptblock[]). When set, it wins and runs as-is.Pester.BeforeInvoke.ps1found when walking up from eachRun.PathtowardRun.RepoRootis dot-sourced (deduped, in order, never escaping the repo root).Each scriptblock is bound to the caller's
SessionStateand dot-sourced, so imported modules, defined functions, and$PesterPreferenceland in the caller's scope whereInvoke-Pesterreads them next. The bootstrap runs for top-level runs only, so nested Pester-in-Pester does not re-run it.Changes
BeforeInvokeScriptBlockArrayOptiononRunConfiguration(mirrorsRepoRoot/ScriptBlock).src/functions/Pester.BeforeInvoke.ps1—Resolve-PesterBeforeInvoke+Invoke-PesterBeforeInvoke.Main.ps1— resolves a preliminary preference and runs the bootstrap before the caller-preference read.BeforeInvokeentry inabout_PesterConfiguration.tst/Pester.BeforeInvoke.ts.ps1(9 P-tests). Full suite green.Open design question (reason this is a draft)
trueBeforeInvokecurrently runs once, in the orchestratingInvoke-Pestercall. For a parallel run, test containers execute in separate runspaces that would not have re-run the bootstrap — so dependency setup done here would be missing in the workers. Configuration ($PesterPreference) is inherently a once-per-invocation concern and is fine, but dependency provisioning may need to also happen per-container (e.g. viaBeforeContainer) for parallel scenarios. Want to settle this before merging.