Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 150 additions & 1 deletion versioned_docs/version-v5/usage/mocking.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,156 @@ Describe 'Mocking native commands' {
Should -Invoke -CommandName 'curl' -Exactly -Times 1 -ParameterFilter { "$args" -match '--url https://google.com -I' }
}
}
```

### Mocking function in scriptblock
Unfortunately, we don't have a straightforward way to mock a function inside a scriptblock, but we could use some workarounds to accomplish this.

warning:Mocking may not work correctly in Pester versions below v5, so this function is only supported in Pester v5. In earlier versions, you cannot run the mock together with the script.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this does not feel right, I wrote about how to do this long time ago, and I don't think there is anything in Pester 5 that would make this work, it is simple mocking.

https://jakubjares.com/2019/06/09/testing-whole-scripts/ https://jakubjares.com/2019/06/14/testing-whole-scripts-part2/

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there any problem with the script tester ?
As far as i concern the inscript work well

   Add-Type -TypeDefinition @'
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Reflection;

namespace ScriptMocking
{
    [Cmdlet(VerbsData.Import, "Script")]
    public sealed class ImportScriptCommand : PSCmdlet
    {
        private static string Body = @"
            function ____placeholder {{ }}
            # alias to prevent main from running
            Set-Alias {0} '____placeholder'

            . {1} {2}
            # remove the alias, now we have all functions
            # from the script dot-sourced in this scope
            Remove-Item 'alias:{0}' -Force
            Remove-Item 'function:____placeholder' -Force";

        [Parameter(Position = 0, Mandatory = true)]
        public string Path { get; set; }

        [Parameter(Position = 1)]
        public string EntryPoint { get; set; }

        [Parameter]
        public string[] ArgumentList { get; set; }

        public ImportScriptCommand() {
            ArgumentList = new string[0];
            EntryPoint = "Main";
        }
        protected override void ProcessRecord()
        {
            var body =  string.Format(Body, EntryPoint, Path, string.Join(" ", ArgumentList));

            var sb = InvokeCommand.NewScriptBlock(body);

            var method = sb.GetType()
                .GetMethod("InvokeUsingCmdlet", BindingFlags.Instance | BindingFlags.NonPublic);

            var emptyArray = new object[0];
            var automationNull = new PSObject();
            const int writeToCurrentErrorPipe = 1;

            var @params = new object[]
                {this, false, writeToCurrentErrorPipe, automationNull, emptyArray, automationNull, new object[0]};

            method.Invoke(sb, @params);
        }
    }
}




```
---t1.ps1---
# base https://github.com/pester/Pester/issues/1069
### --- file t1.ps1
param (
[Parameter(Mandatory)]
$Name
)

# our entry point function that holds
# all the stuff that should happen when
# we run this script
function Main {
param (
[Parameter(Mandatory)]
$Name
)
Write-Host $PSCommandPath
Get-Greeting $Name
}

# internal function that writes greeting
function Get-Greeting {
param ($Name)

$text = Get-GreetingText
$formattedGreetingText = $text -f $Name
Write-Host $formattedGreetingText
$formattedGreetingText
}

# another internal function that collaborates
# with Get-Greeting and that we will mock
# in our tests
function Get-GreetingText {
'Hello, {0}!'
}

Main -Name $Name
---------------
```

```
--t1.test.ps1--


#warning :Mocking may not work correctly in Pester versions below v5, so this function is only supported in Pester v5. In earlier versions, you cannot run the mock together with the script.

BeforeAll{

Add-Type -TypeDefinition @'
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Reflection;

namespace ScriptMocking
{
[Cmdlet(VerbsData.Import, "Script")]
public sealed class ImportScriptCommand : PSCmdlet
{
private static string Body = @"
function ____placeholder {{ }}
# alias to prevent main from running
Set-Alias {0} '____placeholder'

. {1} {2}
# remove the alias, now we have all functions
# from the script dot-sourced in this scope
Remove-Item 'alias:{0}' -Force
Remove-Item 'function:____placeholder' -Force";

[Parameter(Position = 0, Mandatory = true)]
public string Path { get; set; }

[Parameter(Position = 1)]
public string EntryPoint { get; set; }

[Parameter]
public string[] ArgumentList { get; set; }

public ImportScriptCommand() {
ArgumentList = new string[0];
EntryPoint = "Main";
}
protected override void ProcessRecord()
{
var body = string.Format(Body, EntryPoint, Path, string.Join(" ", ArgumentList));

var sb = InvokeCommand.NewScriptBlock(body);

var method = sb.GetType()
.GetMethod("InvokeUsingCmdlet", BindingFlags.Instance | BindingFlags.NonPublic);

var emptyArray = new object[0];
var automationNull = new PSObject();
const int writeToCurrentErrorPipe = 1;

var @params = new object[]
{this, false, writeToCurrentErrorPipe, automationNull, emptyArray, automationNull, new object[0]};

method.Invoke(sb, @params);
}
}
}
'@
Import-Module ([ScriptMocking.ImportScriptCommand].Assembly)
Import-Script -Path "$psScriptRoot/t1.ps1" -EntryPoint 'Main' -ArgumentList '-Name Jakub'


}
Describe 'test scriptMocking'{

Describe "t1.ps1" {
It "Can test 'Main' function" {
Main -Name 'Pester' | Should -Be 'Hello, Pester!'
}

It "Can test 'Get-Greeting' function" {
Get-Greeting -Name 'Pester' | Should -Be 'Hello, Pester!'
}

It "Can mock Get-GreetingText that is used by Get-GreetingFunction" {
Mock Get-GreetingText { '{0} is getting better at mocking!' }
Get-Greeting -Name 'Pester' | Should -Be 'Pester is getting better at mocking!'
}
}
}
<#
output :
Hello, Pester!
Hello, Pester!
Pester is getting better at mocking!
[+] O:\OneDrive\script-tester\mockFunction.test.ps1 363ms (119ms|202ms)
Tests completed in 370ms
Tests Passed: 3, Failed: 0, Skipped: 0, Inconclusive: 0, NotRun: 0
PS C:\Users\Administrator> Remove-Module -name pester -Force
#>




```

## $PesterBoundParameters
Expand Down Expand Up @@ -272,4 +422,3 @@ Describe "d" {
Should -Invoke f -Exactly 0
}
}
```