Lost in scopes

Welcome Forums Pester Lost in scopes

Viewing 3 reply threads
  • Author
    Posts
    • #105454
      Participant
      Topics: 10
      Replies: 16
      Points: 3
      Rank: Member

      testmodule.psm1

      function Invoke-Public ($param)
      {
          Invoke-Private -param $param
      }
      function Invoke-Private ($param)
      {
          try
          {
              $Res = Resolve-DnsName -Name $param -DnsOnly -ErrorAction Stop
              return $Res
          }
          catch
          {
              return $_
          }
      }
      Export-ModuleMember -Function Invoke-Public

      testmodule.Tests.ps1

      Import-Module testmodule
      InModuleScope testmodule {
          $FQDN = "fqdn.domain.com"
          $Netbios = "hostname"
          Mock Resolve-DnsName {
              if ($Name -eq $FQDN)
              {
                  return "true Name $Name. FQDN - $FQDN"
              }
              elseif ($Name -eq $Netbios)
              {
                  return "false Name $Name. Netbios - $Netbios"
              }
              else
              {
                  return "3rd Name $Name. FQDN - $FQDN. Netbios - $Netbios"
              }
          }
          Describe 'Private function test' {
              It 'Passes Invoke-Private with ' -TestCases @(
                  @{Value = $FQDN; Expected = "true Name $FQDN. FQDN - $FQDN"}
                  @{Value = $Netbios; Expected = "false Name $Netbios. Netbios - $Netbios"}
              ) {
                  param ($Value, $Expected)
                  Invoke-Private -param $Value | Should -Be $Expected
              }
          }
      }
      Describe 'Public function test' {
          $FQDN = "fqdn.domain.com"
          It 'Passes Invoke-Public' {
              Invoke-Public -param $FQDN | Should -Be "true Name $FQDN. FQDN - $FQDN"
          }
      }

      tests result:

      Executing Test
      Executing all tests in ‘..\test’

      Executing script C:\Temp\test\testmodule.Tests.ps1

      Describing Private function test
      [+] Passes Invoke-Private with ‘fqdn.domain.com’ 4.04s
      [+] Passes Invoke-Private with ‘hostname’ 284ms

      Describing Public function test
      [-] Passes Invoke-Public 1.21s
      Expected strings to be the same, but they were different.
      Expected length: 49
      Actual length: 45
      Strings differ at index 0.
      Expected: ‘true Name fqdn.domain.com. FQDN – fqdn.domain.com’
      But was: ‘3rd Name fqdn.domain.com. FQDN – . Netbios – ‘
      ———–^
      32: Invoke-Public -param $FQDN | Should -Be “true Name $FQDN. FQDN – $FQDN”
      at , C:\Temp\test\testmodule.Tests.ps1: line 32
      Tests completed in 5.54s
      Tests Passed: 2, Failed: 1, Skipped: 0, Pending: 0, Inconclusive: 0

      Why the $FQDN and $Netbios variables are not available for mocked Resolve-DnsName function? How should I make them available, or what would be the recommended way to test in this scenario?

    • #105467
      Keymaster
      Topics: 18
      Replies: 4872
      Points: 1,903
      Helping HandTeam Member
      Rank: Community Hero

      (Apologies for the spurious incorrect forum post you may have received; that was a bot that didn’t catch the right keywords)

      Gimme a sec to try this on my own!

    • #105470
      Keymaster
      Topics: 18
      Replies: 4872
      Points: 1,903
      Helping HandTeam Member
      Rank: Community Hero

      Yeah, so the problem is just that you’ve passed out of scope for the mock. Mocks are really designed to mock a function that is being run in a test; they’re not designed to mock functions that exist elsewhere in a module. The module’s “local” definition of the function or command “wins,” because it’s more local. Because you’re calling a function, which is calling a function, the mock gets “lost,” if you will.

      Struggling to explain, but maybe think of it this way: you’ve seen that you can go “one level” deep with Invoke-Private. What you can’t do is go “deeper.” Does that make sense?

      • #105629
        Participant
        Topics: 10
        Replies: 16
        Points: 3
        Rank: Member

        I am not sure I understand, to me it seems that the mock is working, it returns the mocked string just with empty variables. My observation is that the variables defined InModuleScope are available when calling mocked function in that same scope, but they are not available when calling in public scope (from within a public function).

        Now I’m on my phone and can’t double check, but I think I was getting the expected result if I just used strings in if checks in mocked function, instead of using $fqdn and $netbios variables.

      • #105754
        Participant
        Topics: 0
        Replies: 1
        Points: -19
        Rank: Member

        Your observation is correct. The public function will not have access to those variables because it it outside the scope of where the mock is defined. If you move the public function test into that scope(InModuleScope testmodule) then the mocked outputs will be available to your public function.

    • #112183
      Participant
      Topics: 0
      Replies: 28
      Points: -7
      Rank: Member

      I am getting a different result than you do. The last Describe is placed outside of the InModuleScope, correct?

      Because in that case the Mock should not apply to the last describe, and that is what I am seeing. The real Resolve-DnsName is called.

      — In general about the scopes, mock is designed to mock stuff in different scopes, not just the one in the test. So what you are doing is correct. You are reaching into the TestModule scope, and shadowing Resolve-DnsName there. That means that any call to Resolve-DnsName within that scope will call the mock an not the real function. Even if you call Invoke-Public, it will reach into the TestModule module scope, and invoke Invoke-Private there. Inside it invokes Resolve-DnsName which is the mock.

      What would not work, and what people sometimes find confusing, is that we cannot overwrite the function directly – that is inject mock to the DnsClient module from which the Resolve-DnsName originates, because your module already got link to the functions from that module, and shadowing the functions in that module does not overwrite the link. So you are correctly mocking it in the scope of your mock, not in the scope of DnsClient.

Viewing 3 reply threads
  • The topic ‘Lost in scopes’ is closed to new replies.