indented-automation / Indented.StubCommand

MIT License
23 stars 4 forks source link

Problem using Assert-MockCalled when dynamic parameter is using type from module #20

Open johlju opened 5 years ago

johlju commented 5 years ago

The stub cmdlet Move-ADDirectoryServer passes a string in the parameter Site. But the parameter Site is of the type Microsoft.ActiveDirectory.Management.ADReplicationSite which is a stub type. When using an assert like below, the ToString() method does not return anything, neither does $Site.Name which is a property of the type Microsoft.ActiveDirectory.Management.ADReplicationSite.

Example of assert that fails.

Assert-MockCalled -CommandName Move-ADDirectoryServer -Times 1 -ParameterFilter {
    $Site.ToString() -eq $correctSiteName
}

The stub cmdlet looks like this.

function Move-ADDirectoryServer {
    <#
    .SYNOPSIS
        Move-ADDirectoryServer [-Identity] <ADDirectoryServer> [-Site] <ADReplicationSite> [-WhatIf] [-Confirm] [-AuthType <ADAuthType>] [-Credential <pscredential>] [-Server <string>] [<CommonParameters>]
    #>

    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkId=219321')]
    param ( )

    dynamicparam {
        $parameters = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary

        # AuthType
        $attributes = New-Object System.Collections.Generic.List[Attribute]

        $attribute = New-Object System.Management.Automation.ParameterAttribute
        $attributes.Add($attribute)

        $parameter = New-Object System.Management.Automation.RuntimeDefinedParameter("AuthType", [Microsoft.ActiveDirectory.Management.ADAuthType], $attributes)
        $parameters.Add("AuthType", $parameter)

        # Credential
        $attributes = New-Object System.Collections.Generic.List[Attribute]

        $attribute = New-Object System.Management.Automation.ParameterAttribute
        $attributes.Add($attribute)

        $attribute = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute
        $attributes.Add($attribute)

        $attribute = New-Object System.Management.Automation.CredentialAttribute
        $attributes.Add($attribute)

        $parameter = New-Object System.Management.Automation.RuntimeDefinedParameter("Credential", [System.Management.Automation.PSCredential], $attributes)
        $parameters.Add("Credential", $parameter)

        # Identity
        $attributes = New-Object System.Collections.Generic.List[Attribute]

        $attribute = New-Object System.Management.Automation.ValidateNotNullAttribute
        $attributes.Add($attribute)

        $attribute = New-Object System.Management.Automation.ParameterAttribute
        $attribute.Position = 0
        $attribute.Mandatory = $True
        $attribute.ValueFromPipeline = $True
        $attributes.Add($attribute)

        $parameter = New-Object System.Management.Automation.RuntimeDefinedParameter("Identity", [Microsoft.ActiveDirectory.Management.ADDirectoryServer], $attributes)
        $parameters.Add("Identity", $parameter)

        # Server
        $attributes = New-Object System.Collections.Generic.List[Attribute]

        $attribute = New-Object System.Management.Automation.ValidateNotNullOrEmptyAttribute
        $attributes.Add($attribute)

        $attribute = New-Object System.Management.Automation.ParameterAttribute
        $attributes.Add($attribute)

        $parameter = New-Object System.Management.Automation.RuntimeDefinedParameter("Server", [System.String], $attributes)
        $parameters.Add("Server", $parameter)

        # Site
        $attributes = New-Object System.Collections.Generic.List[Attribute]

        $attribute = New-Object System.Management.Automation.ParameterAttribute
        $attribute.Position = 1
        $attribute.Mandatory = $True
        $attributes.Add($attribute)

        $attribute = New-Object System.Management.Automation.ValidateNotNullAttribute
        $attributes.Add($attribute)

        $parameter = New-Object System.Management.Automation.RuntimeDefinedParameter("Site", [Microsoft.ActiveDirectory.Management.ADReplicationSite], $attributes)
        $parameters.Add("Site", $parameter)

        return $parameters
    }

    end {
        throw '{0}: StubNotImplemented' -f $MyInvocation.MyCommand
    }
}

The stub type ADReplicationSite looks like this.

    public class ADReplicationSite
    {
        // Constructor
        public ADReplicationSite() { }
        public ADReplicationSite(System.String identity) { }
        public ADReplicationSite(System.Guid guid) { }
        public ADReplicationSite(Microsoft.ActiveDirectory.Management.ADObject identity) { }
        public ADReplicationSite(Microsoft.ActiveDirectory.Management.ADDirectoryServer directoryServer) { }

        // Property
        public System.String DistinguishedName { get; set; }
        public System.String Name { get; set; }
        public System.String ObjectClass { get; set; }
        public System.Nullable<System.Guid> ObjectGuid { get; set; }
        public System.Collections.ICollection PropertyNames { get; set; }
        public System.Collections.Generic.ICollection<System.String> AddedProperties { get; set; }
        public System.Collections.Generic.ICollection<System.String> RemovedProperties { get; set; }
        public System.Collections.Generic.ICollection<System.String> ModifiedProperties { get; set; }
        public System.Int32 PropertyCount { get; set; }
        public Microsoft.ActiveDirectory.Management.ADPropertyValueCollection Item { get; set; }
    }

So the above assert failed because ToString() does not exist in the stub type, and $Site.Name does not contain the string value passed to the parameterSite, of the cmdlet Move-ADDirectoryServer, because there are no logic in the stub classADReplicationSite` to handle that.

If I add logic to the class ADReplicationSite constructor that takes a string as argument, and change the test code to assert on $Site.Name then the assert works. I could have added the ToString() method to return the Site.Name property, but that is more uncertain if that is what the real ToString() method does.

Changed logic in the class ADReplicationSite. Just showing the relevant parts.

    public class ADReplicationSite
    {
        // Constructor
        ...
        public ADReplicationSite(System.String identity) { this.Name = identity; }
        ...

        // Property
        ...
        public System.String Name { get; set; }
        ...
    }

Changed logic in the assert in the test.

Assert-MockCalled -CommandName Move-ADDirectoryServer -Times 1 -ParameterFilter {
    $Site.Name -eq $correctSiteName
}
johlju commented 5 years ago

I don't think this one can be resolved? I thought I share my findings if someone else stumbles on the same problem, or if there are someone that can figure out a clever solution (other than manually change the code after the fact). 🙂