Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Select-Object and New-MockObject - Discarded PSObject #2393

Open
3 tasks done
chrisdent-de opened this issue Sep 27, 2023 · 2 comments
Open
3 tasks done

Select-Object and New-MockObject - Discarded PSObject #2393

chrisdent-de opened this issue Sep 27, 2023 · 2 comments

Comments

@chrisdent-de
Copy link

chrisdent-de commented Sep 27, 2023

Checklist

What is the issue?

When writing unit tests, and mocking complex objects, I want to make use of New-MockObject.

New-MockObject wires up members to the PSObject wrapper, hiding the real properties (or methods). This is a good thing.

New-MockObject avoids Add-Member to achieve this, likely because Add-Member is not regarded as being particularly fast.

This has an unfortunate side-effect if Select-Object is being used in the test subject's code. Select-Object replaces the PSObject wrapper on the selected object, discarding NoteProperty members added via PSObject.Properties.

https://github.com/PowerShell/PowerShell/blob/master/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Select-Object.cs#L573

Expected Behavior

The following tests describe the expected behaviour. All tests should pass:

$container = New-PesterContainer -ScriptBlock {
    Context 'When using PSObject.Properties.Add' {
        BeforeAll {
            $mockObject = New-MockObject -Type object -Properties @{
                Nested = New-MockObject object -Properties @{
                    Name = 'Value'
                }
            }    
        }

        It 'allows access to the property using the member dereference operator' {
            $mockObject.Nested.Name | Should -Be 'Value'
        }

        It 'allows access to the property using Select-Object' {
            # This test currently fails.
            $mockObject |
                Select-Object -ExpandProperty Nested |
                Select-Object -ExpandProperty Name |
                Should -Be 'Value'
        }
    }

    Context 'When using Add-Member' {
        BeforeAll {
            $addMember = New-MockObject -Type object | Add-Member -PassThru -NotePropertyName 'Nested' -NotePropertyValue (
                New-MockObject object | Add-Member -PassThru -NotePropertyName 'Name' -NotePropertyValue 'Value'
            )
        }

        It 'allows access to the property using the member dereference operator' {
            $mockObject.Nested.Name | Should -Be 'Value'
        }

        It 'allows access to the property using Select-Object' {
            # This test currently fails.
            $addMember |
                Select-Object -ExpandProperty Nested |
                Select-Object -ExpandProperty Name |
                Should -Be 'Value'
        }
    }
}
Invoke-Pester -Container $container -Output Detailed

Steps To Reproduce

This problem is exhibited by any object accessed by Select-Object where members have been added using .PSObject.Properties.

$object = [object]::new()
$object.PSObject.Properties.Add(
    [PSNoteProperty]::new(
        'Nested',
        [object]::new()
    )
)
$object.Nested.PSObject.Properties.Add(
    [PSNoteProperty]::new(
        'Name',
        'Value'
    )
)
# This works
$object.Nested.Name
# this doesn't
$object | Select-Object -ExpandProperty Nested | Select-Object -ExpandProperty Name

It is an unfortunate side effect of this problem which affects Pester. However, Pester is the far easier "thing to fix" than PowerShell, so Add-Member may be used to work-around this problem.

Describe your environment

Pester version     : 5.4.0 C:\Development\_modules\Pester\5.4.0\Pester.psm1
PowerShell version : 7.3.6
OS version         : Microsoft Windows NT 10.0.19045.0

Possible Solution?

No response

@fflaten
Copy link
Collaborator

fflaten commented Sep 27, 2023

Thanks for the detailed report and troubleshooting. Will need to compare and benchmark later, but initial thought is that we could probably use Add-Member in New-MockObject specifically. As it's an optional helper, the performance savings of the current .NET approach are not worth the trouble. Hopefully there's no major side effects of changing.

@nohwnd
Copy link
Member

nohwnd commented Oct 16, 2023

We use the property access directly for performance, if that has bugs that we cannot fix, I am okay with reverting back to Add-Member under New-MockObject. @chrisdent-de Please consider PRing this. Please use the add-member from safeCommands table.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants