Giving the module a dependency
There is nothing to mock yet. Get-Planet holds its data in a hard-coded array, so it has no dependencies and always behaves identically. That is convenient and unrealistic — real functions read files, call APIs and query databases.
This page moves the data into a file. The next two pages deal with the consequences.
Moving the data into a file
Create a Data folder in the module and put the planets in a CSV:
New-Item -Path ./Planetarium/Data -ItemType Directory -Force
Name,Order,DistanceFromSunKm
Mercury,1,57909050
Venus,2,108208000
Earth,3,149598023
Mars,4,227939200
Jupiter,5,778570000
Saturn,6,1433530000
Uranus,7,2872460000
Neptune,8,4495060000
Reading it
Add a private helper that loads the file. It is private because it is an implementation detail — users ask for planets, not for the file the planets happen to live in.
function Get-PlanetData {
[CmdletBinding()]
param ()
$path = Join-Path $PSScriptRoot '../Data/planets.csv'
Import-Csv -Path $path | ForEach-Object {
[PSCustomObject] @{
Name = $_.Name
Order = [int] $_.Order
DistanceFromSunKm = [double] $_.DistanceFromSunKm
}
}
}
Two things here are load-bearing.
$PSScriptRoot resolves to the folder of the file that defines the function — Private/ — even though the function is dot-sourced into the module. That is what makes ../Data/planets.csv correct regardless of the directory the user happens to be in when they call your module.
The ForEach-Object block re-types the data. Import-Csv returns every column as a string, so without this Order would be '3' rather than 3.
Your existing tests would not catch that. $planet.Order | Should-Be 3 passes against '3', because Should-Be compares values and PowerShell converts the string first. Delete the ForEach-Object block and all fifteen tests stay green while the module quietly hands out strings.
That is a gap worth closing, and it needs an assertion about the type rather than the value. You will write it on the next page, once mocking makes it possible to feed the function strings on purpose.
Simplifying Get-Planet
Get-Planet now just filters whatever the data source hands back:
function Get-Planet {
[CmdletBinding()]
param (
[string] $Name = '*'
)
$planets = @(
[PSCustomObject] @{ Name = 'Mercury'; Order = 1; DistanceFromSunKm = 57909050 }
# ...six more planets...
[PSCustomObject] @{ Name = 'Neptune'; Order = 8; DistanceFromSunKm = 4495060000 }
)
$planets | Where-Object Name -Like $Name
Get-PlanetData | Where-Object Name -Like $Name
}
Nothing should have broken
This was a refactor: the behaviour is meant to be identical. Your test suite is how you find out whether it actually is.
./test.ps1
Tests Passed: 15, Failed: 0, Skipped: 0, Inconclusive: 0, NotRun: 0
Fifteen tests, all still green, without a single test file changing. That is the return on the work from the previous module — you just restructured how the module gets its data and confirmed in under a second that nothing regressed.
Every one of those tests now reads a real file from disk. They still pass, but they are no longer testing Get-Planet alone — they are testing Get-Planet, plus Get-PlanetData, plus Import-Csv, plus the filesystem, plus the contents of planets.csv. Edit that CSV and unrelated tests start failing.
That coupling is what the next page removes.
Before you move on
0/5Next: cutting the tests loose from that file.