Skip to content

Commit

Permalink
Merge pull request #323 from dlwyatt/GlobalFunctionMock
Browse files Browse the repository at this point in the history
Initial fix for #286
  • Loading branch information
dlwyatt committed Apr 28, 2015
2 parents 735af87 + 854e298 commit 4625759
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 11 deletions.
30 changes: 30 additions & 0 deletions Functions/GlobalMock-A.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This script exists to create and mock a global function, then exit. The actual behavior
# that we need to test is covered in GlobalMock-B.Tests.ps1, where we make sure that the
# global function was properly restored in its scope.

$functionName = '01c1a57716fe4005ac1a7bf216f38ad0'

if (Test-Path Function:\$functionName)
{
Remove-Item Function:\$functionName -Force -ErrorAction Stop
}

function global:01c1a57716fe4005ac1a7bf216f38ad0
{
return 'Original Function'
}

function script:Testing
{
return 'Script scope'
}

Describe 'Mocking Global Functions - Part One' {
Mock $functionName {
return 'Mocked'
}

It 'Mocks the global function' {
& $functionName | Should Be 'Mocked'
}
}
23 changes: 23 additions & 0 deletions Functions/GlobalMock-B.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# This test depends on some state set up in GlobalMock-A.Tests.ps1. The behavior we're verifying
# is that global functions that have been mocked are still properly set up even after the test
# script exits its scope.

$functionName = '01c1a57716fe4005ac1a7bf216f38ad0'

try
{
Describe 'Mocking Global Functions - Part Two' {
It 'Restored the global function properly' {
$globalFunctionExists = Test-Path Function:\global:$functionName
$globalFunctionExists | Should Be $true
& $functionName | Should Be 'Original Function'
}
}
}
finally
{
if (Test-Path Function:\$functionName)
{
Remove-Item Function:\$functionName -Force
}
}
61 changes: 50 additions & 11 deletions Functions/Mock.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -261,17 +261,22 @@ about_Mocking
Metadata = $metadata
CallHistory = @()
DynamicParamScriptBlock = $dynamicParamScriptBlock
FunctionScope = ''
}

$mockTable["$ModuleName||$CommandName"] = $mock

if ($contextInfo.Command.CommandType -eq 'Function')
{
$mock['FunctionScope'] = $contextInfo.Scope

$scriptBlock =
{
if ($ExecutionContext.InvokeProvider.Item.Exists("Function:\$args"))
param ( [string] $CommandName )

if ($ExecutionContext.InvokeProvider.Item.Exists("Function:\$CommandName"))
{
$ExecutionContext.InvokeProvider.Item.Rename("Function:\$args", "script:PesterIsMocking_$args", $true)
$ExecutionContext.InvokeProvider.Item.Rename("Function:\$CommandName", "script:PesterIsMocking_$CommandName", $true)
}
}

Expand Down Expand Up @@ -547,12 +552,15 @@ function Exit-MockScope {

$scriptBlock =
{
param ([string] $CommandName)
param (
[string] $CommandName,
[string] $Scope
)

$ExecutionContext.InvokeProvider.Item.Remove("Function:\$CommandName", $false, $true, $true)
if ($ExecutionContext.InvokeProvider.Item.Exists("Function:\PesterIsMocking_$CommandName", $true, $true))
{
$ExecutionContext.InvokeProvider.Item.Rename("Function:\PesterIsMocking_$CommandName", "script:$CommandName", $true)
$ExecutionContext.InvokeProvider.Item.Rename("Function:\PesterIsMocking_$CommandName", "$Scope$CommandName", $true)
}
}

Expand All @@ -565,7 +573,7 @@ function Exit-MockScope {

if ($null -eq $parentScope)
{
$null = Invoke-InMockScope -SessionState $mock.SessionState -ScriptBlock $scriptBlock -ArgumentList $mock.CommandName
$null = Invoke-InMockScope -SessionState $mock.SessionState -ScriptBlock $scriptBlock -ArgumentList $mock.CommandName, $mock.FunctionScope
$mockTable.Remove($mockKey)
}
else
Expand All @@ -582,36 +590,67 @@ function Validate-Command([string]$CommandName, [string]$ModuleName) {
$module = $null
$origCommand = $null

$commandInfo = New-Object psobject -Property @{ Command = $null; Scope = '' }

$scriptBlock = {
$command = $ExecutionContext.InvokeCommand.GetCommand($args[0], 'All')
while ($null -ne $command -and $command.CommandType -eq [System.Management.Automation.CommandTypes]::Alias)
{
$command = $command.ResolvedCommand
}
return $command

$properties = @{
Command = $command
}

if ($null -ne $command -and $command.CommandType -eq 'Function')
{
if ($ExecutionContext.InvokeProvider.Item.Exists("function:\global:$($command.Name)") -and
(Get-Content "function:\global:$($command.Name)") -eq $command.ScriptBlock)
{
$properties['Scope'] = 'global:'
}
elseif ($ExecutionContext.InvokeProvider.Item.Exists("function:\script:$($command.Name)") -and
(Get-Content "function:\script:$($command.Name)") -eq $command.ScriptBlock)
{
$properties['Scope'] = 'script:'
}
else
{
$properties['Scope'] = ''
}
}

return New-Object psobject -Property $properties
}

if ($ModuleName) {
$module = Get-ScriptModule -ModuleName $ModuleName -ErrorAction Stop
$origCommand = & $module $scriptBlock $CommandName
$commandInfo = & $module $scriptBlock $CommandName
}

$session = $pester.SessionState

if (-not $origCommand) {
if (-not $commandInfo.Command) {
Set-ScriptBlockScope -ScriptBlock $scriptBlock -SessionState $session
$origCommand = & $scriptBlock $commandName
$commandInfo = & $scriptBlock $commandName
}

if (-not $origCommand) {
if (-not $commandInfo.Command) {
throw ([System.Management.Automation.CommandNotFoundException] "Could not find Command $commandName")
}

if ($module) {
$session = & $module { $ExecutionContext.SessionState }
}

@{Command = $origCommand; Session = $session}
$hash = @{Command = $commandInfo.Command; Session = $session}
if ($commandInfo.Command.CommandType -eq 'Function')
{
$hash['Scope'] = $commandInfo.Scope
}

return $hash
}

function MockPrototype {
Expand Down

0 comments on commit 4625759

Please sign in to comment.