I need to "Pester-test" 2 Azure cmdlets, Get-AzureNetworkSecurityGroup and Set-AzureNetworkSecurityRule from a PowerShell function inside a module, that looks like following:
$nsg = Get-AzureNetworkSecurityGroup -Name $NsgName
Set-AzureNetworkSecurityRule -Name $NsgRule.name `
-Type Outbound `
# ... other properties here ...
-NetworkSecurityGroup $nsg |
Format-List -Property Name,Location,Label
The parameters $NsgName, $NsgRule are not so important, the problem is I receive errors when mocking Set-AzureNetworkSecurityRule like:
Mock Get-AzureNetworkSecurityGroup { return [PSCustomObject] @{ Name='Any' } }
Mock Set-AzureNetworkSecurityRule
Mock Format-List
The error says:
[-] Error occurred in Describe block 100ms
PSInvalidCastException: Cannot convert the "@{Name=Any}" value of type "System.Management.Automation.PSCustomObject" to type "Microsoft.WindowsAzure.Commands.ServiceManagement.Network.NetworkSecurityGroup.Model.INetworkSecurityGroup".
ArgumentTransformationMetadataException: Cannot convert the "@{Name=Any}" value of type "System.Management.Automation.PSCustomObject" to type "Microsoft.WindowsAzure.Commands.ServiceManagement.Network.NetworkSecurityGroup.Model.INetworkSecurityGroup".
ParameterBindingArgumentTransformationException: Cannot process argument transformation on parameter 'NetworkSecurityGroup'. Cannot convert the "@{Name=Any}" value of type "System.Management.Automation.PSCustomObject" to type "Microsoft.WindowsAzure.Commands.ServiceManagement.Network.NetworkSecurityGroup.Model.INetworkSecurityGroup".
It's quite clear what's happening, the issue is I don't know how to mock an object of type INetworkSecurityGroup. My expectation was initially to be no problem if mocking both Azure cmdlets.
I've also tried mocking Set-AzureNetworkSecurityRule using -MockWith:
Mock Set-AzureNetworkSecurityRule -MockWith {@{NetworkSecurityGroup='test-stuff'}}
with no luck.
Can anyone point me to the right direction ?
Thanks in advance
UPDATE with full Describe statement
First try
$environmentConfig = (Get-AzureEnvironmentConfig "Staging")
Describe 'Set-NetworkSecurityRuleFromObject' {
$role = ($environmentConfig.roles | Where { $_.role_name -eq 'Web'})
$nsgName = Get-NetworkSecurityGroupName -EnvironmentConfig $environmentConfig -Role $role
$rule = $role.nsg_rules.outbound[0]
Mock Get-AzureNetworkSecurityGroup
Mock Set-AzureNetworkSecurityRule
Set-NetworkSecurityRuleFromObject -NsgName -$nsgName -NsgRule $rule -NsgRuleType "Outbound"
It 'Should call mocked functions at least once' {
Assert-MockCalled Get-AzureNetworkSecurityGroup -Times 1 -Scope Describe
Assert-MockCalled Set-AzureNetworkSecurityRule -Times 1 -Scope Describe
}
}
Associated PS module function call:
Get-AzureNetworkSecurityGroup -Name $NsgName |
Set-AzureNetworkSecurityRule -Name $NsgRule.name `
-Type $NsgRuleType `
# More parameter initialization here ...
-Protocol $NsgRule.protocol |
Format-List -Property Name,Location,Label
Second try, another implementation of PS function that didn't work:
$nsg = Get-AzureNetworkSecurityGroup -Name $NsgName
$nsg |
Set-AzureNetworkSecurityRule -Name $NsgRule.name `
-Type $NsgRuleType `
# More parameter initialization here ...
-Protocol $NsgRule.protocol |
Format-List -Property Name,Location,Label
Third try
Describe 'Set-NetworkSecurityRuleFromObject' {
[ ... ]
Mock Get-AzureNetworkSecurityGroup { return [PSCustomObject] @{ Name='Any' } }
Mock Set-AzureNetworkSecurityRule
Set-NetworkSecurityRuleFromObject -NsgName -$nsgName -NsgRule $rule -NsgRuleType "Outbound"
It 'Should call mocked functions at least once' {
Assert-MockCalled Get-AzureNetworkSecurityGroup -Times 1 -Scope Describe
Assert-MockCalled Set-AzureNetworkSecurityRule -Times 1 -Scope Describe
}
}
This is still one of the most challenging things to do with Pester. You need to mock Get-AzureNetworkSecurityGroup
in such a way that it returns a valid object to be passed to Set-AzureNetworkSecurityGroup
later, and figuring out just how to do that can sometimes be tricky. In this specific case, it's not too bad:
Mock Get-AzureNetworkSecurityGroup {
return New-Object Microsoft.WindowsAzure.Commands.ServiceManagement.Network.NetworkSecurityGroup.Model.SimpleNetworkSecurityGroup('Any', 'someLocation', 'someLabel')
}
Since this function parameter is an interface type, you could also write a quick class in C# (or in PowerShell v5) which implements that interface; sometimes this might be more desirable, if the actual class starts doing "stuff" as soon as you create an instance. However, this SimpleNetworkSecurityGroup
class doesn't really do anything other than hold data, and it's safe to use as-is. (Verified with ILSpy.)