Skip to main content

Teststrategie, acceptatieherleidbaarheid en kwaliteitsgrenzen

23.1 Intentie

Dit hoofdstuk beschrijft hoe OefenHub technisch aantoonbaar betrouwbaar wordt gemaakt. De teststrategie sluit aan op de gekozen middel-zware modulaire monoliet: één deploybare applicatie, één database en meerdere moduleprojecten met eigen DbContext, eigen schema en publieke contracten.

De teststrategie heeft vier doelen:

  1. aantonen dat de vastgestelde eisen uit de Software Requirements Specification en acceptatiecriteria technisch zijn afgedekt;
  2. bewaken dat modulegrenzen, dependency-richting en database-eigenaarschap niet ongemerkt worden doorbroken;
  3. voorkomen dat kritieke workflows halve functionele toestanden opleveren;
  4. zorgen dat nieuwe technische oefenmodules veilig toegevoegd kunnen worden zonder de kernapplicatie te destabiliseren.

23.2 Bronnen en afbakening

De Software Requirements Specification en acceptatiecriteria blijven leidend voor functionele testdekking. Dit hoofdstuk voegt geen nieuwe functionele eisen toe, maar beschrijft hoe bestaande eisen, modulecontracten, technische ontwerpkeuzes en kwaliteitsgrenzen worden getest.

Belangrijke inputbronnen zijn:

23.3 Testprincipes

De teststrategie volgt deze principes.

PrincipeBetekenis
Test dicht bij eigenaarschapEen module test primair haar eigen domeinregels, services, DbContext, seeddata en publieke contracten.
Modulegrenzen zijn testbaarDependency-richting, internal implementaties en publieke Contracts worden niet alleen afgesproken, maar via architecture tests bewaakt.
Geen testdekking via UI alleenKritieke regels mogen niet uitsluitend via end-to-endtests worden gevalideerd. Domein- en integratietests moeten de kernregels rechtstreeks testen.
Cross-module workflows zijn explicietWorkflows die meerdere modules raken krijgen eigen integratietests met aandacht voor transaction boundaries, foutscenario's en idempotentie.
Security is verspreid maar niet implicietOmdat er geen apart securityproject is, moeten pipeline, authorization, headers, logging en veilige foutafhandeling expliciet worden getest.
Testdata is beheerstTestdata mag geen echte persoonsgegevens, tokens, wachtwoorden of productiedata bevatten.
Traceability is aantoonbaarElke centrale eis uit de Software Requirements Specification is testbaar, beleidsmatig geborgd of bewust gemarkeerd als handmatig/operationeel te controleren.
Nieuwe oefenmodules zijn contractgedrevenConcrete technische oefenmodules worden getest tegen het generieke modulecontract voordat zij in de applicatie worden geregistreerd.

23.4 Testprojecten en solution-organisatie

Testprojecten staan in de solution onder een organisatorische folder tests. De fysieke structuur mag hierop aansluiten, maar de solutionfolder is leidend voor overzichtelijkheid.

Voorbeeldstructuur:

src/
OefenHub.Web/
OefenHub.SharedKernel/
OefenHub.Infrastructure/
OefenHub.Scheduling/
OefenHub.Identity/
OefenHub.Authorization/
OefenHub.Relationships/
OefenHub.Catalog/
OefenHub.Modules.Abstractions/
OefenHub.ExerciseModuleHost/
OefenHub.Practice/
OefenHub.Communication/
OefenHub.Support/
OefenHub.LiveMonitoring/
OefenHub.Admin/
OefenHub.Reporting/
OefenHub.Modules.Arithmetic.Addition/

tests/
OefenHub.Identity.Tests/
OefenHub.Authorization.Tests/
OefenHub.Relationships.Tests/
OefenHub.Catalog.Tests/
OefenHub.ExerciseModuleHost.Tests/
OefenHub.Practice.Tests/
OefenHub.Communication.Tests/
OefenHub.Support.Tests/
OefenHub.LiveMonitoring.Tests/
OefenHub.Admin.Tests/
OefenHub.Reporting.Tests/
OefenHub.Scheduling.Tests/
OefenHub.Modules.Arithmetic.Addition.Tests/
OefenHub.Web.Tests/
OefenHub.EndToEndTests/
OefenHub.ArchitectureTests/
OefenHub.IntegrationTests/

Niet elk testproject hoeft op dag één te bestaan. Een module krijgt een eigen testproject zodra er domeinlogica, DbContext-configuratie, publieke contracten, kritieke workflows of module-specifieke regels aanwezig zijn.

23.5 Testprojectstructuur binnen een module

Een testproject mag eenvoudig beginnen en stapsgewijs groeien. Wanneer een testproject groter wordt, wordt de volgende structuur gebruikt.

OefenHub.Practice.Tests/
Unit/
Integration/
Contracts/
Data/
Workflows/
TestData/
TestDoubles/
MapDoel
UnitSnelle tests voor pure domeinlogica, berekeningen, statusovergangen en validators.
IntegrationTests met echte module-DbContext, EF Core-configuratie, schema, migrations of testdatabase.
ContractsTests op publieke modulecontracten en contractgedrag dat andere modules mogen gebruiken.
DataTests voor seeddata, enumwaarden, queryfilters, indexes en database mapping.
WorkflowsTests voor module- of cross-module usecases die meerdere stappen bevatten.
TestDataBuilders, scenariofixtures en veilige voorbeelddata.
TestDoublesFakes/stubs voor publieke contracten van andere modules.

De mappen zijn organisatorisch. Namespaces mogen eenvoudiger blijven wanneer dat de leesbaarheid vergroot.

23.6 Testlagen

TestlaagPrimair doelEigenaarWanneer verplicht
Unit testsDomeinlogica snel en geïsoleerd testen.Module-eigen testproject.Bij berekeningen, statusregels, validatie en modulecontractlogica.
Module integration testsModule met eigen DbContext, schema en EF-configuratie testen.Module-eigen testproject.Bij databasewrites, queries, migrations, seeddata en constraints.
Contract testsPublieke module-ingangen vastleggen.Module-eigen testproject en/of integratietests.Bij Contracts die door andere modules worden gebruikt.
Cross-module integration testsSamenwerking tussen meerdere modules testen.OefenHub.IntegrationTests.Bij workflows die meerdere modules raken.
Architecture testsDependency-richting en modulegrenzen afdwingen.OefenHub.ArchitectureTests.Altijd voor project- en namespace-afspraken.
Web/component testsBlazor componenten, routing en viewmodelcompositie testen.OefenHub.Web.Tests.Bij complexe UI-state, formulieren, rolweergaven en componentlogica.
End-to-end testsKritieke gebruikerspaden via de applicatie testen.OefenHub.IntegrationTests of apart E2E-project.Voor smoke tests en hoofdflows.
Security/configuration testsPipeline, headers, cookies, authorization en veilige defaults controleren.Web, Authorization, Integration of Architecture tests.Bij securitybaseline en releasechecks.
Performance/load testsBelangrijke query's, PDF, SignalR en dashboards beoordelen.Apart scenario of pipeline stage.Bij bekende performancegevoelige flows.
Manual/operational checksZaken controleren die niet goed automatisch te testen zijn.Releaseproces/beheer.Bijvoorbeeld backup/restore, TickerQ-dashboardafscherming en productieconfiguratie.

23.7 Unit tests

Unit tests zijn bedoeld voor logica die zonder database, netwerk, Blazor-rendering, Keycloak, TickerQ of QuestPDF getest kan worden.

Voorbeelden:

ModuleVoorbeeld unit test
Practicestatistieken voor gemiddelde tijd, mediaan, IQR en uitschieters berekenen.
Relationshipsconflicterende uitnodigingen detecteren.
Supportgebruikersstatus Opgelost of Gesloten afleiden uit sluitregistratie en heropentermijn.
Catalogoefeningstatus en zichtbaarheid bepalen.
Communicationongelezenstatus van thread-events bepalen.
Authorizationactieve rolcontext en prioriteitsvolgorde bepalen.
Concrete oefenmoduleantwoordcontrole, vraaggeneratie en configuratievalidatie.

Voorbeeld van een unit-testbare service:

public sealed class ExerciseRunStatisticsCalculatorTests
{
[Fact]
public void CalculatesMedianForEvenNumberOfQuestions()
{
// arrange
var durations = new[] { 2, 4, 8, 10 };

// act
var result = ExerciseRunStatisticsCalculator.Calculate(durations);

// assert
result.MedianSeconds.Should().Be(6);
}
}

Unit tests mogen test doubles gebruiken voor publieke contracten van andere modules. Zij mogen geen module-interne entities van andere modules gebruiken.

Voor interface-gebaseerde mocks is Moq de primaire mockinglibrary in de V1.0-baseline. Gebruik Moq vooral voor randafhankelijkheden en publieke contracten die in een unit test geïsoleerd moeten worden. Eenvoudige expliciete fakes, builders of in-memory testimplementaties blijven toegestaan wanneer zij leesbaarder of stabieler zijn. Afwijken naar een andere mockinglibrary gebeurt alleen wanneer een concrete testbehoefte aantoonbaar niet passend met Moq kan worden opgelost of wanneer security-/dependencyreview dat vereist.

23.8 Module integration tests

Module integration tests controleren of de module technisch correct werkt met haar eigen DbContext en schema.

Te testen onderwerpen:

  • EF Core mapping;
  • verplichte velden en nullable gedrag;
  • indexes en unique constraints;
  • delete behavior en soft-delete gedrag;
  • queryfilters;
  • seeddata;
  • migrations;
  • JSON/base64-payloadopslag;
  • soft links en snapshotvelden;
  • concurrency en idempotentie.

Voorbeeld:

public sealed class PracticeDbContextTests
{
[Fact]
public async Task CompletedRunsAreReturnedByHistoryQuery()
{
await using var db = await PracticeDbContextFactory.CreateAsync();

await db.ExerciseRuns.AddAsync(ExerciseRunTestData.CompletedRun());
await db.ExerciseRuns.AddAsync(ExerciseRunTestData.IncompleteRun());
await db.SaveChangesAsync();

var rows = await new ExerciseRunHistoryReader(db)
.GetCompletedHistoryAsync(StudentContextTestData.ValidStudent());

rows.Should().ContainSingle();
}
}

De testdatabase moet per test of testklasse schoon worden opgebouwd. Tests mogen elkaar niet afhankelijk maken van volgorde.

23.9 Architecture tests

Architecture tests bewaken dat de modulaire monoliet niet langzaam verandert in een ongestructureerde monoliet.

23.9.1 Toolkeuze

OefenHub gebruikt ArchUnitNET als primaire testtooling voor architecture tests in OefenHub.ArchitectureTests.

De baseline bestaat uit:

OnderdeelKeuze
Architecture-testlibraryTngTech.ArchUnitNET
Testprojecttests/OefenHub.ArchitectureTests
TestadapterArchUnitNET-adapter passend bij het gekozen .NET-testframework; bij xUnit is dat TngTech.ArchUnitNET.xUnit of de actuele xUnit v3-adapter.
UitvoeringVia dotnet test, als onderdeel van pull-request- en CI-checks.
AnalysebasisGecompileerde assemblies van de OefenHub-projecten.

De packageversie wordt tijdens implementatie vastgepind in het centrale packagebeheer. Een package-update mag de architectuurregels niet inhoudelijk versoepelen.

NetArchTest is beoordeeld als mogelijke alternatiefklasse, maar is niet de baseline. Afwijken van ArchUnitNET mag alleen met een expliciet TO-besluit, omdat de regels rond modulegrenzen, dependency-richting en publieke contracten anders opnieuw moeten worden gevalideerd.

23.9.2 Te bewaken regels

Minimaal te bewaken regels:

RegelTestcontrole
Moduleprojecten refereren OefenHub.Web niet.Assembly-/dependencycontrole met ArchUnitNET.
OefenHub.Web gebruikt geen EF Core DbContexts van modules.Type- en namespacecontrole.
OefenHub.Web gebruikt geen module-interne entities.Type-, namespace- en public API-controle.
Data/entities van modules zijn niet publiek bruikbaar over modulegrenzen.Public API/typecontrole, aangevuld met internal-controle.
Publieke module-ingangen staan onder Contracts.Namespace/typecontrole.
Module-interne services, repositories en DbContexts blijven buiten Contracts.Namespace/typecontrole.
Concrete oefenmodules refereren geen Catalog, Practice of Web.Assembly-/dependencycontrole.
Concrete oefenmodules gebruiken primair OefenHub.Modules.Abstractions.Assembly-/dependencycontrole.
SharedKernel bevat geen module-specifieke businesslogica.Namespace- en naamgevingscontrole, aangevuld met review.
Cross-module database-toegang loopt niet via andermans DbContext.Type-, namespace- en dependencycontrole.
Domeinmodules hebben geen dependency op OefenHub.Scheduling voor domeinlogica.Assembly-/dependencycontrole; alleen publieke schedulingcontracten zijn toegestaan waar nodig.
Readmodels blijven module-eigen onder Models/ReadModels.Namespace/typecontrole.

23.9.3 Structuur van het architecture-testproject

OefenHub.ArchitectureTests gebruikt een vaste opbouw zodat regels niet per ontwikkelaar verschillend worden geïnterpreteerd.

tests/
OefenHub.ArchitectureTests/
ArchitectureLoad.cs
ProjectReferenceRules.cs
WebBoundaryRules.cs
ModuleBoundaryRules.cs
ContractBoundaryRules.cs
ExerciseModuleRules.cs
SharedKernelRules.cs
ReadModelRules.cs

Richtlijnen:

  • ArchitectureLoad.cs laadt de assemblies één keer centraal.
  • Elke ruleclass test één afgebakend type architectuurregel.
  • Testnamen beschrijven de verboden richting, niet alleen het technische mechanisme.
  • Foutmeldingen moeten de geschonden modulegrens of dependencyrichting benoemen.
  • Architecture tests mogen geen database, Keycloak, SignalR, TickerQ of webhost starten.
  • Architecture tests vervangen geen code review; ze blokkeren bekende, automatisch controleerbare overtredingen.

23.9.4 Voorbeeldregel

Voorbeeld van een ArchUnitNET-regel voor de intentie dat moduleprojecten niet afhankelijk mogen zijn van OefenHub.Web:

using ArchUnitNET.Domain;
using ArchUnitNET.Loader;
using ArchUnitNET.Fluent;
using Xunit;
using static ArchUnitNET.Fluent.ArchRuleDefinition;

public sealed class ProjectReferenceRules
{
private static readonly Architecture Architecture = new ArchLoader()
.LoadAssemblies(ArchitectureAssemblies.All)
.Build();

private static readonly IObjectProvider<IType> Web =
Types().That().ResideInAssembly("OefenHub.Web").As("OefenHub.Web");

private static readonly IObjectProvider<IType> Catalog =
Types().That().ResideInAssembly("OefenHub.Catalog").As("OefenHub.Catalog");

[Fact]
public void Catalog_must_not_depend_on_web()
{
Types().That().Are(Catalog)
.Should().NotDependOnAny(Web)
.Because("moduleprojecten mogen geen dependency op de Blazor Web-laag hebben")
.Check(Architecture);
}
}

Het voorbeeld is richtinggevend voor de regelvorm. De definitieve helpermethoden, assemblyselectie en packageversies worden in de implementatie vastgelegd zonder de hierboven vastgelegde architectuurregels te versoepelen.

23.10 Contract tests

Publieke contracten zijn de toegestane ingangen tussen modules. Een contracttest controleert niet alleen dat een methode bestaat, maar vooral dat het gedrag stabiel blijft voor andere modules.

Voorbeeldcontracten:

IRelationshipAccessReader
IRelationshipInvitationService
IExerciseRunResultReader
IMessageDispatcher
ITicketActionIndicatorReader
ISchedulingJobClient

Contract tests leggen minimaal vast:

  • welke input geldig is;
  • welke foutvorm bij ongeldige input wordt teruggegeven;
  • of autorisatiecontext opnieuw wordt gecontroleerd;
  • of idempotentie wordt ondersteund;
  • welke outputvelden betrouwbaar gevuld zijn;
  • of geen module-interne entities worden teruggegeven.

Voorbeeld:

[Fact]
public async Task RelationshipAccessReaderReturnsFalseWhenRelationshipIsInactive()
{
var reader = fixture.GetRequiredService<IRelationshipAccessReader>();

var allowed = await reader.HasActiveFriendshipAsync(studentA, studentB);

allowed.Should().BeFalse();
}

23.11 Cross-module integration tests

Cross-module integration tests zijn nodig wanneer één gebruikersactie meerdere modules raakt.

Voorbeelden:

WorkflowBetrokken modulesTe testen
Relatie-uitnodiging versturenRelationships, CommunicationUitnodiging en eventueel kritisch systeembericht ontstaan consistent of niet.
AccountprovisioningIdentity, Relationships, CommunicationPending uitnodigingen worden correct gekoppeld en vervolgcommunicatie ontstaat veilig.
Melding doorzetten naar docentSupport, Communication, RelationshipsTicketafsluiting, doorzetregistratie en namens-bericht blijven consistent.
Oefening delenPractice, Relationships, CommunicationVriendschapscontrole, shared-record en communicatie volgen de workflowregels.
Live meekijken startenLiveMonitoring, Practice, AuthorizationToegang wordt server-side gecontroleerd en LiveViewAudit wordt correct gevuld.

Deze tests moeten ook foutscenario's bevatten:

  • tweede kritieke stap faalt;
  • gebruiker heeft geen toegang meer;
  • relatie is net gedeactiveerd;
  • run is niet afgerond;
  • job wordt dubbel uitgevoerd;
  • bericht of readmodel wordt opnieuw verwerkt;
  • transactie wordt teruggedraaid.

23.12 Transaction boundary tests

Omdat cross-module workflows per geval bepalen welke stappen kritisch zijn, moeten transaction boundaries testbaar worden gemaakt.

Per workflow wordt minimaal vastgelegd:

VraagTestverwachting
Welke stappen zijn kritisch?Falen van een kritieke stap laat geen halve functionele toestand achter.
Welke stappen zijn retrybaar?Falen van een retrybare stap zet een beheerbare pending/failed-status.
Is het systeembericht kritisch?Als het bericht de primaire ingang is, faalt de workflow wanneer berichtcreatie faalt.
Is dubbele verwerking mogelijk?Herhaalde uitvoering is idempotent of wordt veilig geweigerd.
Wat ziet de gebruiker bij falen?Geen technische details, wel correcte foutafhandeling.

Voorbeeld:

Relatie-uitnodiging versturen
- RelationshipInvitation aanmaken: kritisch
- SystemMessage aanmaken als primaire ontvangersingang: kritisch
- Badge/teller bijwerken: niet kritisch

Test:
- forceer fout bij SystemMessage-aanmaak
- verwacht: geen openstaande uitnodiging blijft achter
- verwacht: gebruiker krijgt veilige foutmelding
- verwacht: technische fout is gelogd met CorrelationId

23.13 Database-, migration- en seedtests

Databasegerelateerde tests bewaken dat de gekozen project-DbContext-schema-structuur correct blijft.

Minimaal te testen:

OnderwerpTest
Schema per moduleTabellen worden in het juiste schema aangemaakt.
MigrationvolgordeAlle modulemigrations kunnen op een lege database worden toegepast.
Idempotente seeddataSeeddata kan herhaald worden zonder duplicaten of ongewenste overschrijvingen.
Beheerbare contentPopups/templates worden niet blind teruggezet na beheerwijzigingen.
Soft linksCross-module verwijzingen vereisen geen ongewenste harde FK.
SnapshotsSnapshotvelden worden gevuld op het afgesproken moment en niet stilzwijgend aangepast.
AnonimiseringPersoonsgegevens in snapshots worden volgens vaste regels vervangen.
Delete behaviorHistorische data blijft beschikbaar waar functioneel vereist.

Voor migrations wordt een aparte test gebruikt die een lege database opbouwt vanuit alle modulemigrations. Bij voorkeur wordt daarnaast een upgradepad getest vanuit een representatieve vorige baseline.

Voor EF Core migration history geldt aanvullend:

OnderwerpTestverwachting
History per schemaIedere persistente DbContext gebruikt __EFMigrationsHistory in het eigen schema.
Geen centrale historyEr ontstaat geen gedeelde historytabel zonder contextonderscheid.
DeploymentvolgordeModulemigrations kunnen in de vastgelegde volgorde worden toegepast.
UpgradepadBestaande historytabellen worden niet stilzwijgend verplaatst of hernoemd.

23.14 Seeddata en waardelijsttests

Seeddata en waardelijsten zijn extra gevoelig omdat zij vaak functionele betekenis hebben.

Voorbeelden:

ModuleSeeddata / waardelijst
Identitypublieke rollen en niet-publieke rollen.
Supportticketstatussen en resolutietypen.
Communicationsysteemberichttemplates.
Adminpopupdefinities, featuretoggles en systeeminstellingen.
Catalog / ExerciseModuleHosttechnische modulemetadata.
Schedulingjobtypen of schedulerconfiguratie waar van toepassing.

Tests controleren:

  • technische code blijft stabiel;
  • gebruikerslabels kunnen veilig beheerbaar zijn waar toegestaan;
  • verplichte waardes ontbreken niet;
  • idempotente seed maakt geen duplicaten;
  • seed overschrijft beheerwijzigingen niet zonder expliciete migratiekeuze.

23.15 Oefenmodulecontracttests

Concrete technische oefenmodules worden tegen een gemeenschappelijk contract getest. Hierdoor kan een nieuwe module worden toegevoegd zonder de kernapplicatie per module volledig te herschrijven.

Elke concrete module moet minimaal aantonen:

ContractonderdeelTestverwachting
DescriptorModule heeft stabiele technische sleutel, naam, versie en capabilities.
Configuratie-DTOGeldige configuratie wordt geaccepteerd; ongeldige configuratie geeft duidelijke validatiefouten.
VraaggeneratieAantal vragen en configuratieregels worden gerespecteerd.
AntwoordcontroleGoede, foute en lege antwoorden worden consistent beoordeeld.
Geen ideeIndien ondersteund, telt dit functioneel als fout en bewaart juiste status.
RenderingVraag- en antwoordrepresentatie is bruikbaar voor UI.
PDF-representatieComplexe notatie kan veilig aan PDF-export worden aangeboden.
Backwards compatibilityHistorische payloads kunnen worden gelezen zolang dit volgens modulebeleid vereist is.

Voorbeeldstructuur voor de eerste concrete module:

OefenHub.Modules.Arithmetic.AdditionSubtractionSimple.Tests/
Unit/
AdditionSubtractionAnswerEvaluatorTests.cs
AdditionSubtractionQuestionGeneratorTests.cs
Contracts/
AdditionSubtractionModuleContractTests.cs
TestData/

De eerste V1.0-module Optellen & Aftrekken (simpel) gebruikt het moduledossier Optellen & Aftrekken (simpel) als testinput voor configuratie, vraaggeneratie, antwoordvalidatie, schermstates, payloads en randgevallen.

Concrete modules mogen OefenHub.Modules.Abstractions gebruiken en alleen bewust OefenHub.SharedKernel. Tests moeten bewaken dat concrete modules geen afhankelijkheid nemen op Catalog, Practice, Web of module-interne platformimplementaties.

23.16 Autorisatie- en contexttests

Autorisatie is server-side en mag niet door clientstate, routeparameters of zichtbare knoppen worden afgedwongen. Tests moeten daarom niet alleen happy paths testen, maar ook route-manipulatie, oude browsercontext en combinatierollen.

Minimaal te testen:

ContextVoorbeeldtest
LeerlingOefening starten faalt wanneer niveauautorisatie vervalt.
DocentDocent ziet alleen resultaten binnen eigen docentcontext.
Ouder/voogdResultaatdetail vereist actieve GuardianStudent-relatie bij iedere actie.
BeheerderBeheercontext wordt server-side bepaald, niet via routeparameter.
CombinatierolDocent/ouder-contexten lekken geen autorisatie naar elkaar.
AccountstatusIsActive = false blokkeert toegang maar verwijdert geen historie.
Live meekijkenLive-start maakt alleen audit aan na geldige autorisatiecontrole.
PDF-exportRun-ID alleen is nooit genoeg; export hercontroleert toegang.

Autorisatietests horen deels in OefenHub.Authorization.Tests en deels in module- of integratietests waar objecttoegang concreet wordt toegepast.

23.17 Web-, component- en UI-compositietests

OefenHub.Web bevat routing, layouts, componenten, formulieren, viewmodels en page composition. Web mag geen DbContexts of module-interne entities gebruiken. Tests voor Web richten zich daarom op UI-compositie en interactie, niet op domeinregels.

Te testen onderwerpen:

  • layout- en navigatiekeuzes per rolcontext;
  • afleidingsvrije oefenrunweergave;
  • formulierbinding en validatiemeldingen;
  • correcte aanroep van publieke modulecontracten;
  • geen directe DbContext-injectie in pages/components;
  • lege toestanden;
  • veilige foutweergave;
  • responsive componentgedrag waar technisch testbaar.

Voorbeeld:

Teacher frontpage composition
- Web roept Catalog-query aan voor niveausamenvatting
- Web roept Practice-query aan voor resultaatindicaties
- Web bouwt één viewmodel
- Web leest geen catalog/practice DbContext rechtstreeks

UI-labels en schermspecifieke details blijven primair in schermdocumentatie; dit hoofdstuk beschrijft de technische testaanpak.

23.17.1 Toolkeuze voor component- en end-to-endtests

De V1.0-baseline gebruikt twee aanvullende frontendtestlijnen:

TestlijnToolingProjectScope
Blazor componenttestsbUnitOefenHub.Web.TestsOefenHub-wrappercomponenten, componentcatalogus, parameters, events, validatie, loading/empty/error states en page composition zonder browser.
End-to-endtestsPlaywright .NETOefenHub.EndToEndTestsBeperkte smoke- en regressietests voor primaire gebruikersflows in echte browsercontext.

Componenttests zijn de standaard voor herbruikbare UI-bouwblokken. Zij moeten voorkomen dat pagina's opnieuw zelfstandige HTML-/CSS-opbouw krijgen in plaats van gedeelde OefenHub-componenten.

End-to-endtests blijven bewust beperkt. Zij dekken de belangrijkste happy paths en enkele kritieke fout-/autorisatiescenario's, maar vervangen geen module-, contract-, autorisatie- of componenttests. E2E-tests mogen daarom niet de primaire plek worden voor alle domeinlogica.

Minimale E2E-dekking voor V1.0:

FlowMinimale dekking
Login en rolcontextTestgebruiker kan inloggen of via testauth worden geplaatst en juiste rolcontext openen.
Leerling oefening starten en afrondenOefening starten, antwoord registreren, resultaat bereiken.
Docent dashboard en leerlingoverzichtFrontpage/overzicht laadt met testdata en toont toegestane acties.
Ouder-/voogd kindoverzichtGekoppeld kind zichtbaar; niet-gelinkte data niet zichtbaar.
Beheerder kernbeheerMinimaal één beheerbaar content-/instellingenscherm opent en valideert veilig.
PDF-download smokeGeautoriseerde export levert downloadrespons zonder inhoudelijke PDF-pixelvergelijking.
Live meekijken smokeGeautoriseerde viewer ziet live-status of veilige fallback; reconnectdetails blijven primair component-/integratietest.

De exacte testframeworkkeuze voor xUnit, NUnit of MSTest blijft centraal voor alle testprojecten gelden. bUnit en Playwright worden gebruikt binnen die gekozen testframeworklijn.

23.18 SignalR- en live-meekijktests

Live meekijken gebruikt SignalR als transport, maar server-side opgeslagen voortgang als bron van waarheid. Tests moeten beide aspecten onderscheiden.

OnderwerpTestverwachting
Bron van waarheidNa bevestigd antwoord is voortgang server-side opgeslagen voordat updates worden gebruikt.
AutorisatieViewer mag alleen starten binnen geldige docent- of ouder-/voogdcontext.
LiveViewAuditBewuste live-start maakt auditrecord; alleen online-overzicht openen niet.
ReconnectNa reconnect wordt actuele server-side voortgang opnieuw gelezen.
Reconnectdelays0, 2, 10, 30 en 60 seconden; na 5 automatische pogingen veilige verbroken status.
Sessie-eindeVerlaat leerling de oefening, dan eindigt liveweergave veilig.
Browse-modusTerug naar live gebruikt actuele server-side vraagstatus.
Gelijktijdige viewersMeerdere geautoriseerde meekijkers wijzigen de run niet.

SignalR-tests mogen deels component-/integratietests zijn. Niet alle timingdetails hoeven als fragiele end-to-endtest te worden vastgelegd, maar de reconnectstatussen, foutmelding en herlaadactie moeten component- of integratietestbaar zijn.

23.19 QuestPDF- en exporttests

PDF-export is technisch belangrijk omdat de output historisch consistent moet zijn en gebaseerd wordt op opgeslagen runcontext en snapshots. OefenHub gebruikt daarom een vaste PDF-regressieteststrategie in OefenHub.Reporting.Tests.

23.19.1 Testdoelen

Te testen onderwerpen:

  • veilige bestandsnaamlogica;
  • gebruik van historische runcontext;
  • geen afhankelijkheid van actuele naamgeving wanneer snapshot leidend is;
  • verplichte secties, totalen en contextlabels;
  • tabelheaders op vervolgpagina's;
  • geen rij-splitsing waar dat functioneel is uitgesloten;
  • footeropbouw en paginanummering;
  • module-specifieke vraag-/antwoordrepresentatie;
  • fallbackrepresentatie bij module- of payloadproblemen;
  • tijdelijke bestandsopslag en cleanup;
  • autorisatiehercontrole bij export;
  • foutafhandeling zonder dataverlies;
  • privacy: geen persoonsgegevens buiten toegestane context en geen payloads in logs.

23.19.2 Tooling

De PDF-regressietestbaseline gebruikt:

OnderdeelToolingDoel
PDF-renderingQuestPDFProductiepad en testpad gebruiken dezelfde documentopbouw.
SnapshotassertiesVerifyVergelijken van genormaliseerde tekst-, metadata-, objectmodel- en eventueel image-output.
TekstextractiePdfPig of Verify.PdfPigControleren van PDF-inhoud zonder volledige binaire vergelijking.
TestadapterAdapter bij gekozen testframeworkBijvoorbeeld Verify.Xunit bij xUnit.
Visuele kernsnapshotsQuestPDF image- of SVG-output waar stabiel toepasbaarAlleen voor geselecteerde kernlayouts.

Packageversies worden centraal vastgepind zodra het testframework is gekozen. De strategische keuze voor Verify/PdfPig is onderdeel van de baseline.

23.19.3 Structurele regressietests

Structurele regressietests zijn de standaard. Zij maken van een gegenereerde PDF een genormaliseerd testartefact en controleren bijvoorbeeld:

  • pagina-aantal;
  • documenttitel of downloadcontext;
  • aanwezigheid en volgorde van kernsecties;
  • tekstinhoud van samenvatting, resultaattabel en statistieken;
  • aanwezigheid van headers en footers per pagina;
  • geselecteerde metadata, na normalisatie van niet-deterministische waarden;
  • afwezigheid van verboden tekst, zoals technische foutdetails of ruwe payloadvelden.

Volledige bytevergelijking van PDF-bestanden is geen geldige primaire regressietest, omdat PDF-output metadata, ordering of renderingdetails kan bevatten die functioneel niet relevant zijn.

23.19.4 Beperkte visuele regressie

Visuele regressie wordt beperkt toegepast voor enkele stabiele kernlayouts:

LayoutDoel
Eénpagina-resultaatBasale typografie, header, samenvatting en footer.
Meerpagina-resultaatPaginering, herhaalde tabelheaders en footerpagina's.
Rijke moduleweergaveModule-specifieke notatie zonder layoutbreuk.
FallbackweergaveVeilige fallbacktekst zonder visuele ontsporing.

Deze tests draaien bij voorkeur in een vaste runtime/container met vaste fonts. Ze hoeven niet in iedere lokale snelle testlus te draaien. CI mag onderscheid maken tussen pull-requestchecks, nightly checks en releasechecks.

23.19.5 Snapshotbeheer

Voor snapshotbeheer gelden aanvullende regels:

  1. snapshots bevatten alleen fictieve of geanonimiseerde testdata;
  2. klok, cultuur, tijdzone en testfixtures zijn deterministisch;
  3. niet-deterministische velden worden vooraf genormaliseerd;
  4. snapshotupdates vereisen review en een duidelijke reden;
  5. ontvangen testoutput wordt niet automatisch gecommit;
  6. visuele snapshots worden niet gebruikt om brede ontwerpwijzigingen te accorderen zonder inhoudelijke review.

23.20 Scheduling-, TickerQ- en jobtests

Scheduling heeft een eigen project en bij voorkeur een eigen schema. Tests moeten bewaken dat jobs persistent, herleidbaar en beheerbaar zijn.

Te testen onderwerpen:

OnderwerpTestverwachting
JobaanmaakDomeinmodule kan via schedulingcontract een job aanvragen.
PersistenceHerstart van de applicatie verliest geplande of lopende jobs niet.
JobtypeAlleen bekende jobtypes worden uitgevoerd.
RetrybeleidRetryconfiguratie wordt per jobtype toegepast en is begrensd.
IdempotentieDubbele uitvoering veroorzaakt geen dubbele functionele mutatie.
Failed statusNa maximale retries ontstaat een beheerbare failed-status.
CorrelationJobId en CorrelationId zijn door de hele keten herleidbaar.
DashboardTickerQ-webinterface is alleen intern beschikbaar.
DomeinuitvoeringScheduling roept domeinacties alleen via publieke contracten aan.

Bij multi-domain jobs moet expliciet worden getest of de gekozen foutstrategie correct werkt: atomair, compensatie, retry of beheerbare failed-status.

23.20.1 Mailafhandelingstests

Mailafhandeling wordt getest als ondersteunende providerintegratie en als retrybare of kritieke workflowstap waar dat functioneel is vastgelegd.

TestgebiedTe bewijzen gedrag
MailproviderconfiguratieOntbrekende of foutieve providerconfiguratie faalt veilig zonder secrets te lekken.
Maildelivery jobRetrybeleid, idempotency key, failed-status en correlation-id werken correct.
Mailfout na bronmutatieBrondata blijft consistent en de gebruiker ziet geen misleidende rollbackstatus.
Kritieke mailaanvraagWorkflow faalt atomair wanneer mailaanvraag functioneel noodzakelijk is en niet veilig kan worden vastgelegd.
MailinhoudGeen secrets, tokens, wachtwoorden, ruwe modulepayloads of ongeautoriseerde domeindata.

23.21 Logging-, correlation- en foutafhandelingstests

Loggingtests richten zich niet op exacte tekstregels, maar op aanwezigheid van noodzakelijke context en afwezigheid van gevoelige inhoud.

Minimaal te testen:

  • iedere request krijgt of behoudt een CorrelationId;
  • cross-module workflows geven CorrelationId door;
  • jobs loggen JobId, JobType, pogingnummer en status;
  • access-denied wordt gelogd zonder inhoudelijke data te lekken;
  • technische foutpagina's tonen geen stacktrace of SQL-details;
  • logs bevatten geen tokens, wachtwoorden, credentials of volledige payloads;
  • foutafhandeling maakt onderscheid tussen validatiefout, autorisatiefout, niet gevonden en technische fout.

Voorbeeld:

PDF-export zonder toegang
- gebruiker vraagt run van niet-gekoppeld kind op
- response toont veilige toegang-geweigerdafhandeling
- log bevat CorrelationId, actor, rolcontext en foutcategorie
- log bevat geen vraaginhoud, antwoorden of PDF-payload

23.22 Security- en configuratietests

Omdat security geen apart project heeft, moeten securitychecks verspreid maar expliciet in de teststrategie blijven.

Te testen of te controleren:

OnderwerpTest/controle
HSTSActief in productieachtige omgeving, niet onbedoeld lokaal blokkerend.
CSPVastgelegde CSP-baseline aanwezig en passend bij Blazor, SignalR, MudBlazor, fonts, images en PDF-downloads.
SecurityheadersVerwachte headers worden gezet via pipeline/middleware.
CookiesSecure, HttpOnly en SameSite waar passend.
Browser storageGeen tokens, credentials, persoonsgegevens of autorisatiedata.
Rate limitingRoutecategorieën gebruiken de vastgelegde limieten; misbruikscenario's worden begrensd zonder normale flows te blokkeren.
Identity callbackOngeldige of onvolledige provisioning geeft geen gedeeltelijke sessie.
Authorization policiesPolicies falen gesloten bij ontbrekende context.
TickerQ-dashboardAlleen intern en geautoriseerd bereikbaar.
SecretsVerplichte secrets ontbreken niet en worden niet uit code of testdata geladen.

Sommige controles zijn pipeline-/integratietests; andere horen als releasecheck of operationele controle in CI/CD.

23.23 Performance- en beschikbaarheidstests

Performance wordt gericht getest op bekende risicogebieden. Er wordt niet geprobeerd om elke functie kunstmatig te loadtesten of top-tier latency af te dwingen. De V1.0-grenzen uit hoofdstuk 22 zijn voldoende voor acceptabele performance bij representatieve belasting.

Richtgebieden:

  • leerling oefenrun met directe opslag na ieder bevestigd antwoord;
  • geschiedenisqueries met filters en paginering;
  • ouder-/voogdresultaten over alle niveaus;
  • docentoverzichten met leerlingen en autorisaties;
  • berichtenbadges en meldingenindicaties;
  • SignalR live meekijken met meerdere viewers;
  • PDF-export van lange resultaatsets;
  • TickerQ-verwerking van verlopen heropentermijnen, uitnodigingen en cleanup;
  • frontpage-samenvattingsblokken.

Minimale performancevalidatie:

TestgebiedMinimale validatieAcceptatiegrens
Oefenrunstartflow en antwoord bevestigen met representatieve moduledatagrenzen uit 22.5.1
Frontpages en dashboardseerste pagina laden met realistische aantallen kinderen, leerlingen, tickets en badgesgrenzen uit 22.5.1
Overzichtenfilteren, sorteren en pagineren met groeigevoelige datasetsstandaard 25 en maximaal 100 regels per pagina
Geschiedenis en resultatendetailpagina, samenvatting en gefilterde historiegeen volledige onbeperkte dataset in één response
Live meekijkenvoortgang publiceren na opgeslagen antwoordp95 ≤ 3 s voor updatezichtbaarheid; reconnectinstellingen apart valideren
PDF-exportnormale resultaatset en bovengrensscenarionormale export p95 ≤ 60 s; grote export begrenzen of jobmatig verwerken
Beheeranalyselogs, supportoverzichten en beheerfiltersp95 ≤ 8 s met verplichte filters/paginering
Jobsniet-kritieke side-effects en cleanupnormaal binnen geconfigureerde interval en beheerbaar bij failed-status

De testset gebruikt fictieve of geanonimiseerde data. Structurele overschrijding leidt eerst tot queryoptimalisatie, indexering, readmodel/caching, payloadverkleining, paginering of jobmatige verwerking binnen de bestaande modulaire monoliet. Extra infrastructuur, microservices of high-availabilitymaatregelen zijn geen standaardreactie op een performanceoverschrijding in V1.0.

23.24 Testdata en scenariofixtures

Testdata moet representatief zijn zonder echte persoonsgegevens te gebruiken.

Minimale scenariofixtures:

FixtureDoel
Leerling met actief niveauOefenrun-, frontpage- en geschiedenisflows.
Leerling zonder niveauProfiel-/niveaublokkade.
Docent met eigen leerlingAutorisatie, resultaten en live meekijken.
Docent zonder toegangToegang-geweigerdscenario's.
Ouder/voogd met gekoppeld kindResultaten, geschiedenis en live meekijken.
Ouder/voogd na ontkoppelingDirect vervallen toegang.
BeheerderBeheerflows en supportcorrecties.
CombinatierolContextscheiding tussen beheerder, docent en ouder/voogd.
Afgeronde runResultaat, geschiedenis, PDF en statistieken.
Niet-afgeronde runHervatten en live voortgang.
Testrun docentUitsluiting van reguliere geschiedenis en cleanup.
Gedeelde oefeningOntvangst, starten en eigen runvorming.
Ticket met heropentermijnOpgelost/Gesloten-afleiding en schedulerverwerking.

Testdata wordt bij voorkeur opgebouwd via builders in plaats van grote ondoorzichtige databasedumps.

23.25 Traceability voor requirements, acceptatiecriteria en module-eisen

Traceability wordt niet alleen administratief bijgehouden, maar ook gebruikt om ontbrekende testdekking zichtbaar te maken.

Per centrale eis uit de Software Requirements Specification wordt minimaal één van de volgende statussen vastgelegd:

StatusBetekenis
Automatisch getestEr bestaat een unit-, integratie-, contract-, architecture-, component- of E2E-test.
Handmatig getestDe eis vereist handmatige acceptatie of visuele controle.
Operationeel geborgdDe eis wordt via deployment, beheerprocedure, monitoring of configuratie geborgd.
Beleidsmatig geborgdDe eis is een proces-/documentatieregel en niet zinvol automatisch testbaar.
Nog te dekkenEr is nog geen passende test of borging vastgelegd.

Voor module-eisen geldt hetzelfde, maar in het aparte module-eisenregister moet zichtbaar blijven welke concrete module het contract of de concrete module-eis afdekt.

Voorbeeld:

EisDekkingstypeTestlocatie
Autorisatie ouderresultaatIntegratietestOefenHub.Practice.Tests/Integration
ModuleantwoordcontroleModulecontracttestOefenHub.Modules.Arithmetic.Addition.Tests/Contracts
Geen directe Web-toegang tot DbContextArchitecture testOefenHub.ArchitectureTests
PDF-bestandsnaamlogicaUnit/integratietestOefenHub.Reporting.Tests
TickerQ-dashboard internSecurity/configuratiecheckOefenHub.IntegrationTests of releasecheck

23.26 Definition of Done voor technische wijzigingen

Een technische wijziging is pas afgerond wanneer de relevante test- en documentatie-impact is beoordeeld.

Minimale checklist:

ControleVerwachting
Module-eigenaarschapDuidelijk welke module eigenaar is van code, data en tests.
TestdekkingUnit-, integratie-, contract-, architecture- of handmatige test gekozen waar passend.
Cross-module impactPublieke contracten, workflows en transaction boundaries beoordeeld.
Database-impactDbContext, schema, migration, seeddata en soft link/snapshotbeleid gecontroleerd.
Security-impactAutorisatie, logging, secrets, headers, cookies of rate limits beoordeeld waar relevant.
Privacy-impactPersoonsgegevens, snapshots, logs, exports en testdata gecontroleerd.
Software Requirements Specification en acceptatiecriteria-impactRequirement- en acceptatiecriteriadekking bijgewerkt of gemotiveerd.
Documentatie-impactFunctioneel Ontwerp, Software Requirements Specification, Technisch Ontwerp, database-informatie, ERD, schermdocumentatie en usecases beoordeeld.
CIRelevante testsets draaien groen.
RegressierisicoKritieke hoofdflows blijven afgedekt.

23.27 CI/CD kwaliteitsgrenzen

De exacte pipeline wordt in het beheer- en deploymenthoofdstuk uitgewerkt; de teststrategie vereist minimaal deze kwaliteitsgrenzen.

PipelinefaseMinimale checks
BuildAlle projecten compileren, warnings volgens afgesproken beleid.
Unit testsModule-unit tests draaien snel en deterministisch.
Architecture testsDependencyregels en projectgrenzen worden gecontroleerd.
Integration testsGeselecteerde module- en cross-module tests draaien tegen testdatabase.
Migration checkMigrations kunnen op lege database worden toegepast.
Security/config checkBasisheaders, cookies, secretsvalidatie en verboden configuratie worden gecontroleerd.
ModulecontracttestsConcrete oefenmodules voldoen aan abstractions en contractverwachtingen.
Smoke testsKritieke applicatiepaden worden na deployment kort gecontroleerd.

Niet elke zware integratie- of performance-test hoeft bij iedere lokale build te draaien. CI maakt onderscheid tussen snelle checks, pull-requestchecks, nightly checks en releasechecks.

23.28 Kwaliteitsgrenzen en bewuste beperkingen

De teststrategie legt ook vast wat niet wordt nagestreefd.

Geen doelReden
Alles via E2E testenTe traag, fragiel en onvoldoende gericht op modulegrenzen.
Volledige pixel-perfect PDF-dekking voor alle scenario'sStructurele PDF-correctheid is leidend; visuele snapshots blijven beperkt tot stabiele kernlayouts.
Volledige productie-load simuleren vanaf dag éénEerst representatieve risicoscenario's bepalen.
Echte identity-provider volledig mockloos testen in alle testsContract- en integratietests worden gescheiden van volledige externe flowtests.
Oneindige retries testen als succespadRetries zijn begrensd en failed-status is een geldige uitkomst.
Productiedata gebruiken voor testdataNiet toegestaan vanwege privacy en dataveiligheid.

23.29 Implementatieverificaties

De volgende implementatieverificaties moeten vóór of tijdens realisatie expliciet worden afgerond:

PuntTe bepalen
TestframeworkDefinitieve keuze voor xUnit/NUnit/MSTest en assertion library. Moq is de baseline mockinglibrary; packageversie en security-/dependencyreview worden centraal gevalideerd.
ArchUnitNET-inrichtingPackageversie, testadapter, centrale assemblyloader en eerste regelset inrichten.
TestdatabaseKeuze tussen SQL Server container, LocalDB of andere testdatabase.
Migration teststrategieLege-database- en upgradepadtests inrichten inclusief migration history per schema.
bUnit/Playwright-inrichtingPackageversies, testfixtures, testauth en CI-browserinstallatie inrichten.
SignalR-testaanpakReconnectstatussen, foutmelding en herstel uit server-side brondata automatiseren.
TickerQ-testaanpakHoe persistentie, dashboardafscherming en retries geautomatiseerd worden getest.
TraceabilityregisterWaar testdekking per Software Requirements Specification en acceptatiecriteria/module-eis praktisch wordt bijgehouden.
PerformancecriteriaConcrete grenswaarden zodra datasets en hostingprofiel bekend zijn.