Search code examples
powershelldscpester

Testing a powershell DSC script class with pester - Unable to find type [ClassName]


I am trying to test some custom DSC resources written as a class with the help of pester. However I am struggling to work out how to make the class available to a different file. This is not a pester issue really, I can not do this in powershell either. I have a module with a class in it much like the following in a SxDeployment.psm1 file

[DscResource()]
class SxWindowsService {
    [void]Set(){
    }
    [bool]Test(){
        return $true;
    }
    [SxWindowsService]Get(){
        return $this
    }
}

This module has a corresponding .psd1, which is listing the SxWindowsService class as a 'DscResourcesToExport'. It is being registered as a module, I can see this module when I do a Get-Module -ListAvailable and can also perform an Import-Module upon it.

I suppose my question really is how can I create a a reference to this class from another file?

Say I have a different file test.ps1 with the following

Import-Module SxDeployment

$class = [SxWindowsService]::new()

I get the following error, "Unable to find type [SxWindowsService]."

UPDATE After a little more tinkering, I can create an instance of the class by changing the .psm1 file to a .ps file and changing the import-module statement for . .\SxDeployment.ps1. So it seems the problem is how do you consume resources defined in a DSC resource module file outside of a DSC configuration?


Solution

  • The type literal [SxWindowsService] won't be accessible outside the module. This is by design. Here is a quote from the release notes:

    Class keyword >>> Defines a new class. This is a true .NET Framework type. Class members are public, but only public within the module scope. You can't refer to the type name as a string (for example, New-Object doesn't work), and in this release, you can't use a type literal (for example, [MyClass]) outside the script/module file in which the class is defined.

    Edit:

    Well after having said the above. Since there is a bit of a mismatch between object activation in .NET and PowerShell then I thought to myself, there might just be a way. And I found it:

    Import-Module C:\ps\StackOverflow\32743812\issue.psm1 -Force
    function New-PSClassInstance {
        param(
            [Parameter(Mandatory)]
            [String]$TypeName,
            [object[]]$ArgumentList = $null
        )
        $ts = [System.AppDomain]::CurrentDomain.GetAssemblies() |
            Where-Object Location -eq $null | 
                Foreach-Object {
                    $_.Gettypes()
                } | Where-Object name -eq $TypeName |
                    Select-Object -Last 1
        if($ts) {
            [System.Activator]::CreateInstance($ts,$ArgumentList )
        } else {
            $typeException = New-Object TypeLoadException $TypeName        
            $typeException.Data.Add("ArgumentList",$ArgumentList)
            throw $typeException
        }
    }
    New-PSClassinstance -TypeName SxWindowsService