O Pester é o framework de testes padrão para PowerShell, essencial para garantir a qualidade e a confiabilidade dos seus scripts e módulos. Com ele, você pode escrever testes unitários, de integração e até mesmo testes de aceitação, seguindo uma sintaxe simples e poderosa. Nesta aula, vamos explorar os conceitos fundamentais do Pester, desde a instalação até a criação de testes com mocks, para que você possa incorporar testes em seu fluxo de desenvolvimento.

O que é Pester

Pester é um framework de teste e mocking para PowerShell, inspirado em ferramentas como RSpec (Ruby) e Jasmine (JavaScript). Ele permite que você escreva testes de forma declarativa, usando blocos Describe e It, e fornece uma vasta gama de asserções para validar o comportamento do seu código. O Pester também oferece suporte a mocking, permitindo simular dependências externas, como cmdlets ou funções, para isolar o código em teste.

Com o Pester, você pode automatizar a verificação de que seus scripts funcionam como esperado, facilitando a detecção de regressões e a manutenção do código. Ele é amplamente utilizado na comunidade PowerShell e é incluído por padrão em muitas ferramentas de CI/CD, como Azure DevOps e GitHub Actions.

Para instalar o Pester, você pode usar o PowerShell Gallery. No Windows PowerShell 5.1, o Pester já vem instalado, mas é recomendável atualizar para a versão mais recente:

Install-Module -Name Pester -Force -SkipPublisherCheck

No PowerShell 7+, você pode instalar ou atualizar da mesma forma. Após a instalação, verifique a versão:

Get-Module Pester -ListAvailable | Select-Object Name, Version

Describe/It

Os blocos Describe e It são a espinha dorsal dos testes Pester. Describe agrupa um conjunto de testes relacionados, geralmente uma função ou um módulo. Ele pode conter blocos Context para organizar ainda mais os testes em cenários específicos. O bloco It define um teste individual, com uma descrição do que está sendo testado.

A estrutura básica de um teste é:

Describe "Nome do grupo de testes" {
    It "Descrição do teste" {
        # Código do teste
    }
}

Dentro do bloco It, você executa o código que deseja testar e faz uma ou mais asserções. Por exemplo, vamos criar uma função simples e testá-la:

function Add-Numbers {
    param($a, $b)
    return $a + $b
}

Describe "Add-Numbers" {
    It "Soma dois números corretamente" {
        $result = Add-Numbers -a 2 -b 3
        $result | Should -Be 5
    }
}

Para executar os testes, você pode usar o comando Invoke-Pester. Se você salvar o código acima em um arquivo Add-Numbers.Tests.ps1 e executar Invoke-Pester no diretório, verá a saída mostrando que o teste passou.

É importante notar que o Pester usa o termo It para cada teste e Should para as asserções. A sintaxe é fluente e fácil de ler, tornando os testes quase como documentação executável.

Asserções

As asserções são as verificações que você faz em seus testes. O Pester oferece vários operadores de asserção por meio do comando Should. Os mais comuns são:

  • Should -Be: compara igualdade (equivalente a -eq).
  • Should -BeExactly: compara igualdade exata (case-sensitive).
  • Should -BeGreaterThan / Should -BeLessThan: comparações numéricas.
  • Should -Contain: verifica se uma coleção contém um elemento.
  • Should -Match: verifica se uma string corresponde a um padrão regex.
  • Should -Throw: verifica se um comando lança uma exceção.

Exemplos de asserções:

Describe "Exemplos de asserções" {
    It "Verifica igualdade" {
        $value = 42
        $value | Should -Be 42
    }

    It "Verifica que uma coleção contém um item" {
        $array = @('a', 'b', 'c')
        $array | Should -Contain 'b'
    }

    It "Verifica que uma string corresponde a um padrão" {
        $name = 'John Doe'
        $name | Should -Match '^John'
    }

    It "Verifica que um comando lança erro" {
        { throw "Erro" } | Should -Throw
    }
}

Além dessas, há asserções para tipos, propriedades e até para coleções vazias. Você pode combinar asserções em um único teste para verificar múltiplas condições.

Mocking (visão geral)

Mocking é uma técnica que permite substituir partes do sistema por versões simuladas, para isolar a unidade de código que está sendo testada. No Pester, você pode usar o comando Mock para substituir cmdlets, funções ou até mesmo comandos nativos. Isso é útil quando você não quer depender de recursos externos, como serviços web, arquivos ou outros módulos, durante os testes.

A sintaxe básica do Mock é:

Mock NomeDoComando -MockWith { # comportamento simulado }

Por exemplo, suponha que você tenha uma função que chama Get-Process e você quer testar essa função sem depender dos processos reais do sistema:

function Get-RunningProcessCount {
    $processes = Get-Process
    return $processes.Count
}

Describe "Get-RunningProcessCount" {
    It "Retorna o número de processos" {
        Mock Get-Process -MockWith { return @(1, 2, 3) }
        $count = Get-RunningProcessCount
        $count | Should -Be 3
    }
}

O Mock substitui o comando Get-Process dentro do escopo do teste, retornando uma lista fictícia. Isso permite que você teste a lógica da função sem depender do estado real do sistema.

Além disso, você pode verificar se um comando foi chamado com os parâmetros esperados usando Assert-MockCalled. Isso é útil para garantir que uma função dependente foi invocada corretamente.

Describe "Verificação de chamadas" {
    It "Chama Get-Process uma vez" {
        Mock Get-Process -MockWith { return @() }
        Get-RunningProcessCount
        Assert-MockCalled Get-Process -Times 1
    }
}

O mocking é uma ferramenta poderosa para testes unitários, pois permite testar o comportamento da unidade em isolamento. No entanto, é importante não abusar, pois mocks em excesso podem esconder problemas de integração.

Boas Práticas e Observações Finais

Ao escrever testes com Pester, siga estas boas práticas para obter melhores resultados:

  • Nomeie os testes de forma descritiva, para que fique claro o que está sendo verificado.
  • Mantenha os testes independentes e isolados, evitando dependência de estado global.
  • Use Context para agrupar testes em cenários diferentes dentro de um Describe.
  • Teste tanto os caminhos felizes quanto os casos de erro.
  • Execute os testes com frequência, de preferência em um pipeline de CI.
  • Use o mocking com moderação, apenas quando necessário para isolar a unidade.

Lembre-se de que o Pester é uma ferramenta essencial para o desenvolvimento profissional em PowerShell. Dominar seus conceitos básicos, como Describe/It, asserções e mocking, permitirá que você escreva código mais robusto e confiável.

Referências

Exercícios

  1. Exercício 1: Crie um teste Pester para uma função que calcula o fatorial de um número. A função deve ser definida no próprio teste. Teste com os valores 0, 1 e 5.
  2. ✓ Resposta:
    function Get-Factorial {
        param($n)
        if ($n -eq 0) { return 1 }
        return $n * (Get-Factorial -n ($n - 1))
    }
    
    Describe "Get-Factorial" {
        It "Calcula fatorial de 0" {
            Get-Factorial -n 0 | Should -Be 1
        }
        It "Calcula fatorial de 1" {
            Get-Factorial -n 1 | Should -Be 1
        }
        It "Calcula fatorial de 5" {
            Get-Factorial -n 5 | Should -Be 120
        }
    }
  3. Exercício 2: Escreva um teste que verifique se uma função retorna uma lista com o número esperado de itens. Use Should -HaveCount.
  4. ✓ Resposta:
    function Get-Numbers {
        return @(1, 2, 3)
    }
    
    Describe "Get-Numbers" {
        It "Retorna 3 itens" {
            $result = Get-Numbers
            $result | Should -HaveCount 3
        }
    }
  5. Exercício 3: Crie um teste que use mocking para simular um cmdlet que acessa um serviço externo. Por exemplo, simule Invoke-RestMethod e teste uma função que chama esse cmdlet.
  6. ✓ Resposta:
    function Get-DataFromApi {
        $response = Invoke-RestMethod -Uri "https://api.example.com/data"
        return $response
    }
    
    Describe "Get-DataFromApi" {
        It "Retorna dados simulados" {
            Mock Invoke-RestMethod -MockWith { return @{name="John"} }
            $data = Get-DataFromApi
            $data.name | Should -Be "John"
        }
    }
  7. Exercício 4: Escreva um teste que verifique se um comando lança uma exceção quando um parâmetro é inválido. Use Should -Throw.
  8. ✓ Resposta:
    function Test-Param {
        param($value)
        if ($value -lt 0) { throw "Valor não pode ser negativo" }
        return $value
    }
    
    Describe "Test-Param" {
        It "Lança exceção para valor negativo" {
            { Test-Param -value -1 } | Should -Throw
        }
    }
  9. Exercício 5: Use Assert-MockCalled para verificar que um comando foi chamado exatamente uma vez em um teste.
  10. ✓ Resposta:
    function Invoke-MyCommand {
        Get-Process | Out-Null
    }
    
    Describe "Invoke-MyCommand" {
        It "Chama Get-Process uma vez" {
            Mock Get-Process -MockWith { return @() }
            Invoke-MyCommand
            Assert-MockCalled Get-Process -Times 1
        }
    }