Open Bidthedog opened 3 years ago
We don't have PowerShell 7 on our systems yet but here are some small tips I can give you regarding the code you posted:
Should -Invoke Get-Content -Times 0
The code above simply means: I want the CmdLet Get-Content
to be invoked at least 0 times or more. What I think you are trying to asses is that Get-Content
is not called at all. A better, more correct, way would be:
Should -Invoke Get-Content -Times 0 -Exactly
Should -Not -Invoke Get-Content
Then in your module you use this code:
Format-XML $xml.OuterXml |
Out-File -FilePath $outputWebConfigPath -Encoding utf8NoBOM -Force
The default value for -Encoding
in PowerShell 7.1 is already UTF8NoBOM
. So better would be to simply not define it at all:
Format-XML $xml.OuterXml |
Out-File -FilePath $outputWebConfigPath -Force
Can you give this a try? It might just resolve your error:
PSInvalidCastException: Cannot convert the "utf8NoBOM" value of type "System.String" to type "System.Text.Encoding".
One last tip: it's faster to use $null = Get-Stuff
than Get-Stuff | Out-Null
. But that's just me nit picking ;)
Thanks for your reply - and thanks for clearing up the use of -Invoke
, I've just migrated from Assert-MockCalled
and the documentation is lacking somewhat.
I know it's not been long since I posted, but I've already rewritten this to use the TestDrive:\
feature as I couldn't wait for a fix, so I don't need to mock any longer; instead I'm asserting that the file exists and the content is what I expect it to be. The issue still stands, though - there is a problem mocking Out-File
for this use case. I could leave it off, but I prefer to be explicit in my code (it did not used to be the default [it didn't used to exist] - this is a breaking change moving between PS and PS Core). I expect the same behaviour will occur if I used utf8BOM
or any of the other new options.
I was not aware of the $null = Get-Stuff;
syntax either - thanks, though I think I'll leave that as is as it's not causing any issues, and it's much easier to read :)
Awesome! Whatever floats your boot :)
If you would like us still to have a deeper look at the Out-File
issue, would you be able to produce a small preproduction so we don't need to look though a bunch of irrelevant code? That would help us narrowing it down to either Pester or somethig else.
If you want to leave at this feel free to close the ticket.
No problem - I'll add it to my "todo" list and get on to it as soon as I can.
The encoding parameter does not work because powershell is using this trick to convert the string value you provided, into the actual System.Text.Encoding value based on a table of values. This allows you to provide a value that is not a correct System.Text.Encoding, and the engine will convert it when it parses the code.
If you look at the signature of the cmdlet you can see that there is no parameter set that expects a string to be provided to the Encoding parameter, and if you look at System.Text.Encoding you won't find utf8NoBOM there.
We don't have access to this trick because it is internal api. So you see the exception.
[System.Text.Encoding]"utf8nobom"
InvalidArgument: Cannot convert the "utf8nobom" value of type "System.String" to type "System.Text.Encoding".
The only way you can solve this now is by removing the parameter type, using this Mock Out-File -RemoveParameterType Encoding
.
As for the assertion, I would use Should -Not -Invoke Get-Content
.
Thank you @nohwnd - I knew it must be doing some magic, but I just couldn't figure out where the code that did it was! That's good to know.
Thanks for the Mock Out-File -RemoveParameterType Encoding
trick too, I'll give that a go!
To fix this issue implementer of the fix would need to detect that we are on PowerShell 7 or newer, and Encoding parameter is emitted and rewrite it to take string with parameter set coming from the internal encoding table.
We have an example of a similar operation here: https://github.com/pester/Pester/blob/f7e2067ccfbf75e423037e6d32024b6e2870d875/src/functions/Mock.ps1#L1541-L1551
Where we rename known conflicting parameters with their non-conflicting name.
And in the code below that https://github.com/pester/Pester/blob/f7e2067ccfbf75e423037e6d32024b6e2870d875/src/functions/Mock.ps1#L1555-L1574 where parameter types are changed and validation is removed.
The fix for the issue above would be similar, it would also replace one metadata with other, using string type, and adding validation instead of removing it.
I was able to work around the parameter issue like this:
$encoding = [System.Text.Encoding]::UTF8
Write-Output "Hello World" | Out-File -Encoding $encoding -FilePath pathToFile
The other problem I ran into was Out-File never reported being called when I mocked it. I tried with and without -Verifiable and different attempts with Should -Invoke and Assert-VerifiableMock. No matter what it reported Out-File was not being called.
When I commented out my mock I saw the file get written to disk so I know the mock was getting called when the code was in place. I might have to use the testdrive as well as I am not sure why I can't mock Out-File.
Did you get any errors when setting up that mock? It seems that the mock was not set, or maybe you created a mock in different module than from where it was called? Can you share a repro if you have one?
Ugh! I must have done something wrong. I went to put the code back to mock Out-File and it is working. I wish I knew what I did wrong before. Sorry for distraction.
If anyone is running into this there are two possible solutions: https://github.com/pester/Pester/issues/1877#issuecomment-802135815 - Ignore parameter type in mock https://github.com/pester/Pester/issues/1877#issuecomment-826127321 - change your code to use the encoding directly instead of the shortcut string
General summary of the issue
When mocking
Out-File
and invoking it in code usingOut-File -FilePath $path -Encoding utf8NoBOM -Force;
, Pester produces this error when executing a test:'utf8NoBom' is a new value available when using Out-File with PowerShell Core. They updated it to make Out-File (and various other cmdlets) actually useful to Windows users (previously BOMs would always be output when using UTF8, causing no end of encoding issues). It seems that the
Encoding
parameter now accepts more than just an enum / string value, which is where I expect this issue is coming from - possible that Pester's mocking functions don't cater for this?I have been trying to find the specification of this column in PowerShell Core (OSS github code and MS's documentation) but all I've found are these links (which aren't all that useful as I assume that Out-File cmdlet is doing some input param management before it calls any .Net functions):
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/out-file?view=powershell-7.1#parameters
https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding?view=net-5.0
https://docs.microsoft.com/en-us/dotnet/api/system.text.encoding.codepage?view=netcore-2.2
Describe your environment
Pester version : 5.0.2 C:\Users\****\Documents\PowerShell\Modules\Pester\5.0.2\Pester.psm1 PowerShell version : 7.1.3 OS version : Microsoft Windows NT 10.0.18363.0
Steps to reproduce
All code for my specific implementation here (it's in a PS module):
Pester test code here. First test passes (does not hit the code in question - mocks aren't required but included for completeness), second test fails regardless of the assertion params specified:
Expected Behavior
I expect to be able to arbitrarily mock Out-File and "ignore" its underlying operation so I can test various cmdlet inputs without writing files to disk. At present the only way I can really run these tests are by outputting the files into the temp dir and cleaning them up afterwards.