Skip to content

Reject configuration values we cannot use, warn about keys we do not know - #2982

Open
nohwnd wants to merge 1 commit into
mainfrom
nohwnd-strict-configuration
Open

Reject configuration values we cannot use, warn about keys we do not know#2982
nohwnd wants to merge 1 commit into
mainfrom
nohwnd-strict-configuration

Conversation

@nohwnd

@nohwnd nohwnd commented Aug 15, 2026

Copy link
Copy Markdown
Member

Fix #2975

Option 3 from the issue, which @fflaten picked.

A value the option cannot use now throws while the configuration is built:

[PesterConfiguration]@{ Run = @{ Parallel = 'yes' } }
# Run.Parallel expects a bool, but got the string 'yes'.

[PesterConfiguration]@{ Run = @{ Path = @{ a = 1 } } }
# Run.Path expects an array of strings, but got a hashtable.

[PesterConfiguration]@{ Run = 'nonsense' }
# Run expects a dictionary of options, but got the string 'nonsense'.

Nobody writes a value the option cannot read on purpose, so there is nothing to lose by being strict. It also covers the case where the key is spelled right, which is how values coming out of a JSON or psd1 file behave, they arrive as strings.

An unknown key is collected rather than thrown on, because a hashtable may carry keys meant for something else, and Invoke-Pester warns about all of them at once:

Invoke-Pester -Configuration @{ Nonsense = 1; Run = @{ Paralel = $true } }
# WARNING: Ignoring configuration keys 'Run.Paralel', 'Nonsense', there are no such options.
#          Check the spelling, 'Get-Help about_PesterConfiguration' lists all the options.

A key that is present but null still means "not set", so an unset variable does not throw (#2219).

How it works

PesterConfiguration collects the keys that match no section and no option, and exposes them with GetUnknownKeys(). A method and not a property, so it stays out of the console output of the configuration object. Merge carries them across, because Invoke-Pester merges onto the default before it reports anything.

The warning is emitted once, in Invoke-Pester. That is the funnel for every path (-Configuration @{ }, a configuration built by New-PesterConfiguration -Hashtable, and $PesterPreference = @{ } in a test file), so nothing is missed at run time and nobody gets the same warning twice. A bare [PesterConfiguration]@{ } outside a run stays silent, GetUnknownKeys() is there if you want to look.

Known option names come from reflection over the section's Option properties, so nothing has to be kept in sync by hand.

A key counts as known only when looking it up by the option's own name finds it. Comparing case-insensitively on its own is not enough, a dictionary with a case-sensitive comparer holds run without answering to Run, so the value would never be read and the key really is unknown.

Verification

test.ps1 passes, 2906 Pester tests and the P tests, no existing test relied on the silent behavior. Invoke-ScriptAnalyzer on src/Main.ps1 reports the same 9 pre-existing findings as main. 15 tests added to tst/PesterConfiguration.Tests.ps1 covering each rejected shape, null keeping the default, an int still being accepted for a decimal option, case-insensitive matching, the merge, and the three warning cases.

Docs are not updated yet, tell me if you want a page about this on pester.dev and I will send it to the docs repo.

🤖

…know

A configuration hashtable ignored anything it did not recognize and never
said a word, so a misspelled option left the run on the default with
nothing to notice, and so did a correct key holding a value of the wrong
type. The helpers read a failed cast as "not specified", which also hits
people who spelled everything right, because values from a JSON or psd1
file arrive as strings.

A value the option cannot use now throws while the configuration is built,
naming the option, what it expects and what it got. That is never
intentional, so there is nothing to lose by being strict.

An unknown key is collected instead, and Invoke-Pester warns about all of
them at once. A hashtable may carry keys meant for something else, so
throwing there could break a working setup.

A key that is present but null keeps meaning "not set" (#2219).

Fix #2975

🤖
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Configuration hashtable silently drops keys it does not recognize

1 participant