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
How to test Invoke-Command with external cmdlets #791
Comments
I think I follow what you are trying to do. Please forgive me if I've missed something important about your goals; I'm guessing at some of them here. Here's how I would set this up: function Wrapper-Command
{
Param( $Param )
$splat = @{
ComputerName = 'MyServerName'
ArgumentList = $Param
Scriptblock = {
param($Param)
External-Command $Param
}
}
return Invoke-Command @splat
}
function SomeFancyFunction
{
Param($Param)
# ... code of some complexity
Wrapper-Command -Param $Param
# ... maybe some more code of some complexity
}
Describe 'SomeFancyFunction' {
Context 'basic usage' {
Mock -CommandName Wrapper-Command { 'mocked return value' }
It 'returns correct value' {
$r = SomeFancyFunction -Param 'test parameter value'
$r | Should be 'mocked return value'
}
It 'correctly invokes Wrapper-Command' {
Assert-MockCalled Wrapper-Command 1 {
$Param -eq 'test parameter value'
}
}
}
} That puts most of the complexity into |
Thank you @alx9r, that was very helpful! I realized that most of my "code of some complexity" was inside my Invoke-Command ScriptBlock. However, after your reply I realized that most of it can be extracted into a local function, and only the part that needs to be remoted can be inside of the Invoke-Command wrapper, like in the example you gave. I guess the takeaway is that an external cmdlet that needs to be run on a remote computer cannot be mocked, or is very difficult to mock (still not sure how yet). If you do find a way to do that, let me know. But otherwise, I am satisfied with extracting most of the logic out like you proposed. |
@b4tt1enet You're welcome. You could inject a "command invoker" (see dependency inversion principle). In production you would inject a "command invoker" that uses the real I used dependency injection with Pester in this project. In that case the behavior of the dependency was simple enough to use a mock. In this case I think you'd probably need to unit test the behavior of the "command invoker" stub itself to make sure it meaningfully emulates the real "command invoker" that uses |
I'm facing some trouble trying to write unit tests for some code, and maybe you guys can help.
I need to remote into a different server and run the code, because that server has modules and cmdlets that I currently do not have (nor should I have). However, I have been unable to mock calls to those external cmdlets, because I get the Pester error: "Could not find Command 'External-Command'". This makes sense because my machine does not have the "External-Command" cmdlet.
I tried making a wrapper function for "External-Command", and mocking that, which makes my unit tests work fine, but it makes my Invoke-Command call fail because the server does not know about my wrapper function. Putting the wrapper function inside of the top-level function makes the Invoke-Command work, but breaks the unit tests (which is expected, as per #247). Does this make sense?
I made private functions just so I could test the logic without having to worry about Invoke-Command. But I think the behavior would be the same if I took out the private functions and put the code inside the ScriptBlock parameter.
Here's a small example.
Script file:
Unit test:
And changing the script/test file to this makes the unit test pass, but breaks the code:
Script file:
Unit test:
Is there a way I can make this work? It seems like a difficulty between managing Invoke-Command remoting nuances and Pester mocking nuances.
Thanks for any help!
The text was updated successfully, but these errors were encountered: