Skip to content

Commit

Permalink
mock works with any command (not just functions) and the parameter fi…
Browse files Browse the repository at this point in the history
…lters are implemented. also implemented Clear-Mock
  • Loading branch information
mwrock committed Oct 22, 2012
1 parent 9f39761 commit ec77cea
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 8 deletions.
50 changes: 48 additions & 2 deletions Functions/Mock.Tests.ps1
Expand Up @@ -2,7 +2,7 @@ $here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
. "$here\$sut"

function FunctionUnderTest {
function FunctionUnderTest ([string]$param1=""){
return "I am a real world test"
}

Expand All @@ -19,8 +19,31 @@ Describe "When calling Mock on existing function" {
It "Should Invoke the mocked script" {
$result.should.be("I am the mock test")
}
Clear-Mocks
}

Describe "When calling Mock on existing cmdlet" {
Mock Get-Process {return "I am not Get-Process"}

$result=Get-Process

It "Should Invoke the mocked script" {
$result.should.be("I am not Get-Process")
}
Clear-Mocks
}

Describe "When calling Mock on cmdlet Used by Mock" {
Mock Invoke-Command {return "I am not Invoke-Command"}
$result = Invoke-Command {return "yes I am"}

It "Should Invoke the mocked script" {
$result.should.be("I am not Invoke-Command")
}
Clear-Mocks
}


Describe "When calling Mock on non-existing function" {
try{
Mock NotFunctionUnderTest {return}
Expand All @@ -29,6 +52,29 @@ Describe "When calling Mock on non-existing function" {
}

It "Should throw correct error" {
$result.Exception.Message.should.be("Could not find function NotFunctionUnderTest")
$result.Exception.Message.should.be("Could not find command NotFunctionUnderTest")
}
Clear-Mocks
}

Describe "When calling Mock on existing function without matching params" {
Mock FunctionUnderTest {return "fake results"} -parameterFilter {$param1 -eq "test"}

$result=FunctionUnderTest "badTest"

It "Should redirect to real function" {
$result.should.be("I am a real world test")
}
Clear-Mocks
}

Describe "When calling Mock on existing function with matching params" {
Mock FunctionUnderTest {return "fake results"} -parameterFilter {$param1 -eq "badTest"}

$result=FunctionUnderTest "badTest"

It "Should return mocked result" {
$result.should.be("fake results")
}
Clear-Mocks
}
41 changes: 35 additions & 6 deletions Functions/Mock.ps1
@@ -1,10 +1,24 @@
function Mock ([string]$function, [ScriptBlock]$mockWith, [switch]$verifiable, [HashTable]$parameterFilters = @{})
$mockTable = @{}

function Mock ([string]$commandName, [ScriptBlock]$mockWith, [switch]$verifiable, [ScriptBlock]$parameterFilter = {$True})
{
# If verifiable, add to a verifiable hashtable
if(!(Test-Path Function:\$function)){ Throw "Could not find function $function"}
Rename-Item Function:\$function script:PesterIsMocking_$function
Set-Item Function:\script:$function -value $mockWith
# Mocked function should redirect to real function if param filters are not met
$origCommand = (Get-Command $commandName -ErrorAction SilentlyContinue)
if(!$origCommand){ Throw "Could not find Command $commandName"}
$blocks = @{Mock=$mockWith; Filter=$parameterFilter}
$mock = $mockTable.$commandName
if(!$mock) {
if($origCommand.CommandType -eq "Function") {
Rename-Item Function:\$commandName script:PesterIsMocking_$commandName
}
$metadata=New-Object System.Management.Automation.CommandMetaData $origCommand
$cmdLetBinding = [Management.Automation.ProxyCommand]::GetCmdletBindingAttribute($metadata)
$params = [Management.Automation.ProxyCommand]::GetParamBlock($metadata)
$newContent=Get-Content function:\MockPrototype
Set-Item Function:\script:$commandName -value "$cmdLetBinding `r`n param ( $params ) `r`n$newContent"
$mock=@{OriginalCommand=$origCommand;blocks=@($blocks)}
} else {$mock.blocks += $blocks}
$mockTable.$commandName = $mock
# param filters are met, mark in the verifiable table
}

Expand All @@ -16,5 +30,20 @@ function Assert-VerifiableMocks {
function Clear-Mocks {
# Called at the end of Describe
# Clears the Verifiable table
# Renames all renamed mocks back to original names
$mockTable.Keys | % { Microsoft.PowerShell.Management\Remove-Item function:\$_ }
$script:mockTable = @{}
Microsoft.PowerShell.Management\Get-ChildItem Function: | ? { $_.Name.StartsWith("PesterIsMocking_") } | % {Microsoft.PowerShell.Management\Rename-Item Function:\$_ "script:$($_.Name.Replace('PesterIsMocking_', ''))"}
}

function MockPrototype {
$functionName = $MyInvocation.MyCommand.Name
$mock=$mockTable.$functionName
$idx=$mock.blocks.Length
while(--$idx -ge 0) {
if(Microsoft.PowerShell.Core\Invoke-Command $mock.blocks[$idx].Filter) {
Microsoft.PowerShell.Core\Invoke-Command $mockTable.$functionName.blocks.mock
return
}
}
&($mock.OriginalCommand)
}

0 comments on commit ec77cea

Please sign in to comment.