Friday, 30 October 2015

PowerShell Unit Testing with Pester 3.3

I have been working with the Pester unit testing framework at the moment, so I thought I would share some of the more interesting bits...

PowerShell returns everything from a function!

I think this is a trap every PowerShell programmer has fallen into at some point - probably at the beginning. The return statement will return everything which is not "captured" in a function. In my case the answer was to wrap some xml manipulation code in a function which casts to void so that the return statement would actually return the custom PSObject I was expecting instead of an XmlElement which was being created at some point in my code.


   [void](do-XmlManipulation $myParamater)

This was the explanation which really hit home

It helped me realise the awful truth!

Mocking

Mocking in Pester is fantastically simple, there is an object called Mock!

You simply type Mock followed by the function that you wish to mock and then return.

Mock get-MyObject {
   return { [PSCustomObject]@{ Project = "New and Exciting Project" } }
}


In this case I have returned a Powershell object with the property "Project" which is a string. For example, the code in the module may look like this:
## myRealCodeModule.psm1

$document = get-MyObject

function get-MyObject {  
   $xmlDocument = Get-Content -Path "C:\temp.xml"
      return $xmlDocument
   }
}

But the unit test will simply bypass the attempt to get the xml file and will return our object, as defined in our unit test, instead.

Unit testing powershell modules

I keep all my most important logic in PowerShell modules. Sure, I will unit test the main PowerShell script but probably this will be mostly mocking, the real meat should be found in nicely named and separated modules.

Example of testing mocked functions in the main PowerShell script:

$scriptPath = Split-Path $script:MyInvocation.MyCommand.Path
Import-Module ("{0}\migrate.ps1" -f $scriptPath) -Verbose

Describe 'Migrate' {
    Context "When calling" {
        Mock Restore-Database {
            return "OK"
        }
    }
 
 It 'Should call all mocks' {
  Assert-VerifiableMocks
 }
}

One of the strengths of modules is that they must be imported. When they are imported a snapshot is kept in the scope of the importing script. Unfortunately, that means when changing the module, the changes are not immediately reflected because a snapshot still exists against the unit test script. My particular solution is to use "remove-module". There is the recommended solution from Pester here. However, for my purposes this is overkill.


Get-Module | Remove-Module
$scriptPath = (Get-Location).Path
Import-Module ("{0}\myRealCodeModule.psm1" -f $scriptPath)


The other strength of modules is that only exported functions are exposed, which means that functions can be internalised or made "private". This is a good thing because I have more scope for writing neat, well named functions which will make my code more readable to future maintainers.

Unfortunately this is a potential barrier to unit testing effectively. Luckily Pester has the answer here as well with "InModuleScope":


   Describe 'When unit testing get-MyObject which is not exported by the module' {
      InModuleScope myRealCodeModule {
         It 'Should have called the mock' {
            
            Mock get-MyObject {
               return { [PSCustomObject]@{ Project = "New and Exciting Project" } }
            }
            
            my-MainExportedAndThereforePublicFunction

            Assert-MockCalled get-MyObject -Times 1
         }
      }
   }




For a getting started guide: see this excellent blog

Monday, 12 October 2015

ES6 or JS2015 is finally here!

Short post this time. I have been away from the Javascripot space for a little while, but now I have started a new NodeJS project.

I am incredibly excited about the wide spread adoption of ES6 this year and I have to say that VS Code has just made me incredibly happy. Not only does VS Code give ES6 intellisense but by telling the IDE that you wish to compile in ES6 you can stop it giving red squiggly lines as well. Fantastic stuff. Just add a jsconfig.json file with the following:

{
    "compilerOptions": {
        "target": "ES6"
    }
}

The only bad news is that surely I have to remember which features have actually been implemented by the V8 engine.

Well again I was very happy to discover that there is not a great deal left. The only proviso is that you have to run node --harmony which something you can alias in you command line runner of choice.

The other option is to run babel. In the end I have settled on doing the following :

  • npm install babel  
  • Add require(("babel/register") to the server.js file
The advantage of this approach is that I do not have to remember to write a custom build script with the correct parameters.