One of the tools I’ve used the last half of a year and really enjoyed is the C# specification extension methods when writing unit test assertions. If you’re looking for a little more background on the topic, I wrote about Fluent Specification Extensions in a past blog.
Recently I wanted to execute a PowerShell script to do some automated functional testing. I wanted to execute an application and apply some assertions on the output of the software(basically running a console app, parse the xml output and assert on values in the output).
FYI: I’m very new to PowerShell, so any suggestions on how I implemented the below are welcome…
I’ve seen a couple examples of writing test assertions in PowerShell out there. One example is PSUnit; however, this seemed a little heavy for my needs and not quite the syntactic sugar I was looking for.
Besides the syntax flavor I was desiring, another thing I wanted to do was leverage the power of NUnit.Framework’s assertion capabilities. I like the error messages generated when strings and other objects fail the assertion.
Examples of end result ShouldLookLike()…
$true.ShouldBeTrue()
$false.ShouldBeFalse()
"a".ShouldEqual("a")
"a".ShouldNotEqual("b")
Step 1: Figure out how to write a C# style extension method in PowerShell.
I found a great blog post describing how to extend any PowerShell object to add extension methods.
In short, to extend types in PowerShell leveraging the Extended Type System, you need to define them in an xml file and import the method definitions into the PowerShell runtime instance.
Below is PowerShell XML definition for my NUnit Specification Extensions.
<?xml version="1.0" encoding="utf-16"?>
<Types>
<Type>
<Name>System.Object</Name>
<Members>
<ScriptMethod>
<Name>ShouldBeFalse</Name>
<Script>
[NUnit.Framework.Assert]::IsFalse($this)
</Script>
</ScriptMethod>
<ScriptMethod>
<Name>ShouldBeTrue</Name>
<Script>
[NUnit.Framework.Assert]::IsTrue($this)
</Script>
</ScriptMethod>
<ScriptMethod>
<Name>ShouldEqual</Name>
<Script>
[NUnit.Framework.Assert]::AreEqual($args[0], $this)
</Script>
</ScriptMethod>
<ScriptMethod>
<Name>ShouldNotEqual</Name>
<Script>
[NUnit.Framework.Assert]::AreNotEqual($args[0], $this)
</Script>
</ScriptMethod>
</Members>
</Type>
</Types>
Take the above XML and save it to a file…
NOTE: the file HAS to be saved with the extension .ps1xml
Ex: NunitSpecificationPowerShellExtensions.ps1xml
Step 2: Load the extended type definition into the PowerShell runtime.
Once you’ve saved the XML extended types to a file, you need to load it into the PowerShell runtime by executing the command below.
Update-TypeData -PrependPath NunitSpecificationPowerShellExtensions.ps1xml
Before executing the above statement…Let’s quickly look at a System.String’s members and properties – just to show you what the extension methods look like when applied inside of the runtime.
After executing the Update-TypeData command you’ll notice there are a number of “ScriptMethod” MemberTypes added to the object.
Now if you try to execute one of those newly added extension methods, you may get the following error…
PS C:\> $testVar.ShouldEqual("hello world")
Exception calling "ShouldEqual" with "1" argument(s): "Unable to find type [NUnit.Framework.Assert]: make sure that the
assembly containing this type is loaded."
At line:1 char:21
+ $testVar.ShouldEqual <<<< ("hello world")
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ScriptMethodRuntimeException
This is because we need to load the NUnit.Framework assembly into the runtime before we can leverage the extension methods.
[System.Reflection.Assembly]::LoadFrom("C:\Program Files\NUnit 2.5.2\bin\net-2.0\framework\nunit.framework.dll") | Out-Null
Now that the extension methods have been defined and loaded into the runtime, NUnit.Framework is loaded, we can now use the methods on any object that inherits from System.Object (which, as far as I know, is everything in PowerShell).
And now, everything you need in one script (if you have the xml extended type file saved somewhere…)
#
# Update-TypeData -prependPath C:\Code\NunitSpecificationPowerShellExtensions.ps1xml
#
[System.Reflection.Assembly]::LoadFrom("C:\Program Files\NUnit 2.5.2\bin\net-2.0\framework\nunit.framework.dll") | Out-Null
$true.ShouldBeTrue()
$false.ShouldBeFalse()
"a".ShouldEqual("a")
"a".ShouldNotEqual("b")