PSUnit PowerShell Unit Testing Framework – Getting Started Guide – Writing Unit Tests – Version 2 Beta 1

by Klaus Graefensteiner 5. August 2009 08:41

Introduction

I am glad to announce that the first official release of PSUnit is now available for download on Codeplex http://www.psunit.org/Release/ProjectReleases.aspx. This release is a fully functional unit testing framework that lets you write and execute unit tests in PowerShell. It also provides a nice test result reporting feature. The Beta release is missing some convenience features, like an automatic deployment of an PowerShell module. Instead there are a few manual steps required to deploy the framework successfully. To increase the usability in the near future I am also extending the PSUnit integration with the PowerShell ISE.

Note: This release of PSUnit requires PowerShell 2.0 CTP3 or later! Click here to download this version of PowerShell

Writing PSUnit tests

The development of unit tests is relatively straight forward and very similar to the concepts of NUnit for example. The sample code that this post uses can be found in the PSUnit\Samples folder that is part of the PSUnit download. The files are called Interpolate-Records.ps1 (script under test) and Interpolate-Records-Test.ps1 (test script).

Features that are similar to NUnit:

  1. Constraint based Assertion concept (Script Blocks {} as lambda expressions)
  2. Test functions
  3. Skip (Ignore) Attribute to temporarily exclude test functions from being executed
  4. Category Attribute to filter out functions that belong to a specific category and execute only these
  5. Expected Exception Attribute to test for error conditions

Features that are different:

  1. No Setup or Teardown functions
  2. No FixtureSetup or FixtureTeardown functions
  3. Test functions are organized and executed in a script file entity instead of a .NET class

Note: For installation instructions read the following article: http://www.tellingmachine.com/post/2009/08/PSUnit-PowerShell-Unit-Testing-Framework-ndash3b-Getting-Started-Guide-ndash3b-Version-2-Beta-1.aspx

Step 1: Create a new test script

I assume that you are already working on a script that you want to write unit tests for. I call this script “Script-Under-Test”. Open your Script-Under-Test in the PowerShell ISE and create a new empty script. I recommend to name your test script like your Script-Under-Test plus the “.Test.ps1” extension. This convention helps to organize your tests efficiently.

In my example the Script-Under-Test is called “Interpolate-Records.ps1” and I named the test script “Interpolate-Records.Test.ps1”.

image

Figure 1: Name your test script like your Script-Under-Test plus “.Test.ps1”

Step 2: Add script reference to the PSUnit framework and script under test

Add a reference to the PSUnit.ps1 script and to your Script-Under-Test.

   1: . PSUnit.ps1
   2: . (Join-Path -Path $env:PSUNIT_HOME -ChildPath "Samples\Interpolate-Records.ps1")

image

Figure 2: Add script references to PSUnit.ps1 and your Script-Under-Test

Step 3: Add a test function

Test functions are defined as such by prefixing them with the “Test.” string. I recommend to make the function names very descriptive. One way is to use the following pattern:

[Test.][Function that needs to be tested]_[Expected Result]_[Pre-conditions]

Example:

   1: function Test.Is-RecordAtDesiredSampleTimeStamp_ReturnsFalseIfSecondsAreNot0()

image

Figure 3: Prefix your test function name with “Test.” This makes it a unit test.

Function Parameters

A basic test function can be attributed with three parameters. Custom square bracket attributes are not available in PowerShell and I decided to get this functionality with a very pragmatic approach. I am using function parameters to convey the meta data for specific test scenarios.

$ExpectedException Parameter (Attribute)

With the help of this function parameter the test runner can determine that an exception that the test throws is of the specified type. The parameter name is $ExpectedException and the parameter type is provided in the function parameter definition.

Here is an example test:

   1: function Test.Is-RecordAtDesiredSampleTimeStamp_ThrowsArgumentOutOfRangeExceptionIfDateIsInTheFuture([switch] $Category_ParameterValidation, [System.ArgumentOutOfRangeException] $ExpectedException = $Null)
   2: {
   3:     #Arrange
   4:     $Time = ([DateTime]::Now).AddDays(1)
   5:  
   6:     #Act
   7:     $Actual = Is-RecordAtDesiredSampleTimeStamp -Time $Time -SampleInterval 10
   8:         
   9:     #Assert
  10: }

$Category_xyz Parameter (Attribute)

You can decorate a test with a specific category. If you tell the test runner to execute tests with a specific category, then only test that have a category parameter with the specified name will be executed. The parameter name has two parts. It starts with $Category_ and ends with the category name. E.g. $Category_FastTests. The category specified with this parameter is “FastTests”. The parameter is of type switch.

   1: function Test.Is-RecordAtDesiredSampleTimeStamp_DoesntThrowArgumentOutOfRangeExceptionIfSampleIntervalIs12AndTimeIsYesterday([switch] $Category_ParameterValidation)
   2: {
   3:     #Arrange
   4:     $Time = ([DateTime]::Now).AddDays(-1)
   5:  
   6:     #Act
   7:     $Actual = Is-RecordAtDesiredSampleTimeStamp -Time $Time -SampleInterval 12
   8:         
   9:     #Assert
  10: }

$Skip Parameter (Attribute)

A test function with the $Skip parameter will not be executed by the test runner. The test will be ignored. The name of the parameter is $Skip and it is of type switch.

   1: function Test.Change-Status_ThrowsInvalidOperationExceptionIfStatusTransitionsFrom2To4([switch] $Skip)
   2: {
   3:  
   4:     #Arrange
   5:     $OldStatus =2
   6:     $NewStatus =4
   7:     
   8:     #Act
   9:     Change-Status -OldStatus $OldStatus -NewStatus $NewStatus
  10:         
  11:     #Assert
  12: }

These three attributes can be used at the same time and the order doesn’t matter. Here is an example unit test that specifies a category and an expected exception attribute.

   1: function Test.Is-RecordAtDesiredSampleTimeStamp_ThrowsArgumentOutOfRangeExceptionIfSampleIntervalIs-12([switch] $Category_ParameterValidation, [System.ArgumentOutOfRangeException] $ExpectedException = $Null)
   2: {
   3:     #Arrange
   4:     $Time = ([DateTime]::Now).AddDays(-1)
   5:  
   6:     #Act
   7:     $Actual = Is-RecordAtDesiredSampleTimeStamp -Time $Time -SampleInterval -12
   8:         
   9:     #Assert
  10: }

Writing PSUnit tests (Continued)

Step 4: Follow the AAA pattern: Arrange, Act and Assert

This pattern is widespread and will help you to structure your tests nicely.

  • Arrange: Initialize variables
  • Act: Call the function under test
  • Assert: Verify the result of the tested function

Step 5: Add an Assert constraint

One of the core features of any unit testing framework is the assertion library. This framework followed a pragmatic and also very powerful approach to not provide a library of different assertions, but rather take advantage of the fact that PowerShell is a very dynamic language that supports lambda expressions in the form of Scriptblocks. The only assertion function in PSUnit is called Assert-That and takes only two parameters:

  • $Actual is the input object that a constraint is going to be applied to
  • $Constraint is a Scriptblock that represents the constraint that $Actual is going to be evaluated against

Assert-That returns either a Boolean $True or throws an exception that gets analyzed by the test runner.

Here is the example of a very simple constraint:

   1: function Test.Is-RecordAtDesiredSampleTimeStamp_ReturnsFalseIfSecondsAreNot0()
   2: {
   3:     #Arrange
   4:     $Time = New-Object -TypeName "System.DateTime" -ArgumentList 2009,1,1,10,45,01
   5:     #Act
   6:     $Actual = Is-RecordAtDesiredSampleTimeStamp -Time $Time -SampleInterval 10
   7:     #Assert
   8:     Assert-That -ActualValue $Actual -Constraint {$ActualValue -eq $false}
   9: }

image

Figure 4: Simple Assert constraint

The beauty of the constraint based assertion model is that it can be easily extended and the constraints can be as specific and complex as you wish.

Here is an example constraint that verifies that the elements of a collection are in the same order than the elements of a reference collection:

   1: Function Test.GetEnumerator_UsingForceSwitchWithHashTableDoesntChangeOutcome([switch] $Category_HashTable)
   2: {
   3:     #Arrange
   4:     $Team = @{4 = "Joe"; 2 = "Steve"; 12 = "Tom"}
   5:     
   6:     #Act
   7:     $Actual = $Team | Get-Enumerator -Force | Sort-Object -Property "Key" | Foreach-Object{ $_.key}
   8:     
   9:     #Assert
  10:     Write-Debug $($Actual.GetType().FullName)
  11:     $ExpectedOrder = 2,4,12
  12:     Assert-That -ActualValue $Actual -Constraint {(Compare-Object -ReferenceObject $ExpectedOrder -DifferenceObject $ActualValue -IncludeEqual -SyncWindow 0).Count -eq 3}
  13: }

Step 6: Run your tests

Run your test function by making your test script the current script in the PowerShell ISE and select the PowerShell ISE command Custom/Run Unit Tests (CTP3) or AddOns/Execute Unit Tests (RTM).

Use your console output to verify whether your test did what you expected it to do.

Interpolate-RecordsTestResults

Figure 5: Test result report

Questions and comments

Any feedback is well come. Post a comment to this blog or leave a comment on the Codeplex PSUnit site: http://www.psunit.org.

Tags: ,

PowerShell | PSUnit | Test Automation

Comments

9/3/2009 11:01:37 PM #

pingback

Pingback from tech.cgi-biz.com

PSUnit PowerShell Unit Testing Framework – Getting Started Guide – Writing Unit Tests – Version 2 Beta 1 - TECHNOLOGY - CGI BIZ: Technology Updates

tech.cgi-biz.com | Reply

12/1/2009 9:38:35 PM #

Jack Hughes

Is there a way to assert that a float/double is equal to a number +- a given delta as the following NUnit code does:

Is.EqualTo(19.98999749D).Within(.00000001)

Thank you for creating the library.
Jack

Jack Hughes United Kingdom | Reply

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading



About Klaus Graefensteiner

I like the programming of machines.

Add to Google Reader or Homepage

LinkedIn FacebookTwitter View Klaus Graefensteiner's profile on Technorati
Klaus Graefensteiner

Klaus Graefensteiner
works as developer in Test at Rockwell Automation and is founder of the PowerShell Unit Testing Framework PSUnit. More...

Administration

About

Powered by:
BlogEngine.Net
Version: 1.5.0.7

License:
Creative Commons License

Copyright:
© Copyright 2009, Klaus Graefensteiner.

Disclaimer:
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Theme design:
This blog theme was designed and is copyrighted 2009 by Klaus Graefensteiner

Rendertime:
Page rendered at 7/30/2010 2:27:40 AM (PST Pacific Standard Time UTC DST -7)