Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Altijd up to date documentatie met maximaal descriptieve tests (09-05-2023 TestNet Voorjaarsevent)

Altijd up to date documentatie met maximaal descriptieve tests (09-05-2023 TestNet Voorjaarsevent)

Niemand houdt van documentatie schrijven - en terecht! Documentatie schrijven en onderhouden kost veel energie. Tegelijkertijd wordt het nauwelijks gelezen en veroudert snel. Toch is documentatie een belangrijke hulpbron voor (nieuwe) ontwikkelaars om snel up to speed te komen over de werking van een codebase.

Moeten we daarom door de zure appel heen bijten, en weer gaan zwoegen op uitgebreide specificatiedocumenten? Gelukkig niet! Tests bieden een uitkomst. Door extra aandacht te besteden aan de scope en leesbaarheid van je tests, kun je deze transformeren van eenvoudig validatiemechanisme naar gezaghebbende bron van informatie voor ontwikkelaars. Met maximaal descriptieve tests is je documentatie altijd up to date.

Karl van Heijster

May 08, 2023
Tweet

More Decks by Karl van Heijster

Other Decks in Technology

Transcript

  1. =

  2. ests zijn een in potentie informatieve tekst. Het is aan

    jou als schrijver van de test om ervoor te zorgen dat die potentie gerealiseerd o elfs gemaximaliseerd wordt. Dat doe je door ervoor te zorgen dat de informatie die in de test zit, zo effectief mogelijk bij de leze erechtkomt. Met andere woorden: maak het makkelijk voor de lezer om de boodschap van de test te begrijpen. De belangrijk formatie moet als het ware direct van het scherm af springen, zó het hoofd van de lezer in. Als je het de lezer moeilijk om erachter t omen wat de boodschap van je test (qua informatieve tekst) is, dan zal hij of zij ongetwijfeld het punt missen. In dat geval had j isschien net zo goed geen test kunnen schrijven. Jouw test heeft er in elk geval niet voor gezorgd dat de lezer de boel sneller begree an anders. Een veel voorkomend antipatroon is bijvoorbeeld dat je veel te veel informatie geeft, al helemaal als je dat allemaal in éé eer doet. Vergelijk het met het geven van een PowerPoint. Je kunt je hele presentatie uitschrijven en die uitgeschreven tekst op de slide rojecteren, maar dat is een superinefficiënte manier van die informatie overbrengen. Al helemaal als je er nog bij gaat staan prate ok. Je toehoorders zullen proberen de tekst te gaan lezen, en ondertussen gaan ze ook nog naar je luisteren, met als gevolg dat z eide maar half meekrijgen. PowerPoints hebben zo hun eigen conventies als het om het overbrengen van informatie gaat. Je zet allee aar bulletpoints op je slides – dat is de belangrijke informatie – en daar ga je vervolgens over vertellen – en in dat verhaal werk j e belangrijke punten uit. De slides stellen je toehoorders dan in staat om op hoofdlijnen het verhaal te volgen, terwijl je met je praatj lle interessante details overbrengt. Maar goed, ik ben hier niet om een cursus PowerPoint te geven, ik ben hier om te vertellen over tes ls documentatie. Welnu, daar gelden eigenlijk dezelfde principes. Ook voor tests (qua informatieve tekst) geldt: maak het voor de leze akkelijk om de essentie uit je verhaal te halen. Optimaliseer je tests voor leesbaarheid. Dat doe je bijvoorbeeld door aandacht t esteden aan de titel van je test. Deze moet een samenvatting vormen van de inhoud ervan. Een unittest bestaat gewoonlijk uit dri elen: Arrange, Act en Assert. Zorg ervoor dat je titel dezelfde opdeling kent. Het eerste deel van je titel geeft dan weer: dit is wat d oestand van het systeem is; het tweede deel: dit is de actie die het systeem vervolgens uitvoert, en het derde: dit is wat dit tot gevol eeft. Je kunt deze drie delen in één mooie volzin gieten door het Given When Then-patroon te gebruiken. Een voorbeeld van zo’n tite : GivenNavigator_WhenGoesToBarPage_ThenNavigatesToBarUrl. Daarnaast doe je er goed aan om de inhoud van je test zodanig t chrijven dat de belangrijkste informatie meteen zichtbaar is voor de lezer. Leg de nadruk op de relevante aspecten van je test, en – d ndere kant van de munt – haal dat weg wat niet bijdraagt aan het begrip van de test. Gaat je test over een Navigator met ee epaalde eigenschap, zorg dan dat die eigenschap meteen duidelijk is voor de lezer. Alle andere eigenschappen dragen niet bij aa et begrip van de test en hoeven daarom niet expliciet in de testbody genoemd te worden. Je kunt deze informatie wegwerken doo
  3. Effectief je boodschap communiceren 1. Vat je test samen in

    de titel 2. Gebruik geen logica in je tests 3. Benadruk belangrijke informatie
  4. [TestMethod] public void GivenNavigator_WhenGoesToBarPage_ThenNavigatesToBarUrl() { var baseUrl = "http://foo.com/"; var

    nav = new Navigator(baseUrl); nav.GoToBarPage(); nav.GetCurrentUrl().Should().Be(baseUrl + "/bar"); }
  5. [TestMethod] public void Navigator_GoesToBarPage_NavigatesToBarUrl() { var baseUrl = "http://foo.com/"; var

    nav = new Navigator(baseUrl); nav.GoToBarPage(); nav.GetCurrentUrl().Should().Be(baseUrl + "/bar"); }
  6. [TestMethod] public void Navigator_GoesToBarPage_NavigatesToBarUrl() { var baseUrl = "http://foo.com/"; var

    nav = new Navigator(baseUrl); nav.GoToBarPage(); nav.GetCurrentUrl().Should().Be(baseUrl + "/bar"); }
  7. [TestMethod] public void Navigator_GoesToBarPage_NavigatesToBarUrl() { var baseUrl = "http://foo.com/"; var

    nav = new Navigator(baseUrl); nav.GoToBarPage(); nav.GetCurrentUrl().Should().Be(baseUrl + "/bar"); }
  8. [TestMethod] public void Navigator_GoesToBarPage_NavigatesToBarUrl() { var nav = new Navigator("http://foo.com/");

    nav.GoToBarPage(); nav.GetCurrentUrl().Should().Be("http://foo.com/bar"); }
  9. [TestMethod] public void TooMuchInformation() { var dependency1 = new Dependency1();

    var dependency2 = new Dependency2(); var dependency3 = new Dependency3(dependency1, dependency2); var sut = new SystemUnderTest(); sut.Initialize(dependency1, dependency3); var setup = sut.SomeOperation(); foreach (var thing in setup.Things) { thing.Foo = sut.DoFoo(); thing.Bar = sut.DoBar(); thing.Done(); } var counter = sut.DoSomethingWithSetup(setup); counter.Should().Be(3); sut.DoSomething("Relevant variable"); sut.SomethingDone.Should().BeTrue(); }
  10. [TestMethod] public void TooMuchInformation() { var dependency1 = new Dependency1();

    var dependency2 = new Dependency2(); var dependency3 = new Dependency3(dependency1, dependency2); var sut = new SystemUnderTest(); sut.Initialize(dependency1, dependency3); var setup = sut.SomeOperation(); foreach (var thing in setup.Things) { thing.Foo = sut.DoFoo(); thing.Bar = sut.DoBar(); thing.Done(); } var counter = sut.DoSomethingWithSetup(setup); counter.Should().Be(3); sut.DoSomething("Relevant variable"); sut.SomethingDone.Should().BeTrue(); }
  11. [TestMethod] public void Factory() { var sut = GetSutWithComplexSetUpLogic(); sut.DoSomething("Relevant

    variable"); sut.SomethingDone.Should().BeTrue(); } private static SystemUnderTest GetSutWithComplexSetUpLogic() { var dependency1 = new Dependency1(); var dependency2 = new Dependency2(); var dependency3 = new Dependency3(dependency1, dependency2); var sut = new SystemUnderTest(); sut.Initialize(dependency1, dependency3); var setup = sut.SomeOperation(); foreach (var thing in setup.Things) { thing.Foo = sut.DoFoo(); thing.Bar = sut.DoBar(); thing.Done(); } var counter = sut.DoSomethingWithSetup(setup);
  12. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Wat is

    het juiste antwoord op deze vraag? A. Zus B. Zo
  13. QTI

  14. <qti-assessment-item title="TestItem" time-dependent="false" adaptive="false" identifier="TestItemId"> <qti-item-body> <div class="qti-layout-row"> <div class="qti-layout-col12">

    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> </div> </div> </qti-item-body> </qti-assessment-item>
  15. <qti-assessment-item title="TestItem" time-dependent="false" adaptive="false" identifier="TestItemId"> <qti-item-body> <div class="qti-layout-row"> <div class="qti-layout-col12">

    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> <img src="Image.jpg" alt="Image named 'Image.jpg'" width="200" height="113"/> </div> </div> </qti-item-body> </qti-assessment-item>
  16. <qti-assessment-item title="TestItem" time-dependent="false" adaptive="false" identifier="TestItemId"> <qti-item-body> <div class="qti-layout-row"> <div class="qti-layout-col12">

    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> <img src="Image.jpg" alt="Image named 'Image.jpg'" width="200" height="113"/> <p>Wat is het juiste antwoord op deze vraag?</p> </div> </div> </qti-item-body> </qti-assessment-item>
  17. <qti-assessment-item title="TestItem" time-dependent="false" adaptive="false" identifier="TestItemId"> <qti-item-body> <div class="qti-layout-row"> <div class="qti-layout-col12">

    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> <img src="Image.jpg" alt="Image named 'Image.jpg'" width="200" height="113"/> <p>Wat is het juiste antwoord op deze vraag?</p> <qti-choice-interaction max-choices="1" min-choices="0" shuffle="false" response-identifier="RESPONSE"> <qti-simple-choice identifier="A"> <p>Zus</p> </qti-simple-choice> <qti-simple-choice identifier="B"> <p>Zo</p> </qti-simple-choice> </qti-choice-interaction> </div> </div> </qti-item-body> </qti-assessment-item>
  18. <qti-assessment-item title="TestItem" time-dependent="false" adaptive="false" identifier="TestItemId"> <qti-response-declaration identifier="RESPONSE" cardinality="single" base-type="identifier"> <qti-correct-response>

    <qti-value>A</qti-value> </qti-correct-response> </qti-response-declaration> <qti-item-body> <div class="qti-layout-row"> <div class="qti-layout-col12"> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> <img src="Image.jpg" alt="Image named 'Image.jpg'" width="200" height="113"/> <p>Wat is het juiste antwoord op deze vraag?</p> <qti-choice-interaction max-choices="1" min-choices="0" shuffle="false" response-identifier="RESPONSE"> <qti-simple-choice identifier="A"> <p>Zus</p> </qti-simple-choice> <qti-simple-choice identifier="B"> <p>Zo</p> </qti-simple-choice> </qti-choice-interaction> </div> </div> </qti-item-body> </qti-assessment-item>
  19. <qti-assessment-item title="TestItem" time-dependent="false" adaptive="false" identifier="TestItemId"> <qti-response-declaration identifier="RESPONSE" cardinality="single" base-type="identifier"> <qti-correct-response>

    <qti-value>A</qti-value> </qti-correct-response> </qti-response-declaration> <qti-outcome-declaration identifier="SCORE" cardinality="single" base-type="float"> <qti-default-value> <qti-value>0</qti-value> </qti-default-value> </qti-outcome-declaration> <qti-outcome-declaration identifier="MAXSCORE" cardinality="single" base-type="float"> <qti-default-value> <qti-value>1</qti-value> </qti-default-value> </qti-outcome-declaration> <qti-item-body> <div class="qti-layout-row"> <div class="qti-layout-col12"> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> <img src="Image.jpg" alt="Image named 'Image.jpg'" width="200" height="113"/> <p>Wat is het juiste antwoord op deze vraag?</p> <qti-choice-interaction max-choices="1" min-choices="0" shuffle="false" response-identifier="RESPONSE"> <qti-simple-choice identifier="A"> <p>Zus</p> </qti-simple-choice> <qti-simple-choice identifier="B"> <p>Zo</p> </qti-simple-choice> </qti-choice-interaction> </div> </div> </qti-item-body> <qti-response-processing template="https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct.xml"/> </qti-assessment-item>
  20. [TestMethod] public void ChoiceInteractionScoredDichotomous_ConvertToQti_ReturnsValidQtiResponseDeclarationWithoutMapping() { //Arrange var alternativeId = Guid.NewGuid();

    var item = GetItemWithBlock(new ChoiceInteraction() { ScorePolytomous = false, Solution = new List<Guid>() { alternativeId }, }); //Act var result = GetItemGenerator(item).GetQti(); //Assert var responseDeclaration = GetQtiElementOrDefault(result); responseDeclaration.Should().BeEquivalentTo(XElement.Parse($@" <qti-response-declaration identifier=""RESPONSE"" cardinality=""single"" base-type=""identifier"" xmlns=""http://www.imsglobal.org/xsd/imsqtiasi_v3p0""> <qti-correct-response> <qti-value>_{alternativeId}</qti-value> </qti-correct-response> </qti-response-declaration>")); }
  21. [TestMethod] public void ChoiceInteractionScoredDichotomous_ConvertToQti_ReturnsValidQtiResponseDeclarationWithoutMapping() { //Arrange var alternativeId = Guid.NewGuid();

    var item = GetItemWithBlock(new ChoiceInteraction() { ScorePolytomous = false, Solution = new List<Guid>() { alternativeId }, }); //Act var result = GetItemGenerator(item).GetQti(); //Assert var responseDeclaration = GetQtiElementOrDefault(result); responseDeclaration.Should().BeEquivalentTo(XElement.Parse($@" <qti-response-declaration identifier=""RESPONSE"" cardinality=""single"" base-type=""identifier"" xmlns=""http://www.imsglobal.org/xsd/imsqtiasi_v3p0""> <qti-correct-response> <qti-value>_{alternativeId}</qti-value> </qti-correct-response> </qti-response-declaration>")); }
  22. [TestMethod] public void ChoiceInteractionScoredDichotomous_ConvertToQti_ReturnsValidQtiResponseDeclarationWithoutMapping() { //Arrange var alternativeId = Guid.NewGuid();

    var item = GetItemWithBlock(new ChoiceInteraction() { ScorePolytomous = false, Solution = new List<Guid>() { alternativeId }, }); //Act var result = GetItemGenerator(item).GetQti(); //Assert var responseDeclaration = GetQtiElementOrDefault(result); responseDeclaration.Should().BeEquivalentTo(XElement.Parse($@" <qti-response-declaration identifier=""RESPONSE"" cardinality=""single"" base-type=""identifier"" xmlns=""http://www.imsglobal.org/xsd/imsqtiasi_v3p0""> <qti-correct-response> <qti-value>_{alternativeId}</qti-value> </qti-correct-response> </qti-response-declaration>")); }
  23. [TestMethod] public void ChoiceInteractionScoredDichotomous_ConvertToQti_ReturnsVa { //Arrange var alternativeId = Guid.NewGuid();

    var item = GetItemWithBlock(new ChoiceInteraction() { ScorePolytomous = false, Solution = new List<Guid>() { alternativeId }, }); //Act var result = GetItemGenerator(item).GetQti(); //Assert var responseDeclaration = GetQtiElementOrDefault(result); responseDeclaration.Should().BeEquivalentTo(XElement.Parse($@"
  24. [TestMethod] public void ChoiceInteractionScoredDichotomous_ConvertToQti_ReturnsVa { //Arrange var alternativeId = Guid.NewGuid();

    var item = GetItemWithBlock(new ChoiceInteraction() { ScorePolytomous = false, Solution = new List<Guid>() { alternativeId }, }); //Act var result = GetItemGenerator(item).GetQti(); //Assert var responseDeclaration = GetQtiElementOrDefault(result); responseDeclaration.Should().BeEquivalentTo(XElement.Parse($@"
  25. [TestMethod] public void ChoiceInteractionScoredDichotomous_ConvertToQti_ReturnsVa { //Arrange var alternativeId = Guid.NewGuid();

    var item = GetItemWithBlock(new ChoiceInteraction() { ScorePolytomous = false, Solution = new List<Guid>() { alternativeId }, }); //Act var result = GetItemGenerator(item).GetQti(); //Assert var responseDeclaration = GetQtiElementOrDefault(result); responseDeclaration.Should().BeEquivalentTo(XElement.Parse($@"
  26. }); //Act var result = GetItemGenerator(item).GetQti(); //Assert var responseDeclaration =

    GetQtiElementOrDefault(result); responseDeclaration.Should().BeEquivalentTo(XElement.Parse($@" <qti-response-declaration identifier=""RESPONSE"" cardinality=""single"" base-type=""identifier"" xmlns=""http://www.imsglobal.org/xsd/imsqtiasi_v3p0""> <qti-correct-response> <qti-value>_{alternativeId}</qti-value> </qti-correct-response> </qti-response-declaration>")); }
  27. }); //Act var result = GetItemGenerator(item).GetQti(); //Assert var responseDeclaration =

    GetQtiElementOrDefault(result); responseDeclaration.Should().BeEquivalentTo(XElement.Parse($@" <qti-response-declaration identifier=""RESPONSE"" cardinality=""single"" base-type=""identifier"" xmlns=""http://www.imsglobal.org/xsd/imsqtiasi_v3p0""> <qti-correct-response> <qti-value>_{alternativeId}</qti-value> </qti-correct-response> </qti-response-declaration>")); }
  28. }); //Act var result = GetItemGenerator(item).GetQti(); //Assert var responseDeclaration =

    GetQtiElementOrDefault(result); responseDeclaration.Should().BeEquivalentTo(XElement.Parse($@" <qti-response-declaration identifier=""RESPONSE"" cardinality=""single"" base-type=""identifier"" xmlns=""http://www.imsglobal.org/xsd/imsqtiasi_v3p0""> <qti-correct-response> <qti-value>_{alternativeId}</qti-value> </qti-correct-response> </qti-response-declaration>")); }
  29. <qti-assessment-item title="TestItem" time-dependent="false" adaptive="false" identifier="TestItemId"> <qti-response-declaration identifier="RESPONSE" cardinality="single" base-type="identifier"> <qti-correct-response>

    <qti-value>A</qti-value> </qti-correct-response> </qti-response-declaration> <qti-outcome-declaration identifier="SCORE" cardinality="single" base-type="float"> <qti-default-value> <qti-value>0</qti-value> </qti-default-value> </qti-outcome-declaration> <qti-outcome-declaration identifier="MAXSCORE" cardinality="single" base-type="float"> <qti-default-value> <qti-value>1</qti-value> </qti-default-value> </qti-outcome-declaration> <qti-item-body> <div class="qti-layout-row"> <div class="qti-layout-col12"> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> <img src="Image.jpg" alt="Image named 'Image.jpg'" width="200" height="113"/> <p>Wat is het juiste antwoord op deze vraag?</p> <qti-choice-interaction max-choices="1" min-choices="0" shuffle="false" response-identifier="RESPONSE"> <qti-simple-choice identifier="A"> <p>Zus</p> </qti-simple-choice> <qti-simple-choice identifier="B"> <p>Zo</p> </qti-simple-choice> </qti-choice-interaction> </div> </div> </qti-item-body> <qti-response-processing template="https://purl.imsglobal.org/spec/qti/v3p0/rptemplates/match_correct.xml"/> </qti-assessment-item>
  30. [TestMethod] public void ChoiceInteractionScoredDichotomous_ConvertToQti_ReturnsValidQtiResponseDeclarationWithoutMapping() { //Arrange var alternativeId = Guid.NewGuid();

    var item = GetItemWithBlock(new ChoiceInteraction() { ScorePolytomous = false, Solution = new List<Guid>() { alternativeId }, }); //Act var result = GetItemGenerator(item).GetQti(); //Assert var responseDeclaration = GetQtiElementOrDefault(result); responseDeclaration.Should().BeEquivalentTo(XElement.Parse($@" <qti-response-declaration identifier=""RESPONSE"" cardinality=""single"" base-type=""identifier"" xmlns=""http://www.imsglobal.org/xsd/imsqtiasi_v3p0""> <qti-correct-response> <qti-value>_{alternativeId}</qti-value> </qti-correct-response> </qti-response-declaration>")); }