7.8 KiB
Testing Patterns
Analysis Date: 2026-04-02
Test Framework
Status: No automated testing framework detected
Infrastructure: Not applicable
- No test runner (Jest, Vitest, Pester)
- No test configuration files
- No test suite in codebase
- No CI/CD pipeline test stage configured
Testing Approach
Current Testing Model: Manual testing via GUI
Test Methods:
- GUI Testing: All functionality tested through WinForms UI
- Manual interaction with controls and dialogs
- Visual verification of results in generated reports
- Log output observation in RichTextBox
- Report Validation: HTML and CSV exports manually reviewed for correctness
- API Integration: Manual testing of PnP.PowerShell operations against live SharePoint tenant
- Regression Testing: Ad-hoc manual verification of features after changes
Code Organization for Testing
Testability Patterns: Limited
The monolithic single-file architecture (Sharepoint_ToolBox.ps1 at 6408 lines) makes isolated unit testing challenging. Key observations:
Tight Coupling to UI:
- Core business logic embedded in event handlers
- Heavy reliance on global
$script:scope for state - Example:
Load-Profilesreads fromGet-ProfilesFilePath, which is file-system dependent - Site picker functionality (
Show-SitePicker) spawns background runspace but depends on form being instantiated
Hard Dependencies:
- PnP.PowerShell module imported dynamically in background runspace blocks
- File system access (profiles, templates, settings) not abstracted
- SharePoint connection state implicit in PnP connection context
Areas with Better Isolation:
- Pure utility functions like
Format-Bytes,EscHtmlcould be unit tested - Data transformation functions like
Merge-PermissionRowsaccept input arrays and return structured output - HTML generation in
Export-PermissionsToHTMLandExport-StorageToHTMLcould be tested against expected markup
Background Runspace Pattern
Async Execution Model: Most long-running operations execute in separate PowerShell runspace to prevent UI blocking:
# 1. Create synchronized hashtable for communication
$sync = [hashtable]::Synchronized(@{
Done = $false
Error = $null
Result = $null
Queue = [System.Collections.Generic.Queue[object]]::new()
})
# 2. Define background script block (has access to passed parameters only)
$bgScript = {
param($Url, $ClientId, $Sync)
try {
Import-Module PnP.PowerShell -ErrorAction Stop
Connect-PnPOnline -Url $Url -Interactive -ClientId $ClientId
# Perform work
$Sync.Result = $data
} catch {
$Sync.Error = $_.Exception.Message
} finally {
$Sync.Done = $true
}
}
# 3. Launch in runspace
$rs = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace()
$rs.ApartmentState = "STA"; $rs.ThreadOptions = "ReuseThread"; $rs.Open()
$ps = [System.Management.Automation.PowerShell]::Create()
$ps.Runspace = $rs
[void]$ps.AddScript($bgScript)
[void]$ps.AddArgument($url)
$hnd = $ps.BeginInvoke()
# 4. Poll completion with timer
$tmr = New-Object System.Windows.Forms.Timer
$tmr.Interval = 300
$tmr.Add_Tick({
if ($sync.Done) {
[void]$ps.EndInvoke($hnd)
$rs.Close(); $rs.Dispose()
# Update UI with $sync.Result
}
})
$tmr.Start()
Used for:
- Site picker loading:
Show-SitePicker(lines 212-471) - Template capture: Background job in template manager (lines 900+)
- Site creation: Background job in bulk creation (lines 1134+)
- Permission/storage export: Operations triggered from event handlers
- File search: Background search execution
Message Queue Pattern
For logging from background runspaces:
# Background function enqueues messages
function BgLog([string]$m, [string]$c="LightGreen") {
$Sync.Queue.Enqueue([PSCustomObject]@{ Text=$m; Color=$c })
}
# Main thread timer dequeues and displays
$tmr.Add_Tick({
while ($sync.Queue.Count -gt 0) {
$msg = $sync.Queue.Dequeue()
_Tpl-Log -Box $textBox -Msg $msg.Text -Color $msg.Color
}
})
Rationale: Avoids cross-thread UI access violations by queueing messages from worker thread.
Common Testing Patterns in Code
Null/Existence Checks:
# Before using objects
if ($script:LogBox -and !$script:LogBox.IsDisposed) { ... }
if ($data -and $data.Count -gt 0) { ... }
if ([string]::IsNullOrWhiteSpace($value)) { return $false }
Error Logging in Loops:
# Catch errors in data processing, log, continue
foreach ($item in $items) {
try {
# Process item
} catch {
BgLog " Skipped: $($_.Exception.Message)" "DarkGray"
}
}
Validation Before Operations:
function Validate-Inputs {
if ([string]::IsNullOrWhiteSpace($script:txtClientId.Text)) {
[System.Windows.Forms.MessageBox]::Show(...)
return $false
}
return $true
}
# Called before starting long operations
if (-not (Validate-Inputs)) { return }
Data Flow Testing Approach
For Feature Development:
-
Manual Test Cases (observed pattern, not formalized):
-
Permissions Export:
- Select site with multiple libraries
- Choose CSV format
- Verify CSV contains all libraries and permissions
- Test HTML format in browser for interactivity
-
Storage Metrics:
- Run with
PerLibraryflag - Verify folder hierarchy is captured
- Test recursive subsite inclusion
- Validate byte calculations
- Run with
-
Template Capture/Apply:
- Capture from source site
- Verify JSON structure
- Create new site from template
- Verify structure, permissions, settings applied
-
File Search:
- Test regex patterns
- Verify date filtering
- Test large result sets (pagination)
- Check CSV/HTML output
-
-
Visual Verification:
- Log output reviewed in RichTextBox for progress
- Generated HTML reports tested in multiple browsers
- CSV files opened in Excel for format verification
Fragility Points & Testing Considerations
PnP Connection Management:
- No connection pooling; each operation may create new connection
- Interactive auth prompt appears per runspace
- Risk: Auth failures not consistently handled
- Testing Need: Mock PnP module or use test tenant
HTML Generation:
- String concatenation for large HTML documents (lines 1475+)
- Inline CSS for styling
- JavaScript for interactivity
- Risk: Complex HTML fragments prone to markup errors
- Testing Need: Validate HTML structure and JavaScript functionality
JSON Persistence:
- Profiles, templates, settings stored in JSON
- ConvertTo-Json/-From-Json without depth specification can truncate
- Risk: Nested objects may not round-trip correctly
- Testing Need: Validate all object types persist/restore
Background Runspace Cleanup:
- Runspace and PowerShell objects must be disposed
- Timer must be stopped and disposed
- Risk: Resource leaks if exception occurs before cleanup
- Testing Need: Verify cleanup in error paths
Suggested Testing Improvements
Unit Testing:
-
Extract pure functions (no UI/file system dependencies)
Format-Bytes,EscHtml,Merge-PermissionRows- HTML generation functions
-
Use Pester framework for PowerShell unit tests:
Describe "Format-Bytes" { It "Formats bytes to GB" { Format-Bytes (1GB * 2) | Should -Be "2 GB" } } -
Mock file system and PnP operations for integration tests
Integration Testing:
- Use test SharePoint tenant for functional testing
- Automate report generation and validation
- Script common user workflows
Regression Testing:
- Maintain test suite of sites with known structures
- Generate reports, compare outputs
- Run before major releases
Testing analysis: 2026-04-02