How to unit test a PowerShell Core binary cmdlet in C#

Question

I've written a simple PowerShell cmdlet in C# to show off the problem I'm encountering. Feel free to clone the repo, or fork it and get it working and submit a PR, or just look at the source code to see exactly what I am doing.

I've created a simple PowerShell Core cmdlet using the PowerShellStandard.Library NuGet package. I'm using xUnit and am trying to run a unit test against the PowerShell cmdlet that I've created. The problem is that when I call the .Invoke() method of the cmdlet instance, it throws a NullReferenceException.

Here's the cmdlet I've created:

[Cmdlet(VerbsCommon.Get, "RepeatedString")]
[OutputType(typeof(string))]
public class GetRepeatedStringCmdlet : Cmdlet
{
    [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)]
    [Alias("Word")]
    [ValidateNotNullOrEmpty()]
    public string Phrase { get; set; }

    [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true)]
    [Alias("Repeat")]
    public int NumberOfTimesToRepeatPhrase { get; set; }

    protected override void ProcessRecord()
    {
        base.ProcessRecord();

        var result = new StringBuilder();
        for (int i = 0; i < NumberOfTimesToRepeatPhrase; i  )
        {
            result.Append(Phrase);
        }

        WriteObject(result.ToString());
    }
}

Here's an example of a unit test I'm trying to run:

[Fact]
public void ShouldReturnThePhraseRepeatedTheCorrectNumberOfTimes()
{
    // Arrange.
    var phrase = "A test phrase.";
    int numberOfTimesToRepeat = 3;
    var cmdlet = new GetRepeatedStringCmdlet()
    {
        Phrase = phrase,
        NumberOfTimesToRepeatPhrase = numberOfTimesToRepeat
    };
    var expectedResult = Enumerable.Repeat(phrase, numberOfTimesToRepeat);

    // Act.
    var enumerator = cmdlet.Invoke().GetEnumerator(); // NullReferenceException thrown here when calling .Invoke().
    Assert.True(enumerator.MoveNext());
    var results = enumerator.Current;

    // Assert.
    Assert.Equal(results, expectedResult);
}

When I test the script in PowerShell it does appear to work correctly:

ImportModule .\PowerShellCmdletInCSharpExample.dll
Get-RepeatedString -Phrase "Hello there" -NumberOfTimesToRepeatPhrase 3
Hello thereHello thereHello there

I'm following examples I've found on some blog posts, like this one and this one, but I guess they don't have this issue when calling .Invoke(). Other blog posts, like this one, use a PsCmdletAssert class to .Invoke() the cmdlet, but that class does not seem to exist in the PowerShellStandard.Library NuGet package, so I'm guessing it's a not a PowerShell Core friendly class. Other blogs create a new runspace and pipeline on every test execution, but again, I don't seem to have the CreatePipeline() function when using the PowerShellStandard library.

So my question is, how can I run xUnit tests against my PowerShell Core Cmdlets that I create in C# in order to verify they function as expected?

Thanks in advance!


Update

Using the .Net Framework version of the System.Management.Automation.dll does not throw an exception when Invoke()ing the cmdlet, so my work around has been to define my unit test project as a .Net Framework project, instead of .Net Core. This has allowed me to unit test the cmdlet as expected using the code shown in this post. The cmdlet is still defined in a .Net Core project and is cross-platform, only the unit tests are not.

Currently the latest version of the PowerShellStandard.Library NuGet package is 5.1.0, so hopefully this issue gets addressed in a future version.

Solution

I experienced this same problem. I was able to resolve this by installing Microsoft.PowerShell.SDK into my unit test project, whereupon my Cmdlet.Invoke() and Powershell.Create() calls began working correctly.