tests
|---Unit
|---Integration
tests\Unit
folder must contain a Test Script for each DSC Resource in the
DSC Module with the filename DSC_<ResourceName>.Tests.ps1
.tests\Integration
folder should, whenever possible, contain a Test Script
for each DSC Resource in the DSC Module with the filename DSC_<ResourceName>.integration.Tests.ps1
.Not yet written. This should be done through the Plaster template in the Sampler project.
Not yet written. This should be done through the Plaster template in the Sampler project.
It is possible that the Unit and Integration test templates may change in future, either to support new functionality or fix an issue. When a change is made to a template, it will be documented in the change log of the repository Sampler. To get the changes generate a new DSC module template from the Plaster template in the Sampler project.
The HQRM tests are present in the repository DSCResource.Test. The DSC module template will by default use the HQRM tests in this repository.
It is very likely that the DSC resource HQRM tests will be updated in the future and that will be documented in the change log of the repository DscResource.Test. The CI test pipeline defaults to opt-in to all tests so there are no need to do anything to get new functionality in the DSC modules.
Default is that all the tests are opted-in. If a test cannot be resolved to pass then there are different scenarios supported to opt-out from tests. Preferably you should opt-out from tests.
build.yaml
with the tag ExcludeTag
under either the
key Pester
or DscTest
(JSON files override excludes for the key
DscTest
).
Pester:
ExcludeTag:
- 'TagOnUnitTest'
DscTest:
ExcludeTag:
- Common Tests - New Error-Level Script Analyzer Rules
- Common Tests - Validate Example Files
- Common Tests - Relative Path Length
This HQRM tests is opt-out by default since it tests the ScriptAnalyzer
rules PSDSCDscExamplesPresent
and PSDSCDscTestsPresent
which does not
currently work with the folder structure needed by CI pipeline.
DscTest:
ExcludeTag:
- 'Common Tests - New Error-Level Script Analyzer Rules'
Even if opt-out this test will still run. You opt-out from the test phase failing if there are any violations. Noteworthy is also that if there are violations the opt-out test will be skipped (yellow), if there are no violations the test will pass (green).
Not yet written.
If configured, all tests are automatically run using the CI pipeline via Azure Pipelines in the DSC Community Azure DevOps organization when updating the upstream repository.
It is recommended that all tests be run locally before being committed.
Regardless of the method of running the tests you must always resolve the dependencies first. After that you must build the module.
NOTE! Each time a source file changes you must re-build the module before running tests again. This is because the tests are always run against the built module in the ‘output’ folder, not the modules source files.
KNOWN ISSUE: If the DSC resources are importing a module from the folder
Modules
(common module), the common module is already imported if you run the tests a second time. That means that any changes that you make to the common module are not reflected in the test run. To workaround this the common module have to be manually removed usingRemove-Module -Name 'ModuleName'
. There are a second workaround, force import the module in the DSC resources. Force importing the module are not recommended due to performance and could have a big impact in production.
Remember to run the build task if any file has been changed in the source folder.
This runs all the tests for the module. BE AWARE: This may be configured to run integration tests too if they exist! Integration tests may disrupt the system configuration temporarily, leave system changed, or may require specific dependencies to execute.
.\build.ps1 -Tasks test
This will just run the unit tests for the module using code coverage.
.\build.ps1 -Tasks test -PesterScript 'tests/Unit'
This will just run the integration tests for the module without code coverage.
.\build.ps1 -Tasks test -PesterScript 'tests/Integration' -CodeCoverageThreshold 0
Not yet written.
Not yet written.
If you have Attached your fork to a free Azure DevOps organization then for each commit that is pushed to your working branch in your fork the pipeline will be run.
It is possible to re-run failed jobs in the Azure Pipeline. Be aware that in some circumstances it caches dependencies so even re-running a failed jobs that you know will pass it will not. Instead start a new job from the commit in question. If it is a pull request (PR) that failed push another change to the PR, or close and directly reopen the PR.
If a resource contains private (non-exported) functions that need to be tested,
then to allow these functions to be tested by Pester, they must be contained
in an InModuleScope
Pester block:
InModuleScope $script:dscResourceName {
Describe "$($script:dscResourceName)\Get-FirewallRuleProperty" {
# Test Get-FirewallRuleProperty private/non-exported function
}
}
Note: The core DSC Resource functions Get-TargetResource
, Set-TargetResource
and Test-TargetResource
must always be public functions, so they do not
need to be contained in an InModuleScope
block.
It is common for modules to declare variables that are needed inside the module,
but may also be required for unit testing. One common example of this is the
$script:localizedData
variable that contains localized messages used by the
resource. Variables declared at the module scope (private variables) can not
be accessed by unit tests that are not inside an InModuleScope
Pester block.
Note: See Localization section for more information of using localized messages in tests.
There are three solutions to this:
Run the tests inside a InModuleScope
which give access to all
script scope variables like $script:localizedData
.
Use the InModuleScope
to copy the private variable into a variable
scoped to the Unit test:
$localizedData = InModuleScope $script:dscResourceName {
$script:localizedData
}
Add any variables that are required to be accessed outside the module
by unit tests to the Export-ModuleMember
cmdlet in the DSC Resource:
Export-ModuleMember -Function *-TargetResource -Variables LocalizedData
One useful pattern used when creating unit tests is to create variables that contain objects that will be returned when mocked cmdlets are called. These variables are often used many times over a single unit test file and so assigning each to a variable is essential to reduce the size of the unit test as well as improve readability and maintainability. For example:
$MockNetAdapter = @{
Name = 'Ethernet'
PhysicalMediaType = '802.3'
Status = 'Up'
}
# Create a mock
Mock `
-CommandName Get-NetAdapter `
-MockWith { return $MockNetAdapter }
However, when InModuleScope
is being used the variables that are defined won’t
be accessible from within the mocked cmdlets. The solution to this is create a script
block variable that contains the mocked object and then assign that to the Mock.
$GetNetAdapter_PhysicalNetAdapterMock = {
return @{
Name = 'Ethernet'
PhysicalMediaType = '802.3'
Status = 'Up'
}
}
# Create a mock
Mock `
-CommandName Get-NetAdapter `
-ModuleName $script:ModuleName `
-MockWith $GetNetAdapter_PhysicalNetAdapterMock
Pattern 1: The goal of this pattern should be to describe the potential states a system could be in for each function.
Describe 'Get-TargetResource' {
# TODO: Complete Get-TargetResource Tests...
}
Describe 'Set-TargetResource' {
# TODO: Complete Set-TargetResource Tests...
}
Describe 'Test-TargetResource' {
# TODO: Complete Test-TargetResource Tests...
}
Pattern 2: The goal of this pattern should be to describe the potential states a system could be in so that the get/set/test cmdlets can be tested in those states. Any mocks that relate to that specific state can be included in the relevant Describe block. For a more detailed description of this approach please review #143
Add as many of these example ‘states’ as required to simulate the scenarios that the DSC resource is designed to work with, below a simple ‘is in desired state’ and ‘is not in desired state’ are used, but there may be more complex combinations of factors, depending on how complex your resource is.
Describe 'The system is not in the desired state' {
#TODO: Mock cmdlets here that represent the system not being in the desired state
#TODO: Create a set of parameters to test your get/set/test methods in this state
$testParameters = @{
Property1 = 'value'
Property2 = 'value'
}
#TODO: Update the assertions below to align with the expected results of this state
It 'Should return something' {
Get-TargetResource @testParameters | Should -Be 'something'
}
It 'Should return false' {
Test-TargetResource @testParameters | Should -Be $false
}
It 'Should call Demo-CmdletName' {
Set-TargetResource @testParameters
#TODO: Assert that the appropriate cmdlets were called
Assert-MockCalled Demo-CmdletName
}
}
Describe 'The system is in the desired state' {
#TODO: Mock cmdlets here that represent the system being in the desired state
#TODO: Create a set of parameters to test your get/set/test methods in this state
$testParameters = @{
Property1 = 'value'
Property2 = 'value'
}
#TODO: Update the assertions below to align with the expected results of this state
It 'Should return something' {
Get-TargetResource @testParameters | Should -Be 'something'
}
It 'Should return true' {
Test-TargetResource @testParameters | Should -Be $true
}
}
To see examples of the Unit/Integration tests in practice, see the NetworkingDsc MSFT_DhcpClient resource:
When resources use localization (which all normally should) there is the possibility to use the localized messages in the tests. This is normally used to verify the correct error messages are thrown. But could be used for verbose messages, warning messages, and of course any other types.
When using the
Get-LocalizedData
helper function to load the localized strings into $script:localizedData
in
the resource module script file, the strings can easily be used like this in
tests.
Note: For this to work the test (the It-block) must be inside a
InModuleScope
-block.
It 'Should throw the correct error message' {
{
Set-TargetResource @setTargetResourceParameters
} | Should -Throw $script:localizedData.DatabaseMailDisabled
}
There are two helper functions to simplify testing localized error messages. The helper functions can be used to build a correct localized error record to be used in the tests. They are used together with the Helper functions for localization.
These test helper functions can currently only be found in other resource modules, for example in the resource module NetworkingDsc, in the CommonTestHelper module. To use it, copy the PowerShell module CommonTestHelper.psm1 to the new resource module.
This helper function returns an invalid argument exception error object. Below is an example of how it could look like in the tests to test that the correct localized error record is returned.
$errorRecord = Get-InvalidArgumentRecord `
-Message ($script:localizedData.InterfaceNotAvailableError -f $interfaceAlias) `
-ArgumentName 'Interface'
It 'Should throw an InterfaceNotAvailable error' {
{ Assert-ResourceProperty @testRoute } | Should -Throw $errorRecord
}
This helper function returns an invalid operation exception error object. Below is an example of how it could look like in the tests to test that the correct localized error record is returned.
$errorRecord = Get-InvalidOperationRecord `
-Message ($script:localizedData.NetAdapterNotFoundError)
It 'Should throw the correct exception' {
{
$script:result = Find-NetworkAdapter -Name 'NoMatch'
} | Should -Throw $errorRecord
}