@@ -16,9 +16,11 @@ reusable.
1616
1717## Private functions
1818
19- Private functions should always have its separate script file and
20- the function name as the file name with the .ps1 extension, these files
21- shall always be placed in the folder source/Private.
19+ Private functions (also known as helper functions) should always have its
20+ separate script file and the function name as the file name with the .ps1
21+ extension. These files shall always be placed in the folder source/Private.
22+ This also applies to functions that are only used within a single public
23+ command.
2224
2325## Desired State Configuration (DSC) class-based resource
2426
@@ -27,6 +29,98 @@ its separate script file and the resource class name as the file name with
2729the .ps1 extension, these files shall always be placed in the folder
2830source/Classes.
2931
32+ ### Parent classes
33+
34+ #### ResourceBase
35+
36+ A derived class should only inherit from the parent class ` ResourceBase `
37+ when it can't inherit from ` SqlResourceBase ` or when the class-based resource
38+ is not related to SQL Server Database Engine.
39+
40+ The parent class ` ResourceBase ` will set up ` $this.localizedData ` and provide
41+ logic to compare the desired state against the current state. To get the
42+ current state it will call the overridable method ` GetCurrentState ` . If not
43+ in desired state it will call the overridable method ` Modify ` . It will also
44+ call the overridable methods ` AssertProperties ` and ` NormalizeProperties ` to
45+ validate and normalize the provided values of the desired state.
46+
47+ #### SqlResourceBase
48+
49+ A derived class should only inherit from the parent class ` SqlResourceBase `
50+ if the class-based resource need to connect to a SQL Server Database Engine.
51+
52+ The parent class ` SqlResourceBase ` provides the DSC properties ` InstanceName ` ,
53+ ` ServerName ` , ` Credential ` and ` Reasons ` and the method ` GetServerObject `
54+ which is used to connect to a SQL Server Database Engine instance.
55+
56+ ### Derived class
57+
58+ The derived class should use the decoration ` [DscResource(RunAsCredential = 'Optional')] ` .
59+
60+ The derived class should always inherit from a parent class.
61+
62+ The derived class should override the methods ` Get ` , ` Test ` , ` Set ` , ` GetCurrentState ` ,
63+ ` Modify ` , ` AssertProperties ` , and ` NormalizeProperties ` using this pattern
64+ (and replace MyResourceName with actual resource name):
65+
66+ ``` powershell
67+ [MyResourceName] Get()
68+ {
69+ # Call the base method to return the properties.
70+ return ([ResourceBase] $this).Get()
71+ }
72+
73+ [System.Boolean] Test()
74+ {
75+ # Call the base method to test all of the properties that should be enforced.
76+ return ([ResourceBase] $this).Test()
77+ }
78+
79+ [void] Set()
80+ {
81+ # Call the base method to enforce the properties.
82+ ([ResourceBase] $this).Set()
83+ }
84+
85+ <#
86+ Base method Get() call this method to get the current state as a hashtable.
87+ The parameter properties will contain the key properties.
88+ #>
89+ hidden [System.Collections.Hashtable] GetCurrentState([System.Collections.Hashtable] $properties)
90+ {
91+ # Add code to return the current state as an hashtable.
92+ }
93+
94+ <#
95+ Base method Set() call this method with the properties that are not in
96+ desired state and should be enforced. It is not called if all properties
97+ are in desired state. The variable $properties contains only the properties
98+ that are not in desired state.
99+ #>
100+ hidden [void] Modify([System.Collections.Hashtable] $properties)
101+ {
102+ # Add code to set the desired state based on the properties that are not in desired state.
103+ }
104+
105+ <#
106+ Base method Assert() call this method with the properties that was assigned
107+ a value.
108+ #>
109+ hidden [void] AssertProperties([System.Collections.Hashtable] $properties)
110+ {
111+ # Add code to validate class properties that the user passed values to.
112+ }
113+
114+ <#
115+ Base method Normalize() call this method with the properties that was assigned
116+ a value.
117+ #>
118+ hidden [void] NormalizeProperties([System.Collections.Hashtable] $properties)
119+ {
120+ # Add code to normalize class properties that the user passed values to.
121+ }
122+ ```
123+
30124## Comment-based help
31125
32126Comment-based help should always be before the function-statement for each
@@ -64,49 +158,55 @@ For class-based resource you should always add a localized strings in a
64158separate file the folder source\en-US. The strings file for a class-based
65159resource should be named to exactly match the resource class name with the
66160suffix ` .strings.psd1 ` .
67- Localized string key names should never be prefixed with anything but
68- use underscore as word separator if key name has more than one word. Always
69- assume that all localized string keys for a class-based resource already
70- have been assigned to the variable ` $this.localizedData ` .
161+ Localized string key names should use underscore as word separator if key
162+ name has more than one word. Always assume that all localized string keys
163+ for a class-based resource already have been assigned to the variable
164+ ` $this.localizedData ` by the parent class .
71165
72- ## Unit tests
166+ ## Tests
73167
74168All tests should use the Pester framework and use Pester v5.0 syntax.
75-
76- Never test, mock or use ` Should -Invoke ` for ` Write-Verbose ` and ` Write-Debug `
77- regardless of other instructions.
78-
79- Parameter validation should not be tested in unit tests.
169+ Parameter validation should never be tested.
80170
81171Test code should never be added outside of the ` Describe ` block.
82172
83- Unit tests should be added for all public commands and private functions.
84- The unit tests for public command should be placed in the folder tests/Unit/Public
85- and the unit tests for private functions should be placed in the folder
86- tests/Unit/Private. The unit tests should be named after the public command
87- or private function they are testing, but should have the suffix .Tests.ps1.
88- The unit tests should be written to cover all possible scenarios and code paths,
89- ensuring that both edge cases and common use cases are tested.
90-
91173There should only be one Pester ` Describe ` block per test file, and the name of
92- the ` Describe ` block should be the same as the name of the public command or
93- private function being tested. Each scenario or code path being tested should
94- have its own Pester ` Context ` block that starts with the phrase 'When'. Use
95- nested ` Context ` blocks to split up test cases and improve tests readability.
96- Pester ` It ` block descriptions should start with the phrase 'Should'. ` It `
97- blocks must always call the command or function being tested and result and
98- outcomes should be kept in the same ` It ` block. ` BeforeAll ` and ` BeforeEach `
99- blocks should never call the command or function being tested.
174+ the ` Describe ` block should be the same as the name of the public command,
175+ private function, or class-based resource being tested. Each scenario or
176+ code path being tested should have its own Pester ` Context ` block that starts
177+ with the phrase 'When'. Use nested ` Context ` blocks to split up test cases
178+ and improve tests readability. Pester ` It ` block descriptions should start
179+ with the phrase 'Should'. ` It ` blocks must always call the command or function
180+ being tested and result and outcomes should be kept in the same ` It ` block.
181+ ` BeforeAll ` and ` BeforeEach ` blocks should never call the command or function
182+ being tested.
100183
101184The ` BeforeAll ` , ` BeforeEach ` , ` AfterAll ` and ` AfterEach ` blocks should be
102185used inside the ` Context ` block as near as possible to the ` It ` block that
103- will use the mocked test setup and teardown. The ` BeforeAll ` block should
104- be used to set up any necessary test data or mocking, and the ` AfterAll `
105- block can be used to clean up any test data. The ` BeforeEach ` and ` AfterEach `
186+ will use the test data, test setup and teardown. The ` AfterAll ` block can
187+ be used to clean up any test data. The ` BeforeEach ` and ` AfterEach `
106188blocks should be used sparingly. It is okay to duplicated code in ` BeforeAll `
107- and ` BeforeEach ` blocks inside different ` Context ` blocks to help with
108- readability and understanding of the test cases, to keep the test setup
109- and teardown as close to the test case as possible.
189+ and ` BeforeEach ` blocks that are used inside different ` Context ` blocks.
190+ The duplication helps with readability and understanding of the test cases,
191+ and to be able to keep the test setup and teardown as close to the test
192+ case (` It ` -block) as possible.
193+
194+ ### Unit tests
195+
196+ Never test, mock or use ` Should -Invoke ` for ` Write-Verbose ` and ` Write-Debug `
197+ regardless of other instructions.
198+
199+ Unit tests should be added for all public commands, private functions and
200+ class-based resources. The unit tests for class-based resources should be
201+ placed in the folder tests/Unit/Classes. The unit tests for public command
202+ should be placed in the folder tests/Unit/Public and the unit tests for
203+ private functions should be placed in the folder tests/Unit/Private. The
204+ unit tests should be named after the public command or private function
205+ they are testing, but should have the suffix .Tests.ps1. The unit tests
206+ should be written to cover all possible scenarios and code paths, ensuring
207+ that both edge cases and common use cases are tested.
208+
209+ The ` BeforeAll ` block should be used to set up any necessary test data or mocking
110210
111211Use localized strings in the tests only when necessary. You can assign the
112212localized string to a mock variable by and get the localized string key
@@ -175,12 +275,27 @@ AfterAll {
175275}
176276```
177277
178- ## Integration tests
278+ ### Integration tests
279+
280+ Integration tests that depend on an SQL Server Database Engine instance
281+ will run in environments in CI/CD pipelines where an instance DSCSQLTEST
282+ is already installed. Each environment will have SQL Server 2016, 2017,
283+ 2019, or 2022.
284+
285+ Integration tests that depend on an SQL Server Reporting services instance
286+ will run in environments in CI/CD pipelines where an instance SSRS is already
287+ installed. Each environment will have SQL Server Reporting services 2016,
288+ 2017, 2019, or 2022.
289+
290+ Integration tests that depend on a Power BI Report Server instance
291+ will run in one environment in CI/CD pipeline where an instance PBIRS is
292+ already installed. The environment will always have the latest Power BI
293+ Report Server version.
179294
180295Integration tests should be added for all public commands. Integration must
181- never mock any command but run the command in a real environment. The integration
182- tests should be placed in the folder tests/Integration/Commands and the
183- integration tests should be named after the public command they are testing,
296+ never mock any command but run the command in a real environment. All integration
297+ tests should be placed in the root of the folder " tests/Integration/Commands"
298+ and the integration tests should be named after the public command they are testing,
184299but should have the suffix .Integration.Tests.ps1. The integration tests should
185300be written to cover all possible scenarios and code paths, ensuring that both
186301edge cases and common use cases are tested. The integration tests should
@@ -223,3 +338,68 @@ BeforeAll {
223338 Import-Module -Name $script:dscModuleName
224339}
225340```
341+
342+ The module DscResource.Test is used by the pipeline and its commands
343+ are normally not used when testing public functions, private functions or
344+ class-based resources.
345+
346+ ## Style guidelines
347+
348+ This project use the style guidelines from the DSC Community: https://dsccommunity.org/styleguidelines
349+
350+ ### Markdown files
351+
352+ - Line length should be wrapped after a word when a line exceeds 80 characters
353+ in length.
354+ - Use 2 spaces for indentation in Markdown files.
355+
356+ ### PowerShell files
357+
358+ - All files should use UTF8 without BOM.
359+
360+ ### PowerShell code
361+
362+ - Try to limit lines to 120 characters
363+ - Use 4 spaces for indentation, never use tabs.
364+ - Use '#' for single line comments
365+ - Use comment-blocks for multiline comments with the format ` <# ... #> `
366+ where the multiline text is indented 4 spaces.
367+ - Use descriptive, clear, and full names for all variables, parameters, and
368+ function names. All names must be more than 2 characters. No abbreviations
369+ should be used.
370+ - Use camelCase for local variable names
371+ - Use PascalCase for function names and parameters in public commands, private
372+ functions and class-based resources.
373+ - All public command and private function names must follow the standard
374+ PowerShell Verb-Noun format.
375+ - All public command and private function names must use PowerShell approved
376+ verbs.
377+ - All public commands and private functions should always be advanced functions
378+ and have ` [CmdLetBinding()] ` attribute.
379+ - Public commands and private functions with no parameters should still have
380+ an empty parameter block ` param () ` .
381+ - Every parameter in public commands and private functions should include the
382+ ` [Parameter()] ` attribute.
383+ - A mandatory parameter in public commands and private functions should
384+ contain the decoration ` [Parameter(Mandatory = $true)] ` , and non-mandatory
385+ parameters should not contain the Mandatory decoration.
386+ - Parameters attributes, datatype and its name should be on separate lines.
387+ - Parameters must be separated by a single, blank line.
388+ - Use ` Write-Verbose ` to output verbose output for actions an public command
389+ or private function does.
390+ - Use ` Write-Debug ` for debug output for processes within the script for
391+ user to see the decisions a command och private function does.
392+ - Use ` Write-Error ` for error messages.
393+ - Use ` Write-Warning ` for warning messages.
394+ - Use ` Write-Information ` for informational messages.
395+ - Never use ` Write-Host ` .
396+ - Never use backtick as line continuation in code.
397+ - Use splatting for commands to reduce line length.
398+ - PowerShell reserved Keywords should be in all lower case.
399+ - Single quotes should always be used to delimit string literals wherever
400+ possible. Double quoted string literals may only be used when string
401+ literals contain ($) expressions that need to be evaluated.
402+ - Hashtables properties should always be in PascalCase and be on a separate
403+ line.
404+ - When comparing a value to ` $null ` , ` $null ` should be on the left side of
405+ the comparison.
0 commit comments