My Technical Notes

Saturday, 25 March 2017

Generating Random Strings in PowerShell

As part of unit testing my PowerShell functions, I have noticed a small potential issue with having data hard-coded into unit tests. Such tests can be made to pass by hard-coding the answer in the tested function. For instance we could have a unit test for a function called `Concat-String` that takes two `string` arguments and concatenates them together:


$result = Concat-String "Tahir" "Riyadh"
Assert-AreEqual "TahirRiyadh" $result

Such a test could easily be passed by hard-coding a return value:


Function Concat-String([string]$First, [string]$Second) {
    "TahirRiyadh"
}

(Of course, a unit-testing purist would say that you need multiple tests to prevent the possibility of hard-coded values, but that is besides the point - I only want to write one unit test, not many versions of the same test with different data).

Instead, what I am now using to generate test data is the following function, `New-RandomString`:


Add-Type -AssemblyName System.Security

Function New-RandomString {
    param([Parameter()][int]$Size=10)

    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_".ToCharArray();
    
    $bytes = & {
        $data = [byte[]]::new($Size)
        $crypto = [System.Security.Cryptography.RNGCryptoServiceProvider]::new();
        $crypto.GetBytes($data)
        $data
    }

    $str = [char[]]($bytes | % { $_ % $chars.Length } | % { $chars[$_] })

    [string]::new($str)
}

Instead of hard-coding test data in the unit test, `New-RandomString` can be used to give appropriate test values:


$firstVal = New-RandomString 15
$secondVal = New-RandomString 20
$result = Concat-String $firstVal $secondVal
$expected = $firstVal + $secondVal
Assert-AreEqual $expected $result

Such a test cannot be passed by hard-coding a correct return value. It can also be run multiple times to ensure that it passes i all cases.

Sources

No comments: