Using Pipeline.Input for Powershell testing

Last week, I posted about how to unit test powershell, which I have been working with a little bit over the weekend. One thing I quickly realized was testing interaction with the pipeline is a must for Powershell. It’s pretty easy to do.

Let’s start with the Get-Service cmdlet that ships with Powershell. If you load up reflector, you will see the follow class definition.

[Cmdlet("Get", "Service", DefaultParameterSetName="Default")]
public sealed class GetServiceCommand : MultipleServiceCommandBase
{
    // Methods
    public GetServiceCommand();
    protected override void ProcessRecord();

    // Properties
    [Parameter(Position=0, 
          ParameterSetName="Default", 
          ValueFromPipelineByPropertyName=true, 
          ValueFromPipeline=true),
     Alias(new string[] { "ServiceName" })]
    public string[] Name { get; set; }
}

Remembering the base test fixture from the previous post on TDD in Powershell, I can quickly write up a few tests that test a few different use cases of the get-service cmdlet with

[Test]
public void Get_Service_with_single_string_in_pipeline() 
{
    // "MySQL" | get-service

    var name = "MySQL";

    var pipeline = Runspace.CreatePipeline();

    pipeline.Input.Write(name);
    pipeline.Commands.Add("get-service");

    var result = pipeline.Invoke();

    AssertThatPipelineResultIsService(result, name);
}

[Test]
public void Get_Service_with_single_object_in_pipeline_using_Name_property()
{
    //New-Object PSObject | Add-Member NoteProperty Name "MySQL" -PassThru | get-service

    var name = "MySQL";

    var pipeline = Runspace.CreatePipeline();

    pipeline.Input.Write(new { Name = name });
    pipeline.Commands.Add("get-service");

    var result = pipeline.Invoke();

    AssertThatPipelineResultIsService(result, name);
}

[Test]
public void Get_Service_with_single_object_in_pipeline_using_ServiceName_property()
{
    //New-Object PSObject | Add-Member NoteProperty ServiceName "MySQL" -PassThru | get-service

    var name = "MySQL";

    var pipeline = Runspace.CreatePipeline();

    pipeline.Input.Write(new { ServiceName = name });
    pipeline.Commands.Add("get-service");

    var result = pipeline.Invoke();

    AssertThatPipelineResultIsService(result, name);
}

private void AssertThatPipelineResultIsService(Collection<psobject> result, string name)
{
    Assert.That(result.Count == 1);
    Assert.That(result[0].BaseObject is ServiceController);
    Assert.That((result[0].BaseObject as ServiceController).ServiceName == name);
}

By using pipeline.Input.Write(), I can write to the pipeline before invoking commands. Really easy to do, and super useful, because each Powershell cmdlet can probably be called a bunch of different ways.

Where can you use it? Snap in development or testing your own Powershell scripts. Stop guessing how the pipeline is going to work and test it.