powershellmockingpester

Create an instance in PowerShell of a .NET type without a constructor


I have a PowerShell script for setting up a website in IIS. It includes the following function:

function Set-IisWebsiteBinding (
    [Parameter(Mandatory = $true)]
    [string]$SiteName, 
    
    [Parameter(Mandatory = $true)]
    [Microsoft.Web.Administration.Site]$Website,
    
    [Parameter(Mandatory = $true)]
    [array]$WebsiteBindingSettings
)
{
    # Set up website bindings ...
}

I'm trying to write tests for the function in Pester. I'd like to create a dummy $Website object to pass to the function under test. The problem is that the Microsoft.Web.Administration.Site type has no constructor. How can I create an object of type Microsoft.Web.Administration.Site in PowerShell if it has no constructor?

I could create one via IISServerManager.Sites.CreateElement() but I'd like to avoid having to depend on using a real IISServerManager. Alternatively, I could get rid of the type coercion for the function parameter $Website, to allow the test code to pass in a hashtable instead of a Microsoft.Web.Administration.Site object. However, I'd prefer to keep the type coercion so it's obvious to future maintainers of the script what the type of the $Website object is.

Is there any way around this lack of a constructor without using a real IISServerManager or removing the type coercion?


Solution

  • Pester provides an easy option to create an object for a type that doesn't offer a public constructor via New-MockObject:

    $instance = New-MockObject -Type ([Microsoft.Web.Administration.Site])
    

    Alternatively you can use the same API used by Pester to create it, FormatterServices.GetUninitializedObject(Type).

    In both cases as noted in comments and still worth adding is that you will need to have the IISAdministration Module or its assembly loaded.

    Add-Type '
    public class MyType
    {
        private MyType() { }
    }'
    
    $instance = [System.Runtime.Serialization.FormatterServices]::GetUninitializedObject([MyType])
    $instance.GetType()
    
    #    Namespace: 
    # 
    # Access        Modifiers           Name
    # ------        ---------           ----
    # public        class               MyType : object
    

    If you're curious, the method essentially uses an internal API in RuntimeType: RuntimeHelpers.CoreCLR.cs#L330-L345. Not sure what it uses for .NET Framework, code is closed source.

    $methodInfo = [MyType].GetType().GetMethod(
        'GetUninitializedObject',
        [System.Reflection.BindingFlags] 'NonPublic, Instance')
    
    $methodInfo.Invoke([MyType], @())