1

I write a module used for powershell-wrapping a commandline tool.

For my tests I had some variables defined in BeforeAll. Those vars should help in feeding the command with desired params and check them against the expected result, f.i.

BeforeAll {
   $PORead=@{
      Name = "Paul"
      Permission ="Read"
      Inheritance = "ThisFolderSubfoldersAndFiles"
   }

   $POReadExp="-bla bla (bla:bla;fra:fra;tra:tra)"
}

Now I want to use these in an it segment to feed the function and check the result, like:

It "checks result" -ForEach (
  @{Param=$PORead;Expected=$POReadExp}
) {
   $result=FunctionCall -Parameter1 $Param
   $result | Should -Be $Expected
}

Note: This is actually no working code - just to make clear what I want.

The problem is, that $Param and $Expected are $null, thus the current value of $PORead and $POReadExp are not assigned to $Param and $Expected. I made it easy to demonstrate the problem - in my code I use arrays in the -ForEach hash assigned to $Param and $Expected.

Using plain strings, there is no problem and everything works fine. I could use plain text and than use som switch construct to match my strings with the according variables from BeforeAll{}, but I thought there must be a more elegant way.

Using standard hashtables, the variable substition works fine.

I'd appreciate any idea.


To clarify some things:

The solution of Mark works somehow, but it needs the $PORead and $POReadExp to be set outside the BeforeAll-block (if you want to use -ForEach), or within the BeforeAll-block, but in that case you only can use one argument.

My plan was a bit different, so I'm making my point a bit clearer:

In the BeforeAll-block I wanted a bunch of definitions for params and outcomes

BeforeAll {
   $PORead = @{
   Name = "Paul"
   Permission ="Read"
   Inheritance = "ThisFolderSubfoldersAndFiles"

   $PODelete = @{
   Name = "Paul"
   Permission ="Delete"
   Inheritance = "ThisFolderOnly"
   }

   $POReadResult = "add parameters"
   $PODeleteResult = "delete parameters"
}

Now I want to test a whole bunch of combinations, feeding different combinations to the function and checking, if the combination of results match:

It "Test if all combinations works" -ForEach(
   @{FunctionParam=$PORead;Expected=$POReadExp}
   @{FunctionParam=$PORead,$PORead;Expected=$POReadExp,$POReadExp}
   @{FunctionParam=$PORead,$PODelete,$PORead,$PORead;Expected=$PORead,$PORead,$PORead,$PODelete}
) {
  $result = Function-Call -Param $FunctionParam
  $wanted = $expected -join(" ")
  $result | Should -Be $wanted
}

I can realize this by placing the definitions out of any block, but it feels wrong in terms of pester coding guidelines.

Greets Usul

Usul
  • 11
  • 4
  • Hi iRon, trying using yielded the following error: A Using variable cannot be retrieved. A Using variable can be used only with Invoke-Command, Start-Job, or InlineScript in the script workflow. When it is used with Invoke-Command, the Using variable is valid only if the script block is invoked on a remote computer. – Usul Oct 26 '21 at 09:43

2 Answers2

2

The scopes for how to set test data with Pester v5 are quite confusing. What I've found is that anything you set in a BeforeAll or BeforeEach is available within the It. However if you want to set values to the -ForEach (or its alias -TestData) you need to do it as part of or before the It, outside of the BeforeEach or BeforeAll.

As such I think your tests will work if you do this:

$PORead = @{
   Name = "Paul"
   Permission ="Read"
   Inheritance = "ThisFolderSubfoldersAndFiles"
}

$POReadExp = "-bla bla (bla:bla;fra:fra;tra:tra)"

It "checks result" -ForEach (
  @{Param=$PORead;Expected=$POReadExp}
) {
   $result=FunctionCall -Parameter1 $Param
   $result | Should -Be $Expected
}

Or alternatively (if you just need to perform a single test):

BeforeAll {
   $PORead=@{
      Name = "Paul"
      Permission ="Read"
      Inheritance = "ThisFolderSubfoldersAndFiles"
   }

   $POReadExp="-bla bla (bla:bla;fra:fra;tra:tra)"
}

It "checks result" {
   $result = FunctionCall -Parameter1 $PORead
   $result | Should -Be $POReadExp
}

If you want to avoid using a variable that is not within one of the Pester code blocks, then you should do this (which is in line with the examples given in the Pester documentation on Data Driven Tests):

It "checks result" -ForEach (
  @{
      Param = @{
          Name = "Paul"
          Permission = "Read"
          Inheritance = "ThisFolderSubfoldersAndFiles"
      }
      Expected = "-bla bla (bla:bla;fra:fra;tra:tra)"
  }) {
      $result=FunctionCall -Parameter1 $Param
      $result | Should -Be $Expected
}
Mark Wragg
  • 22,105
  • 7
  • 39
  • 68
  • Hi Mark, I didn't try this yet, but migrating to pester 5 I wanted to keep in track with the design guidelines for pester 5, telling that no code at all should be placed outside of a block. Your solution places code outside a block and that is the "$PORead=" line. I'll check if your solution works though. – Usul Oct 26 '21 at 09:42
  • And as I said before, in my code I use array: I want the function call to be fed with different combinations of the input parameters (like $PORead) and conduct the expected result by collecting the the different results (like $POReadExp), so that I can check, whether the combination of all given params works like desired, too. – Usul Oct 26 '21 at 09:47
  • The Pester guide on Data Driven Tests shows the data being directly provided to the `-ForEach` parameter on the `It`, but I don't think its bad practice to use a variable before the `It` block and then pass that variable instead, I personally think its better for readability. I'll add a 3rd example showing it being provided directly. – Mark Wragg Oct 26 '21 at 14:18
0

You need to move your data used to generate tests from the BeforEach{} block to BeforeDiscovery{} block (see the linked pester documentation, has a really good simple example).

Pester needs your data that drives your DataDrivenTests to be available before it dynamically generates the test cases, so it knows how many to make.

BeforeAll{}/BeforeEach{} blocks should be reserved for data used after the It{} blocks are generated, but before they are evaluated.

LastTigerEyes
  • 647
  • 1
  • 9
  • 21