====== Testing ======
Testing strategy and framework for WvdS FPC RAD Studio.
===== Test Types =====
^ Type ^ Scope ^ Tools ^ Execution ^
| Unit Tests | Individual functions | FPCUnit | Local |
| Integration Tests | Component interaction | FPCUnit | Local |
| E2E Tests | Entire extension | VS Code Test Runner | CI |
===== Unit Tests =====
==== Structure ====
sources/
├── extensions/
│ └── wvds.vscode.build/
│ ├── pas/
│ │ ├── Build.Service.pas
│ │ └── Build.Models.pas
│ └── tests/
│ ├── Build.Service.Tests.pas
│ └── Build.Models.Tests.pas
==== Test Framework ====
unit Build.Service.Tests;
{$mode objfpc}{$H+}
interface
uses
TestFramework,
Build.Service,
Build.Models;
type
TBuildServiceTest = class(TTestCase)
published
procedure TestValidatePath_ValidPath_ReturnsTrue;
procedure TestValidatePath_InvalidPath_ReturnsFalse;
procedure TestGenerateCommand_DebugMode_IncludesDebugFlags;
end;
implementation
procedure TBuildServiceTest.TestValidatePath_ValidPath_ReturnsTrue;
var
Error: string;
begin
CheckTrue(ValidateBuildPath('%USERPROFILE%\Projects\MyApp', Error));
CheckEquals('', Error);
end;
procedure TBuildServiceTest.TestValidatePath_InvalidPath_ReturnsFalse;
var
Error: string;
begin
CheckFalse(ValidateBuildPath('', Error));
CheckNotEquals('', Error);
end;
procedure TBuildServiceTest.TestGenerateCommand_DebugMode_IncludesDebugFlags;
var
Config: TWvdSBuildConfig;
Command: string;
begin
Config.Mode := bmDebug;
Config.ProjectPath := 'test.lpr';
Command := GenerateBuildCommand(Config);
CheckTrue(Pos('-g', Command) > 0, 'Debug flag missing');
CheckTrue(Pos('-O0', Command) > 0, 'No-optimization flag missing');
end;
initialization
RegisterTest(TBuildServiceTest.Suite);
end.
==== Naming Conventions ====
Test{Feature}_{Scenario}_{ExpectedResult}
Examples:
TestValidatePath_ValidPath_ReturnsTrue
TestValidatePath_EmptyString_ReturnsFalse
TestGenerateCommand_ReleaseMode_IncludesOptimization
===== Ensuring Testability =====
==== Service Design ====
// TESTABLE - No UI dependency
function ValidateName(const AName: string; out AError: string): Boolean;
// NOT TESTABLE - UI in logic
procedure ValidateAndShowError(const AName: string);
==== Dependency Injection ====
// TESTABLE - Dependency injected
function ProcessFile(const APath: string; AFileReader: IFileReader): TResult;
// NOT TESTABLE - Hardcoded dependency
function ProcessFile(const APath: string): TResult;
begin
Content := ReadFile(APath); // Not mockable
end;
===== Mocking =====
type
TMockFileReader = class(TInterfacedObject, IFileReader)
FContent: string;
function ReadFile(const APath: string): string;
end;
function TMockFileReader.ReadFile(const APath: string): string;
begin
Result := FContent; // Predefined content
end;
procedure TProcessorTest.TestProcessFile_ValidContent_Succeeds;
var
Mock: TMockFileReader;
Processor: TProcessor;
begin
Mock := TMockFileReader.Create;
Mock.FContent := 'test content';
Result := ProcessFile('any.txt', Mock);
CheckEquals('expected output', Result);
end;
===== Integration Tests =====
unit Integration.BuildPipeline.Tests;
procedure TBuildPipelineTest.TestFullBuild_ConsoleApp_ProducesExe;
var
Project: TWvdSProject;
Result: TWvdSBuildResult;
begin
// Setup
Project := CreateTestProject(ptConsole);
// Execute
Result := BuildProject(Project);
// Verify
CheckEquals(bsSuccess, Result.Status);
CheckTrue(FileExists(Result.OutputPath));
// Cleanup
DeleteTestProject(Project);
end;
===== E2E Tests =====
VS Code Extension Testing with @vscode/test-electron:
// test/extension.test.js (generated from Pascal)
const vscode = require('vscode');
const assert = require('assert');
suite('Extension Test Suite', () => {
test('Build command exists', async () => {
const commands = await vscode.commands.getCommands();
assert.ok(commands.includes('wvds.build.run'));
});
test('Build command executes', async () => {
await vscode.commands.executeCommand('wvds.build.run');
// Verify output channel contains expected message
});
});
===== Test Execution =====
==== Local ====
# Unit tests
wvds-test unit
# Integration tests
wvds-test integration
# All tests
wvds-test all
==== CI ====
test:
runs-on: ubuntu-latest
steps:
- name: Run Unit Tests
run: wvds-test unit --report junit
- name: Run E2E Tests
run: wvds-test e2e --report junit
- name: Upload Results
uses: actions/upload-artifact@v3
with:
name: test-results
path: binaries/logs/test-*.xml
===== Code Coverage =====
wvds-test unit --coverage
# Generates:
# binaries/logs/coverage.html
# binaries/logs/coverage.json
**Minimum Coverage:**
* Services: 80%
* Models: 90%
* Utilities: 70%
===== See also =====
* [[.:debugging|Debugging]]
* [[.:build-pipeline|Build Pipeline]]
* [[.:code-konventionen|Code Conventions]]