Recently I started working on a light weight unit testing
framework for my PowerShell modules. There are a lot of testing frameworks for
PowerShell that can be executed as script files from a PS host, but not many
allows you to integrate with VSTests and write test methods and classes in C#.
The following posts in this series is about how you can create a unit testing
framework for Windows PowerShell and use it.
Being a big fan of the Page objects pattern and have seen the
benefits of how easily you can model a page and reduce the amount of duplicate
code when creating UI tests for websites, I wanted to do something similar modules
for PowerShell also. So when I started writing the framework, one of the main
considerations was to simplify my testing process by modelling a PowerShell
module in code.
I also wanted to follow a more declarative approach on
defining the metadata needed to provide inputs for my tests and model, so I started
to think about attributes that I need to model a PowerShell module.
To define a module
name and the location of the module, I created the PsModuleAttribute with a
Name and a Path property, so that I can use this attribute on my PSModule model
for the ModuleObject pattern implementation.
[AttributeUsage(AttributeTargets.Class)]
public class PsModuleAttribute : Attribute
{
public PsModuleAttribute(string name)
{
Name = name;
}
[Required(AllowEmptyStrings
= false,
ErrorMessage = "A PS module name
should be provided to test a module")]
public string Name { get; set; }
public string Path { get; set; }
}
Next I wanted to define the functions and the parameters for
these functions in my model. The functions in the PowerShell module can be simulated
as properties in the ModuleObject. Once you have these properties defined, you
can use the same approach of using attributes to define name, parameters,
return value etc.
[AttributeUsage(AttributeTargets.Property)]
public class PsModuleFunctionAttribute : Attribute
{
[Required(AllowEmptyStrings
= false,
ErrorMessage = "A module should have
a name")]
public string Name { get; set; }
public PsModuleFunctionAttribute(string name)
{
Name = name;
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class PsModuleParameterAttribute : Attribute
{
[Required(AllowEmptyStrings
= false,
ErrorMessage = "A parameter should
have a name")]
public string Key { get; set; }
[Required(AllowEmptyStrings
= false,
ErrorMessage = "A parameter should
have a value")]
public string Value { get; set; }
public PsModuleParameterAttribute(string key, string value)
{
Key = key;
Value = value;
}
}
In the framework, I created the CommandInfo object to wrap
these values in the properties.
public class PsCommandInfo
{
public string Name { get; set; }
public IDictionary Parameters { get; set; }
public Collection<PSObject> Result { get; set; }
}
The final implementation of the ModuleObject should look
like.
[PsModule("xModule", Path = @"E:\MyModules\xModule")]
public class XModule
{
[PsModuleFunction("Get-HelloMessage")]
[PsModuleParameter("context", "VSTest")]
public PsCommandInfo GetGreetings { get; set; }
}
Next we’ll see how to extract this information in a unit test
context to execute it.