Pester icon indicating copy to clipboard operation
Pester copied to clipboard

Write-Error in classes does not work with Should -Throw (in Codespaces)

Open fasteiner opened this issue 6 months ago • 4 comments

Checklist

What is the issue?

when running:

{ $ErrorActionPreference = "stop"; [notion_parent]::ConvertFromObject($obj) } | Should -Throw

in a codespace

and providing input that should lead to an error, I would suspect the test to pass, unfortunately it claims "no error thrown":

[-] Creates an error for unknown type 27ms (26ms|1ms) at { $ErrorActionPreference = "stop"; [notion_parent]::ConvertFromObject($obj) } | Should -Throw, /workspaces/Notion/tests/Unit/Classes/Parent/parent.Tests.ps1:89 at <ScriptBlock>, /workspaces/Notion/tests/Unit/Classes/Parent/parent.Tests.ps1:89 Expected an exception to be thrown, but no exception was thrown.

When catching it in try catch directly, it executes the catch block + if I set the erroraction to stop in the function of the class, the test passes (but we want the user to decide, if it should be terminating or not)

on windows in powershell 7 it leads to the desired result:

Image

Expected Behavior

The test should work in the Codespace (Linux) the same way as they do on windows.

Steps To Reproduce

In a codespace:

Define a class with a function, that uses write error (minified):

enum notion_parent_type
# https://developers.notion.com/reference/parent-object
{
    database_id
    page_id
    workspace
    block_id
}
class notion_parent
{
    #https://developers.notion.com/reference/parent-object
    [notion_parent_type]$type

    notion_parent()
    {
    }

    notion_parent([notion_parent_type]$type)
    {
        $this.type = $type
    }

    static [notion_parent] ConvertFromObject($Value)
    {
        $parent_obj = $null
        switch ($Value.type)
        {
            "database_id"
            {
                Write-Hot "database_id"
            }
            "page_id"
            {
                Write-Hot "page_id"
            }
            "workspace"
            {
                Write-Hot "workspace"
            }
            "block_id"
            {
                Write-Hot "block_id"
            }
            default
            {
                Write-Error "Unknown parent type: $($Value.type)" -Category InvalidData -RecommendedAction "Check the parent type and try again. Supported types are: database, page, workspace, block"
            }
        }
        return $parent_obj
    }
}

Write a test for the class

{ $ErrorActionPreference = "stop"; [notion_parent]::ConvertFromObject($obj) } | Should -Throw

Describe your environment

Codespace:

Pester version : 6.0.0-alpha5 /workspaces/Notion/output/RequiredModules/Pester/6.0.0/Pester.psm1
PowerShell version : 7.5.1 OS version : Unix 6.8.0.1027

Local Dev: Pester version : 6.0.0-alpha5 C:\Users\fasteiner\OneDrive - TTTECH COMPUTERTECHNIK AG\Documents\PowerShell\Modules\Pester\6.0.0\Pester.psm1 PowerShell version : 7.5.1 OS version : Microsoft Windows NT 10.0.26100.0

Possible Solution?

No response

fasteiner avatar Jun 23 '25 15:06 fasteiner

Thank you for the discussion at PSConfEU, issue and repro. This looks like a module scoping issue. Try setting a breakpoint inside your test and inside the class method.

Checking inside the It-block: Image

Breakpoint in the class method: Image

Modules have a separate session state for it's own variables, functions etc, so the value set outside is not inherited internally where Write-Error is called.

You can solve this by using a scriptblock that is bound to (executes in) the module's session state, meaning it can update the module-internal $ErrorActionPreference. E.g.:

$sb = InModuleScope -ModuleName Notion -ScriptBlock {
    # Just outputting a scriptblock so it originates from the module = is module bound
    { $ErrorActionPreference = "stop"; [notion_parent]::ConvertFromObject($obj) }
}
$sb | Should -Throw

fflaten avatar Jun 23 '25 17:06 fflaten

I'm not sure how it worked on Windows vs devcontainer, but that may be a different reason. I was not able to reproduce the success in Windows. Maybe you loaded the class manually (not by module import) in the Windows session?

fflaten avatar Jun 23 '25 17:06 fflaten

I'm not sure how it worked on Windows vs devcontainer, but that may be a different reason. I was not able to reproduce the success in Windows. Maybe you loaded the class manually (not by module import) in the Windows session?

Yes I indeed loaded the class manually, as I wanted to make a quick repro on Windows, to see if it was a general issue.

fasteiner avatar Jun 23 '25 21:06 fasteiner

You can solve this by using a scriptblock that is bound to (executes in) the module's session state, meaning it can update the module-internal $ErrorActionPreference. E.g.:

$sb = InModuleScope -ModuleName Notion -ScriptBlock { # Just outputting a scriptblock so it originates from the module = is module bound { $ErrorActionPreference = "stop"; [notion_parent]::ConvertFromObject($obj) } } $sb | Should -Throw

Yeah that works, thanks! Is there a particular reason this is needed only for classes?

Modules have a separate session state for it's own variables, functions etc, so the value set outside is not inherited internally where Write-Error is called.

But for functions it works? As soon as you loaded the module (and the classes from the module through accelerators), shouldn't be all in the same context?

fasteiner avatar Jun 24 '25 07:06 fasteiner