Pester icon indicating copy to clipboard operation
Pester copied to clipboard

Mocking cmdlet with dynamic parameters not working

Open abhinitmodi opened this issue 10 years ago • 19 comments

Hey, I am trying to use an azure-commandlet which uses dynamic parameters. The commandlet is new-azureresourcegroupdeployment. I am mocking it in the following way

Mock New-AzureResourceGroupDeployment { throw "No appropriate mock called for New-AzureResourceGroupDeployment" }
Mock New-AzureResourceGroupDeployment -MockWith { return $validResourceGroupDeployment } -ParameterFilter{$Name -eq $validResourceGroupName -and $ResourceGroupName -eq $validResourceGroupName -and $TemplateFile -eq $validTemplatePath}

But when I run my tests, I get the exception:

Cannot retrieve the dynamic parameters for the cmdlet. Exception calling "GetDynamicParameters" with "0" argument(s): "Object reference not set to an instance of an object

Am I missing something. Could you please point me to an example where you have mocked a commandlet which has dynamic parameters. I was using the 3.3.6 version. I upgraded to 3.3.8 and tried again but was not successful.

Thanks

abhinitmodi avatar Apr 30 '15 14:04 abhinitmodi

I'll take a look at this later today when I have time. There have been a couple of instances of weird cmdlets that break the dynamic parameter mocking code (such as the AD module). Could be that there's something new happening in these Azure cmdlets that I need to support in Pester.

dlwyatt avatar Apr 30 '15 14:04 dlwyatt

Thanks dave. That would be helpful

abhinitmodi avatar Apr 30 '15 14:04 abhinitmodi

I haven't been able to reproduce this yet. This code doesn't give me the error you reported:

Describe 'Test' {
    Mock New-AzureResourceGroupDeployment { throw "No appropriate mock called for New-AzureResourceGroupDeployment" }

    It 'Works' {
        { New-AzureResourceGroupDeployment -ResourceGroupName Test } | Should Throw
    }
}

I tested this with the latest version of the Azure module, which was just released.

In the code you posted, you had a second version of the mock with a parameter filter, but the variables that the mock depends on were not included. If you can provide the values that you were assigning to those variables, I can try to run that version of the mock and see if it fails.

dlwyatt avatar Apr 30 '15 21:04 dlwyatt

I mocked the command with a parameter filter as well; still no problems, using the latest AzureResourceManager module and Pester 3.3.8.

dlwyatt avatar May 01 '15 00:05 dlwyatt

In my case the New-AzureResourceGroupDeployment cmdlet is used inside a function. And I am writing tests against that function mocking the behavior of the azure cmdlet. Would that make a difference?

abhinitmodi avatar May 01 '15 08:05 abhinitmodi

No, that shouldn't matter.

dlwyatt avatar May 01 '15 11:05 dlwyatt

Hi @dlwyatt, I think I'm encountering this same problem. And I think I have a repro:

Get-Command New-AzureRmResourceGroupDeployment | % ModuleName
[string](Get-Module AzureRM.Resources).Version
Write-Host Pester
[string](Get-Module Pester).Version

Describe 'Describe' {
    Mock New-AzureRmResourceGroupDeployment

    It 'this throws Exception calling "GetDynamicParameters"' {
        New-AzureRmResourceGroupDeployment -ResourceGroupName Test -TemplateFile Test
    }
}

which yields

image

Are you able to reproduce this problem on your machine?

alx9r avatar Oct 04 '16 04:10 alx9r

@alx9r that's the exact issue I'm seeing except I have Pester 3.4.0 The AzureRM.Resources module is 3.0.1, same as yours

@dlwyatt - I've got a Mock from AzureRM.Profiles for Add-AzureRmAccount that works fine but New-AzureRmResourceGroupDeployment cmdlet won't mock. I've tried both "Mock -ModuleName" & "InModuleScope -ModuleName" syntax with the same result. I haven't tried any other cmdlets yet

autonomousit avatar Oct 04 '16 05:10 autonomousit

I'm running into this problem now too.

adbertram avatar Jan 05 '17 22:01 adbertram

I'm running into this as well. I'm on 3.4.4. I'm receiving this error using the same command as referenced here. However, it has worked. I'm not sure what state I have to get this into to repro it. I'm using the latest AzureRM module (3.3.0) as well.

adbertram avatar Jan 12 '17 20:01 adbertram

I don't think this is Pester-version-dependent. I'm seeing the following results:

  • 3.0.0, 3.0.1, 3.0.1.1 -- no error
  • 3.0.2 and later -- error

Mocking dynamic parameters seems to have been implemented between 3.0.1.1 and 3.0.2.

Here is the repro code I'm using. It checks out each Pester version starting at 3.0.0 and tries to mock New-AzureRmResourceGroupDeployment.

alx9r avatar Jan 12 '17 23:01 alx9r

I have gotten it to work in 3.4.4. However, I'm unsure what state it was in when it did work.

On Thu, Jan 12, 2017 at 5:07 PM, alx9r [email protected] wrote:

I don't think this is Pester-version-dependent. I'm seeing the following results:

  • 3.0.0, 3.0.1, 3.0.1.1 -- no error
  • 3.0.2 and later -- error

Mocking dynamic parameters seems to have been implemented between 3.0.1.1 and 3.0.2 https://github.com/pester/Pester/compare/3.0.1.1...3.0.2.

Here is the repro code https://gist.github.com/alx9r/7749f3a0657941d0b5839128dcf19feb I'm using. It checks out each Pester version starting at 3.0.0 and tries to mock New-AzureRmResourceGroupDeployment.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/pester/Pester/issues/339#issuecomment-272312788, or mute the thread https://github.com/notifications/unsubscribe-auth/AEG3-uPiBiTUIzk7fEvWVPl5De2PITUrks5rRrIlgaJpZM4EMnAx .

adbertram avatar Jan 12 '17 23:01 adbertram

I'm encountering this issue with Pester 4.3.1 and PackageManagement\Get-PackageSource Here's a simple repro:

PS> New-Module -Name 'MockTest' -ScriptBlock {
    function Get-ThosePackageSources {
        Get-PackageSource -ProviderName 'NuGet' -ConfigFile $Env:APPDATA\NuGet\nuget.config
    }
}

PS> Describe 'Get-ThosePackageSources' {
    Mock Get-PackageSource -Module PackageManagement -MockWith {
        throw [System.NotImplementedException]::new('No mock yet')
    } -Verifiable

    It 'should invoke our Mock' {
        $null = Get-ThosePackageSources
        Assert-VerifiableMock
    }
}

ChadMcCaffery avatar Jul 24 '18 21:07 ChadMcCaffery

edit: This repro is incorrect, Get-PackageSource is called in the incorrect scope, see below. ~Here is even simpler repro. The Mock is not called.~

Import-Module PackageManagement

Describe 'Get-PackageSource' {
    It 'should invoke our Mock' {
        Mock Get-PackageSource -Module PackageManagement
    
        Get-PackageSource
        Assert-MockCalled Get-PackageSource -Module PackageManagement
    }
}
Describing Get-PackageSource
  [-] should invoke our Mock 37ms
    Expected Get-PackageSource in module PackageManagement to be called at least 1 times but was called 0 times
    7:         Assert-MockCalled Get-PackageSource -Module PackageManagement

nohwnd avatar Dec 14 '18 19:12 nohwnd

@nohwnd Is your last example valid? By using the -Module switch (I suppose you were meaning -ModuleName there), the mock you should apply to function calls within the PackageManagement module context, which is not the case in the test.

Am I missing something there?

renehernandez avatar May 16 '19 10:05 renehernandez

@renehernandez does it not work? -Module should be the same as using -ModuleName, because it will resolve to -ModuleName, unless there is an ambiguous parameter in the function. I don't think there is such parameter because the output is from the command directly. So the repro should be correct.

nohwnd avatar May 16 '19 12:05 nohwnd

@nohwnd The parameter thing was just a side topic :). My main question is: By using -Module or -ModuleName, we are saying that we want to mock the calls inside the module, right? But then, in the example, we are calling Get-PackageSource at the script scope, which it's different that the PackageManagement module scope.

renehernandez avatar May 16 '19 12:05 renehernandez

You are right, that mock is defined within the module but outside of the module it still binds to the cmdlet not the alias so the original command is called and not the mock. So the error correctly reports that the mock was not called.

nohwnd avatar May 16 '19 13:05 nohwnd

This came up again when I was working with New-AzResourceGroupDeployment. I am looking for various ways around this error, and perhaps can update documentation or point to link for fix? Gathering some information here.

When mocking New-AzResourceGroupDeployment and passing in custom parameters for the ARM template, the Pester 5 mock function does not discover the dynamic parameters. Since dynamic parameters are only available at runtime and not compile time, these are not discoverable. This results in following error:

Mock New-AzResourceGroupDeployment -MockWith ''
Cannot retrieve the dynamic parameters for the cmdlet

I think @renehernandez was working on this and put in a PR for Pester 5 which may have been rolled back? I couldn't find a good example to address this, so might make sense to update the error message to include an example if verbosity turned up, or provide path to fix.

I'd prefer not to ignore the module validation checker in Pester and extend to check those parameters I specify in the mock.

In this example, $adUserId and $secretValue are custom parameters, not defined explicitly in the code for the cmdlet.

$outputs = New-AzResourceGroupDeployment `
    -WhatIf `
    -SkipTemplateParameterPrompt `
    -DeploymentName $deploymentName `
    -ResourceGroupName $ResourceGroup `
    -TemplateFile $TemplateFile `
    -TemplateParameterFile $TemplateParameterFile `
    -adUserId $adUserId `
    -secretValue $secretValue `
    -DeploymentDebugLogLevel All -Verbose `

I found some related items here. https://github.com/pester/Pester/pull/1317 https://github.com/pester/Pester/issues/1308 https://gordon.byers.me/azure/templateparameterobject-parameter-azurermresourcegroupdeployment/ https://github.com/MicrosoftDocs/feedback/issues/3073 https://github.com/Azure/azure-powershell/issues/7980 https://github.com/Azure/azure-powershell/issues/4186

Source for the cmdlet, related articles and some potential bugs people may hit. https://github.com/Azure/azure-powershell/blob/56b4197ff38c83ba8d8c240b0e23cbbe39c4305a/src/Resources/ResourceManager/Implementation/ResourceGroupDeployments/NewAzureResourceGroupDeploymentCmdlet.cs https://github.com/Azure/azure-powershell/issues/1528

In the cmdlet, there is (or was?) an option for passing in a TemplateParameterObject. I think I used that to get around this issue last time. It's mentioned recently. https://github.com/Azure/azure-powershell/issues/14170

It would be great if mock could enable extending the success of discovery to include parameters we specify with their types, if this feature isn't already in place.

asears avatar Feb 21 '21 13:02 asears