Mock to New-ADUser failing

637 views
Skip to first unread message

Dexter Dhami

unread,
Oct 24, 2015, 6:54:17 AM10/24/15
to Pester
Hi everyone,

Facing an issue with Pester.
I have a very simple function named New-ADUserFromTemplate (wrapper around New-ADuser) which tries to create a new user using the -Instance parameter on the cmdlet. Below is the definition

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
function New-ADUserFromTemplate {

   param(
        # SPecify the unique SamAccountName for the User
        [Parameter(Mandatory)]                    
        [ValidateNotNullOrEmpty()]
        [string]$SamAccountName, 
        
        # Specify the First Name or Given name for the user
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [String]$GivenName,

        [Parameter(Mandatory)]
        #[PSTyepName('Microsoft.ActiveDirectroy.Management.ADUser')]
        [Object]$Instance,

        [Switch]$Passthru
        )

        TRY {
            
            if ( -not (Get-Module -Name ActiveDirectory) ) {
                # try to import the Module
                Import-Module -name ActiveDirectory -ErrorAction stop
                $null = Get-PSDrive -Name AD -ErrorAction stop  # Query if the AD PSdrive is loaded
            }
        
        }
        CATCH [System.IO.FileNotFoundException]{
            Write-Warning -Message $_.exception 
            throw "AD module not found"
        }
        CATCH {
            throw $_.exception
        }              

        # Let's start by following the link : https://technet.microsoft.com/en-us/library/dd378959(v=ws.10).aspx
        $Instance.UserPrincipalName = $Null

        if ($Passthru.IsPresent) {        
            New-ADuser -Name $SamAccountName -SamAccountName $SamAccountName -GivenName $GivenName -Instance $Instance -Enabled $False -Passthru
        }
        else {
            New-ADuser -Name $SamAccountname -SamAccountName $SamAccountName -GivenName $GivenName -Instance $Instance -Enabled $False
        }       
     
}

Now I have a single Pester test which tries to mock the User creation and assert that the New-ADUser was called.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")

. "$here\$sut"

Describe "New-ADUserFromTemplate" -Tags 'UnitTest'{  
  
  Context "User Creation" {
        
        It "Should return object when -Passthru specified" {
            $TemplateUser = [PSCustomObject]@{Name='templateuser';UserPrincipalName='templa...@dex.com'}
            Mock -CommandName New-ADuser  -MockWith {@{name='testuser'}}  -Verifiable
            $CreatedUser = New-ADUserFromTemplate -GivenName 'test 123' -SamAccountName 'test123' -Instance $TemplateUser -Passthru 
            Assert-VerifiableMocks # Assert that our verifiable mock for New-ADuser cmdlet was called.
            $Createduser | Should Not BeNullOrEmpty
            
        }
    }
}

But both of these fail terribly when I try to run them on a Machine with AD PowerShell module installed. Below is what I see in the error messages. 


PS>invoke-pester -Tag unittest
Executing all tests in 'C:\Users\Administrator\Documents\WindowsPowerShell\Pester_tests\new-aduserfromtemplate'
Describing New-ADUserFromTemplate
   Context User Creation
    [-] Should return object when -Passthru specified 257ms
      Cannot bind parameter 'Instance'. Cannot create object of type "Microsoft.ActiveDirectory.Management.ADUser". The adapter cannot set the value of property "Name".
      at line: 52 in C:\Users\Administrator\Documents\WindowsPowerShell\Pester_tests\new-aduserfromtemplate\New-ADUserFromTemplate.Tests.ps1
Tests completed in 1.33s
Passed: 0 Failed: 1 Skipped: 0 Pending: 0

Can anyone suggest on what might be the issue here ? I am guessing it has to do something specifically to the -Instance parameter implementation for the New-ADUser cmdlet.

Regards
Dex

Dave Wyatt

unread,
Oct 24, 2015, 10:22:12 AM10/24/15
to Dexter Dhami, Pester
That's a big challenge with mocking right now.  When the parameters of a cmdlet are strongly typed (such as here, with the ADUser object), and you want to pass in a mock of those objects, it can be difficult (or impossible) to get the mock working.

In this particular case, it's doable, because the ADUser class has public constructors.  However, the behavior of the class is a bit weird (particularly with regard to the Name property, which for whatever reason is defined as read-only, but a workaround exists to set it anyway).  A bit of trial and error turned up this as a potential solution for setting up your template user object:

# Instead of this:

$TemplateUser = [PSCustomObject]@{Name='templateuser';UserPrincipalName='templa...@dex.com'}

# Try this:
$TemplateUser = New-Object Microsoft.ActiveDirectory.Management.ADUser
$TemplateUser.Item('Name').Value = 'templateuser'
$TemplateUser.UserPrincipalName = 'templa...@dex.com'

--
You received this message because you are subscribed to the Google Groups "Pester" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pester+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dexter Dhami

unread,
Oct 25, 2015, 12:18:54 AM10/25/15
to Pester, deepaksi...@gmail.com
Hey Dave,

Thanks for the workaround. I had one workaround ready where I create a dynamic module in the BeforeEach{} block for the Context.
This module is named ActiveDirectory and has New-ADUser function exported, so the Mock sees the function instead of the cmdlet.
But the downside to this is that the -Instance which was typecasted as Microsoft.ActiveDirectory.Management.ADUser was to be replaced by [Object] , I could add a dummy class using Add-type too but am just lazy at this point. 

The benefit I think of this is that the Pester will run independent of the AD module but downside is in order to mimic behavior of how New-ADUser cmdlet behaves it will require me to work on the dummy function New-ADUser.

Do you think this approach of creating a dummy module is useful ? 

Regards,
Dex


On Saturday, October 24, 2015 at 7:52:12 PM UTC+5:30, Dave Wyatt wrote:
That's a big challenge with mocking right now.  When the parameters of a cmdlet are strongly typed (such as here, with the ADUser object), and you want to pass in a mock of those objects, it can be difficult (or impossible) to get the mock working.

In this particular case, it's doable, because the ADUser class has public constructors.  However, the behavior of the class is a bit weird (particularly with regard to the Name property, which for whatever reason is defined as read-only, but a workaround exists to set it anyway).  A bit of trial and error turned up this as a potential solution for setting up your template user object:

# Instead of this:
$TemplateUser = [PSCustomObject]@{Name='templateuser';UserPrincipalName='templateuser...@dex.com'}

Dave Wyatt

unread,
Oct 25, 2015, 12:21:12 AM10/25/15
to Dexter Dhami, Pester
Yep, that's a solid technique as well (though I generally just define a function, rather than putting it into a module.)

On Sun, Oct 25, 2015 at 12:18 AM, Dexter Dhami <deepaksi...@gmail.com> wrote:
Hey Dave,

Thanks for the workaround. I had one workaround ready where I create a dynamic module in the BeforeEach{} block for the Context.
This module is named ActiveDirectory and has New-ADUser function exported, so the Mock sees the function instead of the cmdlet.
But the downside to this is that the -Instance which was typecasted as Microsoft.ActiveDirectory.Management.ADUser was to be replaced by [Object] , I could add a dummy class using Add-type too but am just lazy at this point. 

The benefit I think of this is that the Pester will run independent of the AD module but downside is in order to mimic behavior of how New-ADUser cmdlet behaves it will require me to work on the dummy function New-ADUser.

Do you think this approach of creating a dummy module is useful ? 

Regards,
Dex

On Saturday, October 24, 2015 at 7:52:12 PM UTC+5:30, Dave Wyatt wrote:
That's a big challenge with mocking right now.  When the parameters of a cmdlet are strongly typed (such as here, with the ADUser object), and you want to pass in a mock of those objects, it can be difficult (or impossible) to get the mock working.

In this particular case, it's doable, because the ADUser class has public constructors.  However, the behavior of the class is a bit weird (particularly with regard to the Name property, which for whatever reason is defined as read-only, but a workaround exists to set it anyway).  A bit of trial and error turned up this as a potential solution for setting up your template user object:

# Instead of this:
$TemplateUser = [PSCustomObject]@{Name='templateuser';UserPrincipalName='templa...@dex.com'}

Dexter Dhami

unread,
Oct 25, 2015, 3:17:52 AM10/25/15
to Pester, deepaksi...@gmail.com
Thanks Dave,

Linking my blog post around the topic to here.

I have few other questions and will be asking them here.

Regards,
Dex


On Sunday, October 25, 2015 at 9:51:12 AM UTC+5:30, Dave Wyatt wrote:
Yep, that's a solid technique as well (though I generally just define a function, rather than putting it into a module.)
On Sun, Oct 25, 2015 at 12:18 AM, Dexter Dhami <deepaksi...@gmail.com> wrote:
Hey Dave,

Thanks for the workaround. I had one workaround ready where I create a dynamic module in the BeforeEach{} block for the Context.
This module is named ActiveDirectory and has New-ADUser function exported, so the Mock sees the function instead of the cmdlet.
But the downside to this is that the -Instance which was typecasted as Microsoft.ActiveDirectory.Management.ADUser was to be replaced by [Object] , I could add a dummy class using Add-type too but am just lazy at this point. 

The benefit I think of this is that the Pester will run independent of the AD module but downside is in order to mimic behavior of how New-ADUser cmdlet behaves it will require me to work on the dummy function New-ADUser.

Do you think this approach of creating a dummy module is useful ? 

Regards,
Dex

On Saturday, October 24, 2015 at 7:52:12 PM UTC+5:30, Dave Wyatt wrote:
That's a big challenge with mocking right now.  When the parameters of a cmdlet are strongly typed (such as here, with the ADUser object), and you want to pass in a mock of those objects, it can be difficult (or impossible) to get the mock working.

In this particular case, it's doable, because the ADUser class has public constructors.  However, the behavior of the class is a bit weird (particularly with regard to the Name property, which for whatever reason is defined as read-only, but a workaround exists to set it anyway).  A bit of trial and error turned up this as a potential solution for setting up your template user object:

# Instead of this:
$TemplateUser = [PSCustomObject]@{Name='templateuser';UserPrincipalName='templateuser...@dex.com'}
Reply all
Reply to author
Forward
0 new messages