Skip to content

Pester

PowerShell TDD with Pester – Adding More Rules (part 2 of 3)

In this article, we’ll add more rules to our version number generator to handle things like bug fixes to major versions, pre-release labels and build labels.

To help guide the rules being implemented, below is a table of the incrementation rules that will be applied.

Element Incrementation Notes
Major Version in file used with major versions treated separately for incrementation purposes
Minor Latest value used within major version.
Patch One higher than previous version unless major or minor version has increased in which case should be 0
Pre-Release Label Removing it or leaving one in place won’t cause an increase (if no existing version exists on that level) but adding it will increase the patch version the label changing doesn’t matter
Build Label Does not cause any incrementations.

Seventh Test – Minor Version Increase Resets Patch

This test is about minor version number changes causing the patch to reset to 0 when the minor version number changes but otherwise it should be increased.

Failing Test

It 'Minor version change in file version (<fileVersion>) compared to published version (<publishedVersion>) causes patch to reset to 0 (<expectedVersion>)' -ForEach @(
  @{ FileVersion = "1.0.0"; PublishedVersion = "1.0.0"; ExpectedVersion = "1.0.1" }
  @{ FileVersion = "1.1.0"; PublishedVersion = "1.0.0"; ExpectedVersion = "1.1.0" }
  @{ FileVersion = "1.0.0"; PublishedVersion = "1.1.0"; ExpectedVersion = "1.1.1" }
  @{ FileVersion = "1.1.0"; PublishedVersion = "1.1.0"; ExpectedVersion = "1.1.1" }
) {
  # Arrange
  $fileVersion = [SemanticVersion]$fileVersion
  $publishedVersion = [SemanticVersion]$nugetVersion
  $preReleaseLabel = ""
  $buildLabel = ""

  $expectedVersion = [SemanticVersion]$expectedVersion

  # Act
  $newVersion = [SemanticVersion](Get-NextVersion -FileVersion $fileVersion -PublishedVersion $publishedVersion -PreReleaseLabel $preReleaseLabel -BuildLabel $buildLabel)

  # Assert
  $newVersion | Should -Be $expectedVersion
}

Making the Test Green

To make this test work, a few small changes are needed. A resetPatch boolean variable is needed, defaulting to false and the published version check needs splitting and a new method adding. Finally, the new patch version calculator needs some extra logic to handle the resetPatch variable.

Once this is all put together, it looks like this:

function Get-NextVersion {
  param (
    [SemanticVersion] $FileVersion,
    [SemanticVersion] $PublishedVersion
  )

  $newVersion = [SemanticVersion]"0.0.0"
  $notSet = [SemanticVersion]"0.0.0"
  $resetPatch = $false

  if ($FileVersion -ne $notSet -and $FileVersion -gt $newVersion) {
    $newVersion = $FileVersion
  }
  if ($PublishedVersion -ne $notSet) {
    if ($newVersion.Minor -gt $PublishedVersion.Minor) {
      $resetPatch = $true
    }
    if ($PublishedVersion -gt $newVersion) {
      $newVersion = $PublishedVersion
    }    
  }

  $patch = $newVersion.Patch + 1;
  if ($resetPatch) {
    $patch = 0
  }
  $newVersion = [SemanticVersion](Get-UpdatedSemanticVersion $newVersion -PatchVersion $patch)

  Write-Output $newVersion
}

This change will, however, cause our previous test to fail. To resolve this, update the second data set’s expected value from 1.1.1 to 1.1.0.

Eighth Test – Pre-release Label Increments Patch If Added But Not If Removed Or Already Present

This test builds the logic for handling the use of pre-release tags (e.g. “-dev” or “-test”). It should only increase the patch version if adding a label. The value of the label isn’t important.

Failing Test

It 'Pre-release tag increments patch if added (<preReleaseTag>) but not if removed or already present (<publishedVersion> --> <expectedVersion>)' -ForEach @(
  @{ FileVersion = "1.0.0"; PublishedVersion = "1.0.0"; PreReleaseLabel = "dev"; ExpectedVersion = "1.0.1-dev" }
  @{ FileVersion = "1.0.0"; PublishedVersion = "1.0.0-dev"; PreReleaseLabel = "dev"; ExpectedVersion = "1.0.0-dev" }
  @{ FileVersion = "1.0.0"; PublishedVersion = "1.0.0-dev"; PreReleaseLabel = ""; ExpectedVersion = "1.0.0" }
  @{ FileVersion = "1.0.0"; PublishedVersion = "1.0.0-dev"; PreReleaseLabel = "test"; ExpectedVersion = "1.0.0-test" }
  @{ FileVersion = "1.1.0"; PublishedVersion = "1.0.1-dev"; PreReleaseLabel = "dev"; ExpectedVersion = "1.1.0-dev" }
  @{ FileVersion = "1.1.0"; PublishedVersion = "1.0.1-dev"; PreReleaseLabel = ""; ExpectedVersion = "1.1.0" }
) {
  # Arrange
  $fileVersion = [SemanticVersion]$fileVersion
  $publishedVersion = [SemanticVersion]$publishedVersion
  $buildLabel = ""

  $expectedVersion = [SemanticVersion]$expectedVersion

  # Act
  $newVersion = [SemanticVersion](Get-NextVersion -FileVersion $fileVersion -PublishedVersion $publishedVersion -PreReleaseLabel $preReleaseLabel -BuildLabel $buildLabel)

  # Assert
  $newVersion | Should -Be $expectedVersion
}

Making the Test Green

The main code changes required are to do with determining if the patch version should be incremented or not. This is largely centred around whether or not the pre-release label is on the existing published package or not.

function Get-NextVersion {
  param (
    [SemanticVersion] $FileVersion,
    [SemanticVersion] $PublishedVersion,
    [string] $PreReleaseLabel
  )

  $newVersion = [SemanticVersion]"0.0.0"
  $notSet = [SemanticVersion]"0.0.0"
  $incrementPatch = $true
  $resetPatch = $false

  if ($FileVersion -ne $notSet -and $FileVersion -gt $newVersion) {
    $newVersion = $FileVersion
  }
  if ($PublishedVersion -ne $notSet) {
    if (![string]::IsNullOrWhiteSpace($PublishedVersion.PreReleaseLabel)) {
      $incrementPatch = $false
    }
    if ($newVersion.Minor -gt $PublishedVersion.Minor) {
      $resetPatch = $true
    }
    if ($PublishedVersion -gt $newVersion) {
      $newVersion = $PublishedVersion
    }    
  }

  $patch = $newVersion.Patch
  if ($resetPatch) {
    $patch = 0
  } elseif ($incrementPatch) {
    $patch++
  }
  $newVersion = [SemanticVersion](Get-UpdatedSemanticVersion $newVersion -PatchVersion $patch -PreReleaseLabel $PreReleaseLabel)

  Write-Output $newVersion
}

Ninth Test – Any Build Label Stops Patch Incrementation

If a build label is specified, it will stop patch incrementation from happening. It won’t stop resets though.

Failing Test

It 'Any build label (<buildLabel>) stops patch incrementation (<publishedVersion> --> <expectedVersion>)' -ForEach @(
  @{ FileVersion = "1.0.0"; PublishedVersion = "1.0.0-dev"; PreReleaseLabel = "dev"; BuildLabel = "1234"; ExpectedVersion = "1.0.0-dev+1234" }
  @{ FileVersion = "1.0.0"; PublishedVersion = "1.0.0-dev+1234"; PreReleaseLabel = "dev"; BuildLabel = "1234"; ExpectedVersion = "1.0.0-dev+1234" }
  @{ FileVersion = "1.0.0"; PublishedVersion = "1.0.0-dev+1233"; PreReleaseLabel = "dev"; BuildLabel = "1234"; ExpectedVersion = "1.0.0-dev+1234" }
  @{ FileVersion = "1.0.0"; PublishedVersion = "1.0.0"; PreReleaseLabel = ""; BuildLabel = "1234"; ExpectedVersion = "1.0.0+1234" }
  @{ FileVersion = "1.0.0"; PublishedVersion = "1.0.0"; PreReleaseLabel = "dev"; BuildLabel = "1234"; ExpectedVersion = "1.0.1-dev+1234" }
) {
  # Arrange
  $fileVersion = [SemanticVersion]$fileVersion
  $publishedVersion = [SemanticVersion]$publishedVersion

  $expectedVersion = [SemanticVersion]$expectedVersion

  # Act
  $newVersion = [SemanticVersion](Get-NextVersion -FileVersion $fileVersion -PublishedVersion $publishedVersion -PreReleaseLabel $preReleaseLabel -BuildLabel $buildLabel)

  # Assert
  $newVersion | Should -Be $expectedVersion
}

Making the Test Green

To make this test, the build number parameter needs adding and an “else if” condition adding after the pre-release label check to stop incrementing if a new tag isn’t present but a build number is.

function Get-NextVersion {
  param (
    [SemanticVersion] $FileVersion,
    [SemanticVersion] $PublishedVersion,
    [string] $PreReleaseLabel,
    [string] $BuildLabel
  )

  $newVersion = [SemanticVersion]"0.0.0"
  $notSet = [SemanticVersion]"0.0.0"
  $incrementPatch = $true
  $resetPatch = $false

  if ($FileVersion -ne $notSet -and $FileVersion -gt $newVersion) {
    $newVersion = $FileVersion
  }

  if ($PublishedVersion -ne $notSet) {
    if (![string]::IsNullOrWhiteSpace($PublishedVersion.PreReleaseLabel)) {
      $incrementPatch = $false
    } elseif ([string]::IsNullOrWhiteSpace($PreReleaseLabel) -and ![string]::IsNullOrWhiteSpace($BuildLabel)) {
      $incrementPatch = $false
    }
    if ($newVersion.Minor -gt $PublishedVersion.Minor) {
      $resetPatch = $true
    }
    if ($PublishedVersion -gt $newVersion) {
      $newVersion = $PublishedVersion
    }    
  }

  $patch = $newVersion.Patch
  if ($resetPatch) {
    $patch = 0
  } elseif ($incrementPatch) {
    $patch++
  }
  $newVersion = [SemanticVersion](Get-UpdatedSemanticVersion $newVersion -PatchVersion $patch -PreReleaseLabel $PreReleaseLabel -BuildLabel $BuildLabel)

  Write-Output $newVersion
}

Tenth Test – Reading Files

The next test, covered in the next blog, will cover testing the process of reading files.

PowerShell TDD with Pester – Adding More Rules (part 3 of 3)

PowerShell TDD with Pester – Adding More Rules (part 3 of 3)

This final article will cover reading mocked files and retrieving the version number from the CSProj or Chart.yaml file.

Tenth Test – Reading From Chart.yaml File

The first test is to read a Helm’s Chart.yaml file, extract the current current chart version and call the Get-NextVersion function.

Failing Test

Describe 'Read-CurrentVersionNumber' {
  It 'Extract current chart version from Chart.yaml and call Get-NextVersion' {
    # Arrange
    $expected = [SemanticVersion]"1.0.0"

    # Act
    $actual = [SemanticVersion](Read-CurrentVersionNumber -File "Chart.yaml)

    # Assert
    $actual | Should -Be $expected
  }
}

To give a fail (rather than non-running) test, add the following function:

function Read-CurrentVersionNumber {

}

Making the Test Green

As we’re reading a YAML file, we’ll use the powershell-yaml module.

To make the test pass, simple return “1.0.0” from the function:

function Read-CurrentVersionNumber {
  Write-Output "1.0.0"
}

Refactor to Read (Faked) File

To make the code (simulate) reading a file, we need to add a parameter to pass in the file name, process the YAML and return the version.

function Read-CurrentVersionNumber {
  param (
    [Parameter(Mandatory=$true)] [string] $file
  )

  Import-Module powershell-yaml

  $chartYaml = (Get-Content -Path $file | ConvertFrom-Yaml)

  Write-Output $chartYaml.version
}

We’ll also add some code to BeforeAll to mock certain functions to prevent module installs happening as part of unit tests.

  function Get-Content {
    Write-Output "
    apiVersion: v2
    appVersion: 1.0.0
    description: A Helm chart to deploy applications to a Kubernetes cluster using standard settings
    name: Generalised
    type: application
    version: 1.0.0    
    "

Eleventh Test – Reading From CSProj File

This will cover reading from the XML-formatted CSProj file.

  It 'Extract current chart version from a CSProj file' {
    # Arrange
    $expected = [SemanticVersion]"1.2.0"

    # Act
    $actual = [SemanticVersion](Read-CurrentVersionNumber -File "My.Project.csproj")

    # Assert
    $actual | Should -Be $expected
  }

Making the Test Green

We’ll put some logic around the code reading the version so that it handles YAML files vs XML files. We’ll also need to add another mock to handle the reading of the CSProj/XML file.

The code changes involve a new if statement that analyses the file name being passed:

function Read-CurrentVersionNumber {
  param (
    [Parameter(Mandatory=$true)] [string] $file
  )

  $fileName = Split-Path $file -Leaf

  if ($fileName -eq "Chart.yaml") {
    Import-Module powershell-yaml

    $chartYaml = (Get-Content -Path $file | ConvertFrom-Yaml)

    Write-Output $chartYaml.version
  } elseif ($fileName.EndsWith(".csproj")) {
    $projectVersion = $(Select-Xml -Path "$file" -XPath '/Project/PropertyGroup/Version').Node.InnerText

    Write-Output $projectVersion
  }
}

To work, this needs to following stub adding:

  function Select-Xml {
    Write-Output @{
      Node = @{
        InnerText = "1.2.0"
      }
    }
  }

PowerShell TDD with Pester – Setup and Basic Tests (part 1 of 3)

Following on from my experiment with TDD using C#, I wanted to look at TDD with PowerShell as that’s another language I currently use quite a bit. To do this, I will be using the Pester framework. These articles are covering how I built a function to generate a new version number for a NuGet package, Helm chart, etc… based upon the version number in a file (e.g. chart.yaml or csproj) and the latest published version.

In these articles you may see more casting (e.g. [SemanticVersion]) than is necessary however I find it’s clearer to include it when dealing with non-basic types likes strings and ints.

As with my C# TDD articles, this will also have a GitHub repository made available. When it is, the link to it will appear here.

These articles are evolving so, particularly the third part, will change over time.

Pester Installing or Updating

I’m using Windows 11, PowerShell Core 7 and VS Code for this project so any instructions will be for that platform.

To install the latest version of Pester and ensure any existing versions don’t cause issues, including the default v3 installed with Windows, run the following command:

Install-Module -Name Pester -Force
Import-Module Pester

The second command is to force the current session to pick up the latest version of Pester.

First Test – Return 0.0.1 If No Version Exists

The first test should return 0.0.1 if no existing version is found and the csproj file version is 0.0.1 or less, no pre-release tag is specified and no build number is specified.

Failing Test

using namespace System.Management.Automation

BeforeAll {
  . $PSScriptRoot/Get-NextVersion.ps1
}

Describe 'Get-NextVersion' {
  It 'Return 0.0.1 when no meaningful version exists' {
    # Arrange
    $fileVersion = [SemanticVersion]"0.0.0"
    $publishedVersion = [SemanticVersion]"0.0.0"
    $preReleaseLabel = ""
    $buildLabel = ""

    $expectedVersion = [SemanticVersion]"0.0.1"

    # Act
    $newVersion = [SemanticVersion](Get-NextVersion -FileVersion $fileVersion -PublishedVersion $publishedVersion -PreReleaseLabel $preReleaseLabel -BuildLabel $buildLabel)

    # Assert
    $newVersion | Should -Be $expectedVersion
  }
}

To make this test run, an empty function definition should be added to the main ps1 file:

function Get-NextVersion {

}

The tests can then be ran using the following command:

Invoke-Pester -Output Detailed .\Get-NextVersion.Tests.ps1

Making the Test Green

To make the test pass, the easiest thing to do is simply return the expected version of 0.0.1 so that is what we’ll do.

function Get-NextVersion {
  Write-Output "0.0.1"
}

Second Test – Returning One Patch Higher Than The Latest Published Package

This test will ensure that any version number has a one higher patch number than the latest published patch. At this stage, the pre-release tag and build number are being ignored.

Failing Test

It 'Return one patch higher than existing published version' {
  # Arrange
  $fileVersion = [SemanticVersion]"0.0.0"
  $nugetVersion = [SemanticVersion]"0.3.1"
  $preReleaseLabel = ""
  $buildLabel = ""

  $expectedVersion = [SemanticVersion]"0.3.2"

  # Act
  $newVersion = [SemanticVersion](Get-NextVersion -FileVersion $fileVersion -PublishedVersion $publishedVersion -PreReleaseLabel $preReleaseLabel -BuildLabel $buildLabel)

  # Assert
  $newVersion | Should -Be $expectedVersion
}

Making the Test Green

To make the test green, we need to add a parameter to the Get-NextVersion function and then use it to add suitable logic. We should only add the needed parameter from the ones currently being passed.

The resulting function code will look like this:

using namespace System.Management.Automation

function Get-NextVersion {
  param (
    [SemanticVersion] $PublishedVersion
  )

  $newVersion = [SemanticVersion]"0.0.1"
  $notSet = [SemanticVersion]"0.0.0"

  if ($NuGetVersion -ne $notSet) {
    $newVersion = [SemanticVersion]::new($PublishedVersion.Major, $PublishedVersion.Minor, $PublishedVersion.Patch + 1, $PublishedVersion.PreReleaseLabel, $PublishedVersion.BuildLabel)
  }

  Write-Output $newVersion
}

This will work but could mean a lot of repeated and similar code each time one or more parts of the version change. Therefore, it would make sense to create a function for building up a new version, only updating those values which have changed.

Third Test – Function To Build New SemanticVersion When All Parameters Are Set

This test will pass in all new values for a SemanticVersion object and return a new version. This is effectively replicating the constructor but test four will handle no parameters being passed making this new function useful as only the changed values will need to be passed.

Failing Test

Describe 'Get-UpdatedSemanticVersion' {
  It 'Returns completely new version when all parameters are set' {
    # Arrange
    $currentVersion = [SemanticVersion]"1.2.3-dev+build1"
    $expectedVersion = [SemanticVersion]"4.5.6-new+build2"

    # Act
    $newVersion = [SemanticVersion](Get-UpdatedSemanticVersion $currentVersion -MajorVersion 4 -MinorVersion 5 -PatchVersion 6 -PreReleaseLabel "new" -BuildLabel "build2")

    # Assert
    $newVersion | Should -Be $expectedVersion
  }
}

Also, so the test fails (rather than fails to execute), add the following method to Get-NextVersion.ps1:

function Get-UpdatedSemanticVersion {
}

Making the Test Green

Making the test pass when all the parameters are specified is relatively straight forward:

function Get-UpdatedSemanticVersion {
  param (
    [Parameter(Mandatory, Position=0)] [SemanticVersion] $CurrentVersion,
    [int] $MajorVersion,
    [int] $MinorVersion,
    [int] $PatchVersion,
    [string] $PreReleaseLabel,
    [string] $BuildLabel
  )

  $newVersion = [SemanticVersion]::new($MajorVersion, $MinorVersion, $PatchVersion, $PreReleaseLabel, $BuildLabel)

  Write-Output $newVersion
}

But to make this test useful, only updating the new values passed is key so a new test is needed.

Fourth Test – Return Current Version When No Parameters Passed

Failing Test

  It 'Returns current version when all parameters except CurrentVersion are not set' {
    # Arrange
    $currentVersion = [SemanticVersion]"1.2.3-dev+build1"
    $expectedVersion = [SemanticVersion]"1.2.3-dev+build1"

    # Act
    $newVersion = [SemanticVersion](Get-UpdatedSemanticVersion $currentVersion)

    # Assert
    $newVersion | Should -Be $expectedVersion
  }

Making the Test Green

We need to check if all parameters were passed or not. We can use $PSBoundParameters to do this by checking each parameter name.

function Get-UpdatedSemanticVersion {
  param (
    [Parameter(Mandatory, Position=0)] [SemanticVersion] $CurrentVersion,
    [int] $MajorVersion,
    [int] $MinorVersion,
    [int] $PatchVersion,
    [string] $PreReleaseLabel,
    [string] $BuildLabel
  )

  if (($PSBoundParameters.ContainsKey('MajorVersion') -eq $false) -and
      ($PSBoundParameters.ContainsKey('MinorVersion') -eq $false) -and 
      ($PSBoundParameters.ContainsKey('PatchVersion') -eq $false) -and 
      ($PSBoundParameters.ContainsKey('PreReleaseLabel') -eq $false) -and
      ($PSBoundParameters.ContainsKey('BuildLabel') -eq $false)) {
    $newVersion = $CurrentVersion
  } else {
    $newVersion = [SemanticVersion]::new($MajorVersion, $MinorVersion, $PatchVersion, $PreReleaseLabel, $BuildLabel)
  }

  Write-Output $newVersion
}

Fifth Test – Removing Labels

This fifth test will be testing two things in a way – removing the labels when they’re already defined and that only some parameters can be specified for updating.

Failing Test

  It 'Returns current version with labels when labels are set to blanks strings' {
    # Arrange
    $currentVersion = [SemanticVersion]"1.2.3-dev+build1"
    $expectedVersion = [SemanticVersion]"1.2.3"

    # Act
    $newVersion = [SemanticVersion](Get-UpdatedSemanticVersion $currentVersion -PreReleaseLabel "" -BuildLabel "")

    # Assert
    $newVersion | Should -Be $expectedVersion
  }

Making the Test Green

We’ll do a check per parameter and set the new value to use to the current version if a new one isn’t specified.

function Get-UpdatedSemanticVersion {
  param (
    [Parameter(Mandatory, Position=0)] [SemanticVersion] $CurrentVersion,
    [int] $MajorVersion,
    [int] $MinorVersion,
    [int] $PatchVersion,
    [string] $PreReleaseLabel,
    [string] $BuildLabel
  )

  if ($PSBoundParameters.ContainsKey('MajorVersion') -eq $false) {
    $MajorVersion = $CurrentVersion.Major
  }

  if ($PSBoundParameters.ContainsKey('MinorVersion') -eq $false) {
    $MinorVersion = $CurrentVersion.Minor
  }

  if ($PSBoundParameters.ContainsKey('PatchVersion') -eq $false) {
    $PatchVersion = $CurrentVersion.Patch
  }

  if ($PSBoundParameters.ContainsKey('PreReleaseLabel') -eq $false) {
    $PreReleaseLabel = $CurrentVersion.PreReleaseLabel
  }

  if ($PSBoundParameters.ContainsKey('BuildLabel') -eq $false) {
    $BuildLabel = $CurrentVersion.BuildLabel
  }

  $newVersion = [SemanticVersion]::new($MajorVersion, $MinorVersion, $PatchVersion, $PreReleaseLabel, $BuildLabel)

  Write-Output $newVersion
}

Refactoring Get-NextVersion to Use This New Method

The code change is pretty simple and then, once made, re-run the tests to make sure all responses are working as expected.

Change the following line:

$newVersion = [SemanticVersion]::new($PublishedVersion.Major, $PublishedVersion.Minor, $PublishedVersion.Patch + 1, $PublishedVersion.PreReleaseLabel, $PublishedVersion.BuildLabel)

To the following:

$newVersion = [SemanticVersion](Get-UpdatedSemanticVersion $PublishedVersion -PatchVersion ($PublishedVersion.Patch + 1))

Sixth Test – New Version Is One Version Higher Than the Higher of File Version and NuGet Version

This test is about ensuring the next version is higher than any existing version. Three data sets will be tried; one where file version is higher than published version, one where it’s lower and one where it’s the same.

Failing Test

  It 'New version is one version higher (<expectedVersion>) than the higher of file version (<fileVersion>) and published version (<publishedVersion>)' -ForEach @(
    @{ FileVersion = "1.0.0"; PublishedVersion = "1.0.3"; ExpectedVersion = "1.0.4" }
    @{ FileVersion = "1.1.0"; PublishedVersion = "1.0.3"; ExpectedVersion = "1.1.1" }
    @{ FileVersion = "1.2.0"; PublishedVersion = "1.2.0"; ExpectedVersion = "1.2.1" }
  ) {
    # Arrange
    $fileVersion = [SemanticVersion]$fileVersion
    $publishedVersion = [SemanticVersion]$publishedVersion
    $preReleaseLabel = ""
    $buildLabel = ""

    $expectedVersion = [SemanticVersion]$expectedVersion

    # Act
    $newVersion = [SemanticVersion](Get-NextVersion -FileVersion $fileVersion -PublishedVersion $publishedVersion -PreReleaseLabel $preReleaseLabel -BuildLabel $buildLabel)

    # Assert
    $newVersion | Should -Be $expectedVersion
  }

You’ll notice that there are now 8 tests passing/failing even though we’ve only written six. That is because the -ForEach is effectively creating multiple variants of one test.

Making the Test Green

We’ll need to add a new parameter (FileVersion) and use this to determine what the highest new version is. By doing independent checks and updating newVersion as appropriate, the code is easier to read. newVersion is now defaulting to 0.0.0 so that the patch can also be incremented. We’ll see in later tests that we may not always want this.

function Get-NextVersion {
  param (
    [SemanticVersion] $FileVersion,
    [SemanticVersion] $PublishedVersion
  )

  $newVersion = [SemanticVersion]"0.0.0"
  $notSet = [SemanticVersion]"0.0.0"

  if ($FileVersion -ne $notSet -and $FileVersion -gt $newVersion) {
    $newVersion = $FileVersion
  }
  if ($PublishedVersion -ne $notSet -and $PublishedVersion -gt $newVersion) {
    $newVersion = $PublishedVersion
  }

  $newVersion = [SemanticVersion](Get-UpdatedSemanticVersion $newVersion -PatchVersion ($newVersion.Patch + 1))

  Write-Output $newVersion
}

Seventh Test

As this article is getting quite long, I’ll stop here and continue in the next article which will see more rules added and some mocking done.