IT Praktyk Blog

Mock Exchange objects with Pester

Posted May 11, 2016; Last update June 02, 2016

Introduction

When you write complicated PowerShell tools used with Exchange Server you need call specific Exchange Servers cmdlets e.g. Get-Mailbox, Get-ExchangeServer etc. Those commands are available only if you are using remote sessions to Exchange Server and additionally some (most?) your tools are intended to change mail objects or an environment settings so you can’t do everything if you don’t have a lab. Even in the lab some scenarios need be simulated by creating special objects or configuration. Resolution for that can be Mock function from Pester PowerShell module - help for function you can find here.

When I first time heard about Pester and I started read about that I was confused how Exchange objects can be mocked. It is like some kind of inception or mind-looping - you are using PowerShell function to test code written in PowerShell with objects generated by PowerShell. Some time took me to understand how I can mock Exchange objects and after the first fail public try - I wrote about that here - I wrote the first working Exchange object and the first working tests.

Action

Exchange objects are usually complicated, for example

[PS] > Get-ExchangeServer | Get-Member  Group-Object -Property membertype

Count Name                      Group
----- ----                      -----
    7 Method                    {System.Object Clone(), bool Equals(System.Object obj), int GetHashCode(), System.Ob...
    2 NoteProperty              {System.String PSComputerName=nleegex01.ethosenergygroup.com, System.Guid RunspaceId...
   47 Property                  {Microsoft.Exchange.Data.ServerVersion AdminDisplayVersion {get;}, System.String Cur...

so mocking that in every Pester test can be ineffective so I started from write the function New-MockedExchangeServer


<#
    .SYNOPSIS
    Function intended to create ExchangeServer object to use with Mock in Pester

    .DESCRIPTION
    Function intended to create ExchangeServer object to use with Mock in Pester.

    .PARAMETER Name
    Name of Exchange server.

    .PARAMETER AdminDisplaVersion
    Value for AdminDisplaVersion .

    .PARAMETER IsClientAccessServer
    Set to true if server has installed Client Access Role.

    .PARAMETER IsFrontendTransportServer
    Set to true if server has installed Client Access Role.

    .PARAMETER IsHubTransportServer
    Set to true if server has installed Mailbox Role.

    .PARAMETER IsMailboxServer
    Set to true if server has installed Mailbox Role.

    .PARAMETER IsUnifiedMessagingServer
    Set to true if server has installed Mailbox Role.

    .PARAMETER IsEdgeServer
    Set to true if a server is a Edge.

    .EXAMPLE
    PS > New-MockedExchangeServer | Get-Member

    TypeName: System.Management.Automation.PSCustomObject

    Name                      MemberType   Definition
    ----                      ----------   ----------
    Equals                    Method       bool Equals(System.Object obj)
    GetHashCode               Method       int GetHashCode()
    GetType                   Method       type GetType()
    ToString                  Method       string ToString()
    AdminDisplayVersion       NoteProperty string AdminDisplayVersion=Version 15.0 (Build 1178.4)
    IsClientAccessServer      NoteProperty bool IsClientAccessServer=True
    IsEdgeServer              NoteProperty bool IsEdgeServer=False
    IsFrontendTransportServer NoteProperty bool IsFrontendTransportServer=True
    IsHubTransportServer      NoteProperty bool IsHubTransportServer=True
    IsMailboxServer           NoteProperty bool IsMailboxServer=True
    IsUnifiedMessagingServer  NoteProperty bool IsUnifiedMessagingServer=True
    Name                      NoteProperty string Name=EX-1

    .NOTES
    AUTHOR: Wojciech Sciesinski, wojciech[at]sciesinski[dot]net
    KEYWORDS: PowerShell,Pester,Mock,Exchange

    VERSIONS HISTORY
    0.2.0 -  2016-05-10 - initial working version

    LICENSE
    Copyright (c) 2016 Wojciech Sciesinski
    This function is licensed under The MIT License (MIT)
    Full license text: https://opensource.org/licenses/MIT

#>
function New-MockedExchangeServer {
    [CmdletBinding(DefaultParameterSetName = 'IsNonEdgeServer')]
    [OutputType('System.Management.Automation.PSObject')]
    param (
        [Parameter(ParameterSetName = 'IsEdgeServer',
                   ValueFromPipelineByPropertyName = $true)]
        [Parameter(ParameterSetName = 'IsNonEdgeServer',
                   ValueFromPipelineByPropertyName = $true)]
        [String]$Name = 'EX-1',
        [Parameter(ParameterSetName = 'IsEdgeServer')]
        [Parameter(ParameterSetName = 'IsNonEdgeServer')]
        [String]$AdminDisplaVersion = 'Version 15.0 (Build 1178.4)',
        [Parameter(ParameterSetName = 'IsNonEdgeServer')]
        [bool]$IsClientAccessServer = $true,
        [Parameter(ParameterSetName = 'IsNonEdgeServer')]
        [bool]$IsFrontendTransportServer = $true,
        [Parameter(ParameterSetName = 'IsNonEdgeServer')]
        [bool]$IsHubTransportServer = $true,
        [Parameter(ParameterSetName = 'IsNonEdgeServer')]
        [bool]$IsMailboxServer = $true,
        [Parameter(ParameterSetName = 'IsNonEdgeServer')]
        [bool]$IsUnifiedMessagingServer = $true,
        [Parameter(ParameterSetName = 'IsEdgeServer')]
        [ValidateSet($true)]
        [bool]$IsEdgeServer
    )

    Process {

        switch ($PsCmdlet.ParameterSetName) {

            'IsEdgeServer' {

                $properties = @{
                    'Name' = $Name;
                    'AdminDisplayVersion' = $AdminDisplaVersion;
                    'IsEdgeServer' = $true;
                    'IsClientAccessServer' = $false;
                    'IsFrontendTransportServer' = $false;
                    'IsHubTransportServer' = $false;
                    'IsMailboxServer' = $false;
                    'IsUnifiedMessagingServer' = $false
                }

            }

            'IsNonEdgeServer' {

                $properties = @{
                    'Name' = $Name;
                    'AdminDisplayVersion' = $AdminDisplaVersion;
                    'IsEdgeServer' = $false;
                    'IsClientAccessServer' = $IsClientAccessServer;
                    'IsFrontendTransportServer' = $IsFrontendTransportServer;
                    'IsHubTransportServer' = $IsHubTransportServer;
                    'IsMailboxServer' = $IsMailboxServer;
                    'IsUnifiedMessagingServer' = $IsUnifiedMessagingServer
                }

            }

        }


        $ExchangeServer = New-Object -TypeName System.Management.Automation.PSObject -Property $properties

    }

    End {

        Return $ExchangeServer

    }

}

Additionally I wrote the simple script saved as Simple-Exchange-Script.ps1

function Simple-Exchange-Script {

    $TestExchangeServer = Get-ExchangeServer

    if ($TestExchangeServer.IsClientAccessServer) {

        Return "The server is Client Access Server"

    }
    else {

        Return "The server is not Client Access Server"

    }

}

and the Pester tests saved as Simple-Exchange-Script.Tests.ps1


Import-Module Pester

Import-Module C:\Users\Wojtek\Documents\Scripts\1-MyOnGitHub\Pester-ExchangeServer\Functions\New-MockedExchangeServer.ps1 -Force

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
. "$here\$sut"

Describe "Simple-Exchange-Script" {

    Context -Name 'Server is CAS' {

        function Get-ExchangeServer { }

        Mock -CommandName Get-ExchangeServer -MockWith { New-MockedExchangeServer -IsClientAccessServer $false }

        It "Server is CAS" {

            Simple-Exchange-Script | Should Be "The server is Client Access Server"

        }

    }
}

Results

Result 1

Result 2

When I changed the part of Pester test to

Mock -CommandName Get-ExchangeServer -MockWith { New-MockedExchangeServer -IsClientAccessServer $false }

test failed :-)

Summary

Probably in the next days I’ll try create more functions used to mocking Exchange objects and I’ll re-factor some my previously wrote codes.

Resources

  1. PowerShell + DevOps Global Summit 2016 - Test Driven Development with Pester Blender - https://www.youtube.com/watch?v=jvvh9cpD_LM
  2. Iain’s Brighton blog post on VirtualEngine - Mocking Missing Cmdlets with Pester - http://virtualengine.co.uk/2015/mocking-missing-cmdlets-with-pester/

Remarks

  1. The examples was prepared and tested with
    • PowerShell version: 5.0.10586.117
    • Pester version: 3.4.0
  2. Codes are also available on GitHub in the repository Codes-for-Blog-It-Praktyk-PL.