3

I have a number of modules, including ModuleMain and ModuleSql. There is an interdependence between the modules such that Main-Function in ModuleMain uses 4 functions from ModuleSql:

function Main-Function {
  [CmdletBinding(SupportsShouldProcess=$true)]

  # The following 4 lines are all wrapped in $PSCmdlet.ShouldProcess() and
  # try {} catch {} logic. I have left that out in this post, but I mention
  # it in case it is relevant.

  $a = New-SqlConnection
  $b = Invoke-SqlStoredProc -connection $a
  $c = Invoke-SqlQuery -connection $a
  Close-SqlConnection -connection $a | Out-Null
  return $c
}

I have created a Function-Main1.tests.ps1 file to test Function-Main1. At first I used InModuleScope but then switched to specifying the module per-mock with the -ModuleName parameter.

Import-Module "ModuleMain" -Force

Describe "Main-Function" {

  Mock -ModuleName ModuleMain New-SqlConnection {
    return $true }
  Mock -ModuleName ModuleMain Invoke-SqlStoredProc {
    return $true }
  Mock -ModuleName ModuleMain Invoke-SqlQuery {
    return $true }
  Mock -ModuleName ModuleMain Close-SqlConnection {
    return $true }

  Context "When calling Main-Function with mocked SQL functions" {
    It "Calls each SQL function once" {
      Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName New-SqlConnecion -Times 1 -Exactly
      Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName Invoke-SqlStoredProc -Times 1 -Exactly
      Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName Invoke-SqlQuery -Times 1 -Exactly
      Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName Close-SqlConnecion -Times 1 -Exactly
    }
  }
}

When I run this test I get the following results:

[-] Calls each SQL function once 223ms
  Expected Invoke-SqlStoredProc in module ModuleMain to be called 1 times exactly but was called 0 times
  at line: xx in 
  xx:       Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName Invoke-SqlStoredProc -Times 1 -Exactly

Note the following:

  1. I did not import ModuleSql, where the -Sql functions are defined, since I am mocking them all anyway.
  2. I have observerd/worked out that I need to set -ModuleName to be the module where Main-Function is defined and not the one where the SQL function (that I am trying to mock) is defined.
  3. I have played around with InModuleScope and -ModuleName, e.g. setting one or other to be ModuleSQL, but mainly that just made things worse.

By playing around, adding Verbose output in the other mocked functions, I have confirmed that New-SqlConnection and Close-SqlConnection are both being intercepted, but Invoke-SqlStoredProc and Invoke-SqlQuery are not.

Probing deeper, I can see that the following exception is being thrown by the Invoke-Sql* (mocked) functions: Error: "Invalid cast from 'System.Boolean' to 'System.Data.SqlClient.SqlConnection'." This is behaviour that I would expect when the real versions of those functions are called, but I am expecting that the mocked versions would ignore parameter types.

Why would Pester intercept only 2 of my 4 functions?

Charlie Joynt
  • 4,411
  • 1
  • 24
  • 46
  • Could you provide [mcve]? Currently I do not even see where did you call `Main-Function` as part of test. – user4003407 Mar 18 '16 at 11:38
  • Not sure how easy it will be to create a MCV version of what I'm doing without needing to add in all the Sql function definitions, etc. I have just added a little more debug that I pulled out of the testing. – Charlie Joynt Mar 18 '16 at 11:57
  • I run the test by loading the .tests.ps1 file in PowerShell ISE and pressing F5! – Charlie Joynt Mar 18 '16 at 12:02
  • 2
    Mocked functions does not ignore parameter types. – user4003407 Mar 18 '16 at 12:02
  • Just to clarify further, my evidence that the functions are not being intercepted is (1) the error message generated by `Assert-MockCalled` and (2) the exception caught which suggests that the real function is being called with invalid inputs. – Charlie Joynt Mar 18 '16 at 12:08
  • Ah, they don't ignore parameter types... That would probably explain it. The two functions that are not being intercepted require the `-connection` to be a `System.Data.SqlClient.SqlConnection` type. I guess I'll have to update the `New-SqlConnection` mock to return such an object. – Charlie Joynt Mar 18 '16 at 12:11

1 Answers1

4

So, the short answer to this question was given in the comments above:

Mocked functions does not ignore parameter types. --PetSerAl

This meant that when I was trying to call the Invoke-Sql* functions, and using the phony $sqlConnection variable (simply set to $true) this was causing an error since the input parameter wasn't the expected data type.

And the solution in my case was to mock the New-SqlConnection function so it returned a [System.Data.SqlCient.SqlConnection] object. As it happens, I also reverted back to using InModuleScope rather than specifying the module on each Mock:

InModuleScope "ModuleMain" {
  Describe "MainFunction" {

    Mock New-SqlConnection {
      New-Object -TypeName System.Data.SqlCient.SqlConnection
    }

    ...
}
Charlie Joynt
  • 4,411
  • 1
  • 24
  • 46