Skip to content

Instantly share code, notes, and snippets.

@DigitalAXPP
Last active November 10, 2023 06:30
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DigitalAXPP/935255e8ec984f3e24245386f491bcf1 to your computer and use it in GitHub Desktop.
Save DigitalAXPP/935255e8ec984f3e24245386f491bcf1 to your computer and use it in GitHub Desktop.
The gist lists both options to test PowerShell cmdlets in C#.

PowerShell testing in C#

PowerShell cmdlets can be written in C# by inheriting either form the Cmdlet class or the PSCmdlet class. In short, the difference between both classes are the level of dependence to the PowerShell runspace. The Cmdlet-class is less tighly coupled to the PowerShell runspace, than the PSCmdlet class. A more in-depth comparison between both classes can be found here.

One important difference between both classes are the requirements for automated testing. The Cmdlet class can be easily invoked in C#, while the PSCmdlet class requires to be run in a PowerShell runspace. A great description about the two testing variations can be found in the course by Nathan Honeycutt "PowerShell Cmdlet Development in C# - The Ins and Outs".

Testing for the Cmdlet class

In general, I use for automated testing the XUnit framework.

[Fact]
public void ConvertJWTTest()
{
  //-- Arrange
  var jwt = "{imaging long JWT string}";
  # You create a variable containing the Cmdlet.
  var cmdlet = new ConvertFromJWTCmdlet()
  {
    JWT = jwt
  };
  
  //-- Act
  # When invoking the cmdlet, the return will always be an IEnumerable.
  var actual = cmdlet.Invoke().OfType<Hashtable>().ToList();

  //-- Assert
  Assert.IsType<Hashtable>(actual[0]);
}

Testing for the PSCmdlet class

The PSCmdlet class requires a PowerShell runspace to run the commands.

[Fact]
public void NewJWTHMAC()
{
  //-- Arrange
  var secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
  var claim = new Hashtable()
  {
    { "name", "DigitalAXPP" },
    { "age", 21},
    { "date", DateTimeOffset.Now.AddHours(3).ToUnixTimeSeconds() }
  };
  var regex = @"(^[\w-]*\.[\w-]*\.[\w-]*$)";
  # Creating your initial session, plus adding the to-be tested cmdlet
  var initialSessionState = InitialSessionState.CreateDefault();
  initialSessionState.Commands.Add(
    new SessionStateCmdletEntry("New-JWT", typeof(NewJWTCmdlet), null)
  );
  # Creating the runspace and opening a PowerShell session
  using (var runspace = RunspaceFactory.CreateRunspace(initialSessionState))
  {
    runspace.Open();
    using (var powershell = PowerShell.Create())
    {
      powershell.Runspace = runspace;

  //-- Act
      # Starting the command and adding all required arguments, plus eventually invoking the cmdlet
      var newJWTCommand = new Command("New-JWT");
      newJWTCommand.Parameters.Add("Secret", secret);
      newJWTCommand.Parameters.Add("Payload", claim);
      newJWTCommand.Parameters.Add("Algorithm", Algorithm.HS512);

      powershell.Commands.AddCommand(newJWTCommand);
      var result = powershell.Invoke<string>();

  //-- Assert
      Assert.IsType<string>(result[0]);
      Assert.Matches(regex, result[0]);
    }
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment