Skip to content

Commit

Permalink
Merge branch 'master' into MockFix3
Browse files Browse the repository at this point in the history
  • Loading branch information
nohwnd authored Mar 23, 2017
2 parents 04c22cc + e4e6075 commit 7893e71
Show file tree
Hide file tree
Showing 9 changed files with 335 additions and 300 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## 4.0.3-rc (March 22, 2017)
- Fix context and describe in test results [GH-755]
- Fix mocking functions in two consequitive contexts [GH-744]
- Fix import localized data on non en-US systems [GH-711]
- Fix IncludeVSCodeMarker [GH-726]
- Fix should be when working with distinct types [GH-704]
- Add commas to output [GH-690]
- Updated help and other small fixes

## 4.0.2-rc (January 18, 2017)
- Fix build script that builds the package for PowerShell gallery to include lib

Expand Down
21 changes: 19 additions & 2 deletions Functions/Assertions/Should.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,25 @@ function New-ShouldErrorRecord ([string] $Message, [string] $File, [string] $Lin
return $errorRecord
}

function Should
{
function Should {
<#
.SYNOPSIS
Should is a keyword what is used to define an assertion inside It block.
.DESCRIPTION
Should is a keyword what is used to define an assertion inside the It block.
Should provides assertion methods for verify assertion e.g. comparing objects.
If assertion is not met the test fails and an exception is throwed up.
Should can be used more than once in the It block if more than one assertion
need to be verified. Each Should keywords need to be located in a new line.
Test will be passed only when all assertion will be met (logical conjuction).
.LINK
about_Should
about_Pester
#>

[CmdletBinding(DefaultParameterSetName = 'Legacy')]
param (
[Parameter(ParameterSetName = 'Legacy', Position = 0)]
Expand Down
4 changes: 2 additions & 2 deletions Functions/Describe.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ function Describe {
Creates a logical group of tests.
.DESCRIPTION
Creates a logical group of tests. All Mocks and TestDrive contents
Creates a logical group of tests. All Mocks and TestDrive contents
defined within a Describe block are scoped to that Describe; they
will no longer be present when the Describe block exits. A Describe
block may contain any number of Context and It blocks.
Expand All @@ -19,7 +20,6 @@ this typically holds the arrange and act sections. The Asserts will also lie
in this block but are typically nested each in its own It block. Assertions are
typically performed by the Should command within the It blocks.
.PARAMETER Tag
Optional parameter containing an array of strings. When calling Invoke-Pester,
it is possible to specify a -Tag parameter which will only execute Describe blocks
Expand Down
46 changes: 30 additions & 16 deletions Functions/Mock.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,6 @@ function PipelineInputFunction {
}
}

Describe "When calling Mock on existing function" {
Mock FunctionUnderTest { return "I am the mock test that was passed $param1"}

$result = FunctionUnderTest "boundArg"

It "Should rename function under test" {
$renamed = (Test-Path function:PesterIsMocking_FunctionUnderTest)
$renamed | Should Be $true
}

It "Should Invoke the mocked script" {
$result | Should Be "I am the mock test that was passed boundArg"
}
}

Describe "When the caller mocks a command Pester uses internally" {
Mock Write-Host { }

Expand Down Expand Up @@ -282,7 +267,7 @@ Describe "When calling Mock on existing cmdlet to handle pipelined input" {
Describe "When calling Mock on existing cmdlet with Common params" {
Mock CommonParamFunction

$result=[string](Get-Content function:\CommonParamFunction)
$result=[string](Get-Alias CommonParamFunction).ResolvedCommand.ScriptBlock

It "Should strip verbose" {
$result.contains("`${Verbose}") | Should Be $false
Expand Down Expand Up @@ -1759,3 +1744,32 @@ Describe 'Assert-MockCalled when mock called outside of It block' {
}
}
}

Describe "Restoring original commands when mock scopes exit" {
function a (){}
Context "first context" {
Mock a { "mock" }

# Deliberately not using "Should Exist" here because that executes in
# Pester's module scope, where function:\a does not exist
It "original function exists" {
$function:a | Should -Not -Be $null
}

It "passes in first context" {
a | Should Be "mock"
}
}

Context "second context" {
Mock a { "mock" }

It "original function exists" {
$function:a | Should -Not -Be $null
}

It "passes in second context" {
a | Should Be "mock"
}
}
}
104 changes: 39 additions & 65 deletions Functions/Mock.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ about_Mocking
$cmdletBinding = $cmdletBinding.Insert($cmdletBinding.Length-2, 'PositionalBinding=$false')
}

$paramBlock = [Management.Automation.ProxyCommand]::GetParamBlock($metadata)
$paramBlock = [Management.Automation.ProxyCommand]::GetParamBlock($metadata)

if ($contextInfo.Command.CommandType -eq 'Cmdlet')
{
Expand Down Expand Up @@ -331,42 +331,35 @@ about_Mocking
Metadata = $metadata
CallHistory = @()
DynamicParamScriptBlock = $dynamicParamScriptBlock
FunctionScope = ''
Alias = $null
Aliases = @()
BootstrapFunctionName = [Guid]::NewGuid().ToString()
}

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

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

$scriptBlock =
{
param ( [string] $CommandName )
$scriptBlock = { $ExecutionContext.InvokeProvider.Item.Set("Function:\script:$($args[0])", $args[1], $true, $true) }
$null = Invoke-InMockScope -SessionState $mock.SessionState -ScriptBlock $scriptBlock -ArgumentList $Mock.BootstrapFunctionName, $mockScript

if ($ExecutionContext.InvokeProvider.Item.Exists("Function:\$CommandName", $true, $true))
{
$ExecutionContext.InvokeProvider.Item.Rename([System.Management.Automation.WildcardPattern]::Escape("Function:\$CommandName"), "script:PesterIsMocking_$CommandName", $true)
}
}
$mock.Aliases += $CommandName

$null = Invoke-InMockScope -SessionState $mock.SessionState -ScriptBlock $scriptBlock -ArgumentList $CommandName
$scriptBlock = {
$setAlias = & (Pester\SafeGetCommand) -Name Set-Alias -CommandType Cmdlet -Module Microsoft.PowerShell.Utility
& $setAlias -Name $args[0] -Value $args[1] -Scope Script
}

$scriptBlock = { $ExecutionContext.InvokeProvider.Item.Set("Function:\script:$($args[0])", $args[1], $true, $true) }
$null = Invoke-InMockScope -SessionState $mock.SessionState -ScriptBlock $scriptBlock -ArgumentList $CommandName, $mockScript
$null = Invoke-InMockScope -SessionState $mock.SessionState -ScriptBlock $scriptBlock -ArgumentList $CommandName, $mock.BootstrapFunctionName

if ($mock.OriginalCommand.ModuleName)
{
$mock.Alias = "$($mock.OriginalCommand.ModuleName)\$($CommandName)"
$aliasName = "$($mock.OriginalCommand.ModuleName)\$($CommandName)"
$mock.Aliases += $aliasName

$scriptBlock = {
$setAlias = & (Pester\SafeGetCommand) -Name Set-Alias -CommandType Cmdlet -Module Microsoft.PowerShell.Utility
& $setAlias -Name $args[0] -Value $args[1] -Scope Script
}

$null = Invoke-InMockScope -SessionState $mock.SessionState -ScriptBlock $scriptBlock -ArgumentList $mock.Alias, $CommandName
$null = Invoke-InMockScope -SessionState $mock.SessionState -ScriptBlock $scriptBlock -ArgumentList $aliasName, $mock.BootstrapFunctionName
}
}

Expand Down Expand Up @@ -757,19 +750,17 @@ function Exit-MockScope {
{
param (
[string] $CommandName,
[string] $Scope,
[string] $Alias
[string[]] $Aliases
)

$ExecutionContext.InvokeProvider.Item.Remove("Function:\$CommandName", $false, $true, $true)
if ($ExecutionContext.InvokeProvider.Item.Exists("Function:\PesterIsMocking_$CommandName", $true, $true))
{
$ExecutionContext.InvokeProvider.Item.Rename([System.Management.Automation.WildcardPattern]::Escape("Function:\PesterIsMocking_$CommandName"), "$Scope$CommandName", $true)
}

if ($Alias -and $ExecutionContext.InvokeProvider.Item.Exists("Alias:$Alias", $true, $true))
foreach ($alias in $Aliases)
{
$ExecutionContext.InvokeProvider.Item.Remove("Alias:$Alias", $false, $true, $true)
if ($ExecutionContext.InvokeProvider.Item.Exists("Alias:$alias", $true, $true))
{
$ExecutionContext.InvokeProvider.Item.Remove("Alias:$alias", $false, $true, $true)
}
}
}

Expand All @@ -782,7 +773,7 @@ function Exit-MockScope {
$shouldRemoveMock = (-not $ExitTestCaseOnly) -and (ShouldRemoveMock -Mock $mock -ActivePesterState $pester)
if ($shouldRemoveMock)
{
$null = Invoke-InMockScope -SessionState $mock.SessionState -ScriptBlock $removeMockStub -ArgumentList $mock.CommandName, $mock.FunctionScope, $mock.Alias
$null = Invoke-InMockScope -SessionState $mock.SessionState -ScriptBlock $removeMockStub -ArgumentList $mock.BootstrapFunctionName, $mock.Aliases
$mockTable.Remove($mockKey)
}
elseif ($mock.PesterState -eq $pester)
Expand Down Expand Up @@ -831,69 +822,52 @@ function ShouldRemoveMock($Mock, $ActivePesterState)

function Validate-Command([string]$CommandName, [string]$ModuleName) {
$module = $null
$origCommand = $null

$commandInfo = & $SafeCommands['New-Object'] psobject -Property @{ Command = $null; Scope = '' }
$command = $null

$scriptBlock = {
$getContentCommand = & (Pester\SafeGetCommand) Get-Content -Module Microsoft.PowerShell.Management -CommandType Cmdlet
$newObjectCommand = & (Pester\SafeGetCommand) New-Object -Module Microsoft.PowerShell.Utility -CommandType Cmdlet

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

$properties = @{
Command = $command
}

if ($null -ne $command -and $command.CommandType -eq 'Function')
{
if ($ExecutionContext.InvokeProvider.Item.Exists("function:\global:$($command.Name)", $true, $true) -and
(& $getContentCommand -LiteralPath "function:\global:$($command.Name)" -ErrorAction Stop) -eq $command.ScriptBlock)
{
$properties['Scope'] = 'global:'
}
elseif ($ExecutionContext.InvokeProvider.Item.Exists("function:\script:$($command.Name)", $true, $true) -and
(& $getContentCommand -LiteralPath "function:\script:$($command.Name)" -ErrorAction Stop) -eq $command.ScriptBlock)
{
$properties['Scope'] = 'script:'
}
else
{
$properties['Scope'] = ''
}
}

return & $newObjectCommand psobject -Property $properties
return $command
}

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

$session = $pester.SessionState

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

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

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

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

if ($command.CommandType -eq 'Function')
{
$hash['Scope'] = $commandInfo.Scope
foreach ($mock in $mockTable.Values)
{
if ($command.Name -eq $mock.BootstrapFunctionName)
{
return @{
Command = $mock.OriginalCommand
Session = $mock.SessionState
}
}
}
}

return $hash
Expand Down
8 changes: 6 additions & 2 deletions Functions/PesterState.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,11 @@ function New-PesterState
$describe = ''
$contexts = [System.Collections.ArrayList]@()

foreach ($group in $script:TestGroupStack.GetEnumerator())
# make a copy of the stack and reverse it
$reversedStack = $script:TestGroupStack.ToArray()
[array]::Reverse($reversedStack)

foreach ($group in $reversedStack)
{
if ($group.Hint -eq 'Root' -or $group.Hint -eq 'Script') { continue }
if ($describe -eq '')
Expand All @@ -196,7 +200,7 @@ function New-PesterState
}
}

$context = $contexts -join '/'
$context = $contexts -join '\'

$script:TestResult += & $SafeCommands['New-Object'] psobject -Property @{
Describe = $describe
Expand Down
Loading

0 comments on commit 7893e71

Please sign in to comment.