用 Pester 完善 PowerShell 测试 ── 让运维脚本不易损坏的实务方法

· · PowerShell, Pester, Windows, 测试, 自动化, CI, 存量资产利用

1. 首先要弄清楚的事

PowerShell 脚本,往往一开始只是一些小型工作的自动化。

收集文件。 搜索日志。 生成 CSV。 搬移旧文件。 确认服务状态。

这些工作只要几十行,用眼睛看就能确认动作是否正确。但在实务中持续使用之后,就会出现这些变化。

  • 增加要处理的文件夹
  • 加入排除条件
  • 修改 CSV 的列
  • 删除前先归档
  • 从任务计划程序或 CI 中执行
  • 出错时要发出通知

到了这个阶段,「曾经在自己手边跑起来过」已经不够了。

PowerShell 的可怕之处,恰恰和它的便利性是一体两面。 如果只是读取数据,可以放心尝试,但删除、移动、覆盖、重启服务、变更权限这类操作,只要条件判断出一点差错,就会直接变成事故。

这时就该用上 PowerShell 的测试框架 Pester。本文不打算覆盖 Pester 的所有功能,而是整理如何为现有的 PowerShell 脚本,在实务中做「测试完善」,让它们变得不容易损坏。

PowerShell 的测试,不只是为了写出干净的代码。它是在修改之前减少不安、在修改之后能有依据地进行确认的工具。

顺带一提,本文出现的代码,已经作为一整套可用 Invoke-Pester 执行的示例(测试对象脚本、Pester 测试、CI 用执行脚本)发布在 GitHub 上。

pester-powershell-test-maintenance - komurasoft-blog-samples (GitHub)

2. Pester 要守护什么

引入 Pester,并不代表一切都会自动变得安全。首先该决定的是「用测试守护什么」。

在 PowerShell 的运维脚本中,优先确认这 4 项,通常最容易见效。

想守护的事 测试要看的内容
条件判断 以哪些文件、行、用户、服务为对象
输出格式 输出到 CSV 的列名、返回值的属性、条数
危险操作之前 删除、移动、停止的对象是否符合预期
外部依赖 文件系统、API、命令执行、日期时间、环境变量的处理

尤其最先该测试的,不是删除处理本身,而是挑选删除对象的处理。

举例来说,如果是删除旧日志的脚本,不要一开始就直接测试 Remove-Item,而是先测试「哪些日志会被选为对象」。

用这种拆分方式,会更容易测试。

收集对象的函数
  ↓
确认、记录对象的处理
  ↓
移动、删除、通知等变更处理

PowerShell 的测试完善,并不是要立刻对现有脚本做大改造。首先,把危险处理之前的判断部分拆成函数,用 Pester 确认它的返回值。

3. 先统一版本

本文以 Pester v5 为前提。

在比较旧的 Windows 环境中,即使原本就预装了 Pester,也可能是 v3 系列。不要直接沿用现有环境里安装的版本,先确认版本号。

Get-Module Pester -ListAvailable |
  Sort-Object Version -Descending |
  Select-Object Name, Version, Path

如果要重新安装,可以从 PowerShell Gallery 安装。

Install-Module -Name Pester -Scope CurrentUser -Force -SkipPublisherCheck
Import-Module Pester
Get-Module Pester

团队使用时,要确认开发机、构建服务器、任务执行环境中的 Pester 版本是否一致。

PowerShell 测试中常见的混乱,往往不是代码本身的问题,而是来自测试运行器版本的差异。

尤其是旧文章或内部笔记中,可能还残留着 Pester v4 及更早的写法。如果要重新完善,建议向 v5 的写法靠拢,后续会更容易维护。

4. 确定文件的存放位置

在 Pester 中,一般会把测试文件命名为 *.Tests.ps1

最小的配置如下:

scripts/
  Get-OldLogFile.ps1
  Get-OldLogFile.Tests.ps1

规模稍大一点时,可以把 srctests 分开。

src/
  public/
    Get-OldLogFile.ps1
    Remove-OldLogFile.ps1

tests/
  public/
    Get-OldLogFile.Tests.ps1
    Remove-OldLogFile.Tests.ps1

两种方式都可以,重要的是先定好规则。

  • 每个函数对应一个测试文件
  • 测试文件名以 .Tests.ps1 结尾
  • 统一测试对象的加载方式
  • 不要把单元测试和集成测试混在一起

一开始,把测试对象的 .ps1 和测试文件 .Tests.ps1 放在同一目录下就足够了。

5. 跑通最小的测试

先准备一个简单的函数 Get-OldLogFile.ps1

function Get-OldLogFile {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string] $Path,

        [int] $Days = 30,

        [string] $Filter = '*.log',

        [datetime] $Now = (Get-Date)
    )

    if (-not (Test-Path -LiteralPath $Path -PathType Container)) {
        throw "Folder not found: $Path"
    }

    $limit = $Now.AddDays(-1 * $Days)

    Get-ChildItem -LiteralPath $Path -Filter $Filter -File |
        Where-Object { $_.LastWriteTime -lt $limit } |
        Sort-Object -Property LastWriteTime |
        Select-Object FullName, Name, Length, LastWriteTime
}

这里为了方便测试,让 $Now 可以通过参数传入。

如果在函数内部每次都直接调用 Get-Date,结果就会随测试执行日而变化。只要把日期做成参数,就能把「以 2026 年 6 月 1 日为基准、超过 30 天的文件」这种条件固定下来测试。

接下来,把测试写在 Get-OldLogFile.Tests.ps1 中。

BeforeAll {
    . $PSScriptRoot\Get-OldLogFile.ps1
}

Describe 'Get-OldLogFile' {
    BeforeEach {
        $script:Root = Join-Path $TestDrive 'logs'
        New-Item -ItemType Directory -Path $script:Root -Force | Out-Null

        $oldLog = Join-Path $script:Root 'old.log'
        $newLog = Join-Path $script:Root 'new.log'
        $oldTxt = Join-Path $script:Root 'old.txt'

        Set-Content -LiteralPath $oldLog -Value 'old log' -Encoding UTF8
        Set-Content -LiteralPath $newLog -Value 'new log' -Encoding UTF8
        Set-Content -LiteralPath $oldTxt -Value 'old text' -Encoding UTF8

        (Get-Item -LiteralPath $oldLog).LastWriteTime = [datetime]'2026-05-01T00:00:00'
        (Get-Item -LiteralPath $newLog).LastWriteTime = [datetime]'2026-05-31T00:00:00'
        (Get-Item -LiteralPath $oldTxt).LastWriteTime = [datetime]'2026-05-01T00:00:00'
    }

    It '只返回超过指定天数的 .log 文件' {
        $result = Get-OldLogFile `
            -Path $script:Root `
            -Days 30 `
            -Now ([datetime]'2026-06-01T00:00:00')

        $result | Should -HaveCount 1
        $result[0].Name | Should -Be 'old.log'
    }

    It '对不存在的文件夹会失败' {
        { Get-OldLogFile -Path (Join-Path $TestDrive 'missing') } |
            Should -Throw
    }
}

执行测试。

Invoke-Pester -Output Detailed .\Get-OldLogFile.Tests.ps1

这里用到的 $TestDrive,是 Pester 提供的测试用临时区域。不需要动用真正的 C:\Logs 或共享文件夹,就能使用仅在测试中创建的文件。对于包含文件操作的 PowerShell 脚本,先养成使用 $TestDrive 的习惯会更安全。

6. 把测试名称写成规格

Pester 的 It 里写的字符串,不只是单纯的说明,对日后阅读的人来说,其实是一份小小的规格书。

举例来说,这样的命名就有点弱。

It 'works' {
    # ...
}

看不出来到底该正常运行的是什么。

在实务中,把条件和期望结果写进名称,会更容易阅读。

It '只返回超过指定天数的 .log 文件' {
    # ...
}

It '刚好到期日的文件不视为对象' {
    # ...
}

It '对不存在的文件夹会失败' {
    # ...
}

好的测试名称,在失败时特别管用。CI 的日志一旦出现这样的信息,就能马上知道坏掉的是哪里。

[-] Get-OldLogFile.刚好到期日的文件不视为对象

测试名称,其实是写给未来的自己的备忘录。

7. 补一个边界条件

前面的 Get-OldLogFile,是用这个条件判断旧文件的。

$_.LastWriteTime -lt $limit

由于用的是 -lt,和到期日刚好相同时间的文件不会被视为对象。

这个判断看起来很小,但在实务中很重要。因为「超过 30 天」和「包含第 30 天」,结果的条数会不一样。

把边界条件加入测试。

It '刚好到期日的文件不视为对象' {
    $border = Join-Path $script:Root 'border.log'
    Set-Content -LiteralPath $border -Value 'border log' -Encoding UTF8
    (Get-Item -LiteralPath $border).LastWriteTime = [datetime]'2026-05-02T00:00:00'

    $result = Get-OldLogFile `
        -Path $script:Root `
        -Days 30 `
        -Now ([datetime]'2026-06-01T00:00:00')

    $result.Name | Should -Not -Contain 'border.log'
}

测试不是写得越多越好。不过,日期、数字、条数、权限、文件名模式这类带有边界的处理,测试的价值特别高。

8. 固定返回值的形状

PowerShell 脚本的返回值形状,有时会在不知不觉间发生变化。

一开始是直接返回 FileInfo。 中途加入了 Select-Object。 后来为了 CSV 又改了列名。

这类变更会影响后续处理,所以先测试返回值的属性,就能察觉到未预期的变化。

It '返回后续处理需要的属性' {
    $result = Get-OldLogFile `
        -Path $script:Root `
        -Days 30 `
        -Now ([datetime]'2026-06-01T00:00:00')

    $propertyNames = $result[0].PSObject.Properties.Name

    $propertyNames | Should -Contain 'FullName'
    $propertyNames | Should -Contain 'Name'
    $propertyNames | Should -Contain 'Length'
    $propertyNames | Should -Contain 'LastWriteTime'
}

对于传给 CSV 输出或报表生成的函数,不仅值本身,列名也是规格的一部分。

要确认的不只是「跑起来了」,而是「以后续处理所期望的形状返回」。

9. 把删除处理和对象选定分开

接下来看删除处理。先看一个不太好的例子。

Get-ChildItem C:\Logs -Filter *.log -File |
    Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
    Remove-Item -Force

写法简短又方便,但不容易测试。因为对象选定和删除串在同一条管道里,很难分清楚该确认哪一部分。

在实务中,会这样拆开:

function Remove-OldLogFile {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        [string] $Path,

        [int] $Days = 30,

        [datetime] $Now = (Get-Date)
    )

    $targets = Get-OldLogFile -Path $Path -Days $Days -Now $Now

    foreach ($target in $targets) {
        if ($PSCmdlet.ShouldProcess($target.FullName, 'Remove old log file')) {
            Remove-Item -LiteralPath $target.FullName -Force
        }
    }
}

这里加上了 SupportsShouldProcess,让函数端可以接受 -WhatIf

Remove-OldLogFile -Path C:\Logs -Days 30 -WhatIf

对于删除类的 PowerShell 函数,尽可能设计成能用 -WhatIf 先行演练,会更安全。

10. 用 Mock 替代危险操作

在 Pester 中,可以用 Mock 替代实际的命令执行。

测试删除处理时,不需要真的执行 Remove-Item

该调用的场景有没有被调用。 不该调用的场景有没有被误调用。

只要看这两点就够了。

以下是 Remove-OldLogFile.Tests.ps1 的示例。

BeforeAll {
    . $PSScriptRoot\Get-OldLogFile.ps1
    . $PSScriptRoot\Remove-OldLogFile.ps1
}

Describe 'Remove-OldLogFile' {
    It '对旧日志文件调用 Remove-Item' {
        Mock Get-OldLogFile {
            [pscustomobject]@{
                FullName      = 'C:\Logs\old.log'
                Name          = 'old.log'
                Length        = 10
                LastWriteTime = [datetime]'2026-05-01'
            }
        }

        Mock Remove-Item {}

        Remove-OldLogFile `
            -Path 'C:\Logs' `
            -Days 30 `
            -Now ([datetime]'2026-06-01')

        Should -Invoke Remove-Item `
            -Times 1 `
            -Exactly `
            -ParameterFilter { $LiteralPath -eq 'C:\Logs\old.log' }
    }

    It '使用 WhatIf 时不调用 Remove-Item' {
        Mock Get-OldLogFile {
            [pscustomobject]@{
                FullName      = 'C:\Logs\old.log'
                Name          = 'old.log'
                Length        = 10
                LastWriteTime = [datetime]'2026-05-01'
            }
        }

        Mock Remove-Item {}

        Remove-OldLogFile `
            -Path 'C:\Logs' `
            -Days 30 `
            -Now ([datetime]'2026-06-01') `
            -WhatIf

        Should -Invoke Remove-Item -Times 0
    }
}

在这个测试中,Get-OldLogFileRemove-Item 都被 Mock 掉了,所以就算真正的 C:\Logs\old.log 不存在也没关系。要确认的是 Remove-OldLogFile 的判断逻辑。

  • 有对象时就调用 Remove-Item
  • 使用 -WhatIf 时不调用 Remove-Item
  • 调用时传入的是预期的路径

危险程度越高的处理,测试调用条件,会比测试真正执行本身更安全。

11. 不要过度使用 Mock

Mock 很方便,但用得太多会让测试失去价值。因为如果全部都用 Mock,就会离真实的 PowerShell 行为太远。

大致的参考标准如下。

处理 建议做法
日期 用参数固定
创建文件 使用 $TestDrive
删除、移动 Mock-WhatIf 确认
调用 Web API Invoke-RestMethod 等做 Mock
发送邮件、通知 对发送用命令做 Mock
CSV 读写 $TestDrive 中创建小型的真实文件

如果连文件读写都全部 Mock 掉,可能会漏掉真正的字符编码、换行、列名之类的问题。

另一方面,删除、通知、外部 API、停止服务这类处理,最好真的不要去执行。

要把「使用真实对象的地方」和「Mock 的地方」区分开来。

12. 把现有脚本改成便于测试的样子

引入 Pester 之后,现有脚本的写法也会稍微发生变化。不过一开始不需要做大规模的设计变更,先做到这个程度就足够了。

修改前

$limit = (Get-Date).AddDays(-30)

Get-ChildItem C:\Logs -Filter *.log -File |
    Where-Object { $_.LastWriteTime -lt $limit } |
    Remove-Item -Force

修改后

function Get-OldLogFile {
    param(
        [string] $Path,
        [int] $Days = 30,
        [datetime] $Now = (Get-Date)
    )

    $limit = $Now.AddDays(-1 * $Days)

    Get-ChildItem -LiteralPath $Path -Filter *.log -File |
        Where-Object { $_.LastWriteTime -lt $limit }
}

function Remove-OldLogFile {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [string] $Path,
        [int] $Days = 30,
        [datetime] $Now = (Get-Date)
    )

    Get-OldLogFile -Path $Path -Days $Days -Now $Now |
        ForEach-Object {
            if ($PSCmdlet.ShouldProcess($_.FullName, 'Remove old log file')) {
                Remove-Item -LiteralPath $_.FullName -Force
            }
        }
}

改动的地方并不大。

  • 把日期改成参数
  • 把对象选定变成函数
  • 把删除处理拆成另一个函数
  • 加上 SupportsShouldProcess

仅凭这些,就会变得容易测试很多。

在 PowerShell 的测试完善中,与其从设计理论谈起,把「日期」「路径」「外部命令」「变更操作」变成可以从外部替换,会更有效果。

13. 确定测试的分类

在 Pester 中,可以给 DescribeContextIt 打标签。

例如,把快速的单元测试和真正接触环境的集成测试分开。

Describe 'Get-OldLogFile' -Tag 'Unit' {
    It '只返回超过指定天数的 .log 文件' {
        # 使用 TestDrive 的快速测试
    }
}

Describe 'Log maintenance smoke test' -Tag 'Smoke' {
    It '可以读取真正的日志文件夹' {
        Test-Path -LiteralPath 'C:\Logs' | Should -BeTrue
    }
}

只执行单元测试。

Invoke-Pester -TagFilter Unit

排除较慢或依赖环境的测试。

Invoke-Pester -ExcludeTagFilter Slow, RequiresAdmin, Network

在实际工作中,如果每次都想跑完所有测试,有时会难以持续下去。

先把快速又没有副作用的测试设为标准,再用标签把依赖环境的测试区分出来,在需要的时机再执行。

14. 在 CI 上运行

Pester 就算只在本地运行也很有帮助,但如果要以团队方式管理脚本,能在 CI 上运行会更让人安心。

例如,可以准备一个类似 tools/Invoke-ProjectTests.ps1 的文件。

$ErrorActionPreference = 'Stop'

Import-Module Pester

$config = New-PesterConfiguration

$config.Run.Path = @(
    Join-Path $PSScriptRoot '..\tests'
)

$config.Run.Exit = $true
$config.Output.Verbosity = 'Detailed'

$config.TestResult.Enabled = $true
$config.TestResult.OutputFormat = 'JUnitXml'
$config.TestResult.OutputPath = Join-Path $PSScriptRoot '..\test-results.xml'

$config.CodeCoverage.Enabled = $true
$config.CodeCoverage.Path = @(
    Join-Path $PSScriptRoot '..\src'
)
$config.CodeCoverage.OutputPath = Join-Path $PSScriptRoot '..\coverage.xml'

Invoke-Pester -Configuration $config

在 CI 端,执行这个脚本。

pwsh -NoProfile -File .\tools\Invoke-ProjectTests.ps1

重点是,不要把 CI 专属的配置写得太深入到测试文件里。

测试文件是用来写规格的地方。 CI 用的输出格式、覆盖率、退出代码等,整理到执行用脚本里会更清晰。

15. 把覆盖率当作地图,而不是目标

Pester 也能输出代码覆盖率。不过,最好不要一开始就过度追逐数字。因为覆盖率本身并不等同于测试的质量。

举例来说,只要调用一次删除对象的提取函数,那一行就算通过了。但如果边界条件或排除条件没有被确认过,并不会带来实务上的安心感。

覆盖率可以这样使用。

  • 找出完全没有被执行过的函数
  • 找出重要却没有测试的分支
  • 按变更频率高的脚本排出优先级
  • 在 CI 中留下测试执行的证据

重点不在于把数字拉高,而是要看「重要的判断有没有被测试到」。

16. 导入现有脚本的顺序

要为现有的 PowerShell 资产导入 Pester 时,不要一开始就把整体都纳入测试范围,这样会更容易推进。下面列出建议的顺序。

1. 挑选失败会造成麻烦的脚本

一开始,适合挑选这类脚本。

  • 包含删除、移动、覆盖
  • 每天或每月都会运行
  • 写在操作手册上,但实际依赖特定人员
  • 过去曾发生过条件判断失误
  • 输出的 CSV 会被其他业务使用

从方便但坏掉会很麻烦的脚本开始。

2. 先把读取部分拆出来做成函数

一开始要测试的,不是变更处理,而是读取处理。

读取日志
筛选对象
统计条数
整理成 CSV 用的格式

这部分容易用 $TestDrive 测试,也不容易出事故。

3. 从外部传入日期和路径

如果日期或路径被写死,就很难测试。

# 想避免的写法
$root = 'C:\Logs'
$limit = (Get-Date).AddDays(-30)

便于测试的写法。

param(
    [string] $Path,
    [datetime] $Now = (Get-Date)
)

只要能从外部传入值,测试的稳定性就会大幅提升。

4. 把危险操作放到最后

删除、移动这类操作,统一放到最后处理。

创建对象
  ↓
把对象记录下来
  ↓
用 -WhatIf 先行确认
  ↓
真正执行

测试时也按相同的顺序确认。

17. 常见的卡壳情况

症状 原因 应对方法
本地能通过但 CI 失败 当前工作目录不同 $PSScriptRoot 为基准
结果每天都不一样 直接使用了 Get-Date 准备类似 -Now 的参数
测试差点删掉真实文件 使用了真实文件夹 使用 $TestDriveMock
Mock 没有生效 模块边界或作用域不同 检查 -ModuleName 或加载方式
不知道该测到什么程度 规格没有拆分到函数里 拆成对象选定、整形、变更处理
测试很慢 接触了外部服务或网络 单元测试中对外部依赖做 Mock
看测试名称看不出内容 名称写成 It 'works' 这种形式 把条件和期望结果写进名称

表面上看起来是 Pester 的问题,实际上很多时候是脚本结构本身造成的。

不容易测试的部分,通常在运维上也是容易出问题的部分。

18. 测试完善中要先定好的规则

如果要以团队方式管理 PowerShell 脚本,比起细节写法,更应该优先定好规则。

例如可以订立这类规则。

  • 测试文件统一命名为 *.Tests.ps1
  • 测试对象一律从 $PSScriptRoot 加载
  • 文件操作的测试使用 $TestDrive
  • 删除、移动、通知、API 调用原则上都做 Mock
  • 把日期做成参数,让它可以被固定
  • DescribeIt 上加 UnitSmokeRequiresAdmin 等标签
  • CI 中以 Unit 为标准执行
  • 对变更类函数尽可能加上 SupportsShouldProcess
  • 把过去的问题留下来作为回归测试

规则太多反而守不住。一开始,光靠这 3 条就足够了。

使用 TestDrive
固定日期
危险操作做 Mock

只要守住这 3 条,PowerShell 的测试就会相当稳定。

19. 也要确定哪些不测试

在测试完善中,「测试什么」和「不测试什么」同样重要。

举例来说,下面这些内容不需要在单元测试中过度确认。

  • Windows 本身的 Get-ChildItem 是否正常运行
  • Remove-Item 是否真的会删除文件
  • PowerShell 标准命令的内部规格
  • 外部 API 是否一直有响应
  • 网络共享是否一直可用

该测试的,是自己团队的判断逻辑。

  • 用什么条件确定对象
  • 传入哪个路径
  • 输出哪些列
  • 失败时该如何处理
  • 危险操作能不能先行演练

把「信任标准命令的部分」和「守护自己逻辑的部分」区分开来。

20. 总结

PowerShell 是能快速自动化日常工作的便利工具。但在实务中长期使用的脚本,责任会一点点变重。一开始只是自己使用的单行命令,逐渐变成每天运行的运维处理,最终开始影响其他人的工作和业务数据。

用 Pester 做测试完善,就是配合这种变化来守护脚本的工作。

把要点列出来。

  • 先测试对象选定
  • 让日期和路径可以从外部传入
  • 把文件操作封闭在 $TestDrive
  • 对删除、移动、通知、API 调用做 Mock
  • 让变更类函数可以用 -WhatIf 先行演练
  • 让测试名称可以当作规格来读
  • 在 CI 中先跑快速又没有副作用的测试

PowerShell 的安全运维,不是要一次性引入庞大的机制。

拆成小函数。 写小测试。 在危险处理之前,确保能够先确认状态。

不断积累这些做法,PowerShell 就能从「方便但有点可怕的脚本」,逐渐变成「改动后也能确认的业务工具」。

参考链接

相关文章

共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。

常见问题

汇总了咨询这一主题时常见的问题。

使用 Pester 时,最先该测试什么?
不是删除处理本身,而是从挑选删除对象的处理(对象选定)开始测试。运维脚本中,优先确认「条件判断」「输出格式」「危险操作之前」「外部依赖」这 4 项,通常最容易见效。不需要一开始就对现有脚本做大幅改造,只要把危险处理之前的判断部分拆成函数,先用 Pester 确认它的返回值即可。
Pester 的 TestDrive 是什么?
`$TestDrive` 是 Pester 提供的测试用临时区域。不需要动用真正的 `C:\Logs` 或共享文件夹,只用测试中创建的文件就能验证文件操作。在包含文件操作的 PowerShell 脚本测试中,先养成使用 `$TestDrive` 的习惯,就能避免误删真实文件这类事故。
Pester 的 Mock 该用到什么程度?
删除、移动、调用 Web API、发送邮件、发送通知这类不该真正执行的处理,适合用 Mock 替代;日期则用参数固定;文件创建和 CSV 读写,比较适合在 `$TestDrive` 中创建小型的真实文件来处理。如果全部都用 Mock,会离真实的 PowerShell 行为太远,可能会漏掉字符编码、换行、列名之类的问题,因此重点在于把「使用真实对象的地方」和「Mock 的地方」区分开来。
为什么在本地能通过的 Pester 测试,在 CI 上却会失败?
常见原因是当前工作目录不同,把测试对象的加载基准改成 `$PSScriptRoot` 就能解决。如果结果每天都不一样,通常是因为直接使用了 `Get-Date`,可以用类似 `-Now` 的参数把日期固定下来。如果 Mock 不生效,要怀疑是模块边界或作用域不同,检查 `-ModuleName` 或加载方式。表面看起来是 Pester 的问题,实际上很多时候是脚本结构的问题。

作者简介

本文作者的个人简介页面。

Go Komura

小村软件有限公司 代表

以 Windows 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。

返回博客列表