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.