[[TOC(heading=Unit tests)]] [[TOC(heading=Omgeving, sectionindex, compact, depth=3, allactive, Documentatie/Ontwikkelaar/Omgeving/)]] [[TOC(heading=Ontwikkelaar, sectionindex, compact, depth=2, allactive, Documentatie/Ontwikkelaar/)]] [[TOC(heading=Documentatie, sectionindex, compact, depth=1, allactive, Documentatie/)]] = Unit tests = == OpenAC 3 == === .NET / xUnit === Run tests met {{{npm test}}} vanaf de command line in de {{{ClientApp}}} directory. == OpenAC 2 / Python == OpenAC heeft een grote verzameling ''unit tests'' om de functionaliteit van het programma te testen. Hierbij wordt vooral de algoritmische- en data-kant van het programma getest. Er wordt niet stilgestaan bij de user interface (op enkele puntjes na). === Unit Tests Uitvoeren === In de {{{development/}}} branch en releases vanaf versie 2.000 staat er in de bovenste directory van OpenAC een file {{{unittests.py}}}. Dit is het "aandrijf" script van de unittests voor het gehele programma. Het kan worden opgestart vanaf de command-line als ieder ander Python programma: {{{ Python27\python.exe unittests.py }}} Zonder argumenten, of wanneer je {{{--help}}} toevoegt, legt het script uit welke argumenten het accepteert. Meestal ben je geinteresseerd in alle unittests voor OpenAC, toegepast op je eigen adaptatie. Dan gebruik je {{{ Python27\python.exe unittests.py --enable-all -a }}} Vul voor {{{}}} de naam van je adaptatie in, zoals "amsterdam" of "drenthe" ("ac_amsterdam" mag ook). De unit tests produceren heel veel uitvoer met aan het eind een overzicht van hoeveel tests er zijn uitgevoerd, hoeveel er mis zijn gegaan. Dat ziet er zo uit: {{{ Ran 352 tests in 55.550s FAILED (failures=15, errors=3) }}} Er zit een handvol verwachte failures in de unittests, waar OpenAC dingen niet doet zoals we het verwachten (in bijzondere randgevallen). Errors geven aan dat er een probleem is in de OpenAC code die niet goed wordt afgevangen. Errors in de unittests kunnen direct als defect worden gerapporteerd in het [/newticket tickets systeem]. ==== Tests Selecteren ==== De tests van de adaptaties, desktop-laag, includes, kern en logica zijn allemaal afzonderlijk te selecteren met de opties `--with-*` (bijvoorbeeld `--with-include`). Hiermee zijn de grote groepen tests aan of uit te zetten. Met `--enable-all` worden alle tests aangezet. Als `--with-adaptaties` aan staat, dan kan de test worden beperkt tot de tests van specifieke adaptaties met de optie `-a` (of `--adaptaties`). Geef een lijst van adaptatie-namen op, gescheiden door komma's. Als voorbeeld met de tests van drie verschillende AC's: {{{ Python27\python.exe unittests.py --with-adaptaties -a amc,amsterdam,vumc }}} Als `--with-kern` aanstaat, dan kan de test worden beperkt ror specifieke kern-modules. De optie `-m` (of `--modules`) kan een komma-gescheiden lijst van module-namen gebruiken. Alleen die kern-modules met een naam die lijkt op de opgegeven lijst worden gebruikt. De namen worden gezocht in alle kernmodules, dus "grouper" selecteert de unit tests van alle kernmodules waar het woord "grouper" in de naam voorkomt (dat is er maar een, ''e080_grouper''), maar als je {{{-m hl7,dos}}} gebruikt krijg je de tests van zeven modules). {{{ Python27\python.exe unittests.py --with-kern -m grouper,easidata }}} Om de tests uit ''een enkele test-klasse'' te draaien, gebruik de optie `it` (of `--tests`). Dit is vooral interessant bij het maken van tests voor een module, als je niet de hele tijd alle tests wilt draaien maar je wilt concentreren op de nieuwe tests. Geef een komma-gescheiden lijst van test-klassen op. Alleen die klassen die voorkomen in de lijst worden uitgevoerd (de andere selecties blijven van kracht, dus om alleen de test-klasse ''!TestPermissies'' van de kern-module ''systeem'' uit te voeren, moet je zowel `-m` als `-t` opgeven, anders krijg je de ''!TestPermissies'' test-klasse van alle modles). {{{ Python27\python.exe unittests.py --with-kern -m systeem -t TestPermissies }}} === Unit Tests Schrijven === Unit tests worden traditioneel opgeslagen in een bestand {{{unittests.py}}} in de desbetreffende directory. We streven ernaar om alle kernmodules een pakket unit tests te geven. Daarnaast hebben de directories {{{include/}}} en {{{logica/}}} tests '''en''' de adaptaties kunnen hun eigen unit tests hebben om lokale functionaliteit te testen. De makkelijkste manier om een test te schijven is om een bestaand voorbeeld te volgen. Elk unit test bestand heeft de volgende structuur: {{{ import unittest from include import testhelpers class MijnTest(testhelpers.TestCaseMetLogin): def test_10_iets(self): def suite(): return testhelpers.create_suite(globals()) }}} Een test is een functie in een test-klasse. Een unit test kan meerdere test-klassen bevatten, en een klasse kan meerdere tests bevatten. De klassen en functies zijn vooral een manier om de tests te organiseren en te structureren. De tests worden uiteindelijk in alfabetische volgorde binnen elke klasse uitgevoerd. Om de volgorde af te dwingen wordt meestal een tweecijferig volgnummer gebruikt, zoals `test_10_iets`. Traditioneel zijn ook `test_00_import` die alleen de te-testen modules importeert en controleert op aanwezigheid, en `test_99_cleanup` die eventuele test-data opruimt. Als alle test-functies de conventies volgen, wordt de cleanup dan als laatste uitgevoerd. In elke `unittests.py` module verwachten we een functie `suite()` die de tests van de module teruggeeft. Zie de documentatie van `include.testhelpers.create_suite()` voor uitleg hoe die de tests aanmaakt. Voor volledige documentatie zie [http://docs.python.org/library/unittest.html de Python documentatie voor de unittest module]. === Unit Test Coverage === In principe zouden de unittests 100% van de code-base testen. In de praktijk gaat dat niet lukken, want je hebt altijd wat platform-specifieke code die op je test-platform niet uitgevoerd gaat worden. Maar we streven, langzaam, naar volledige test-coverage. Test-coverage kan je meten met het `coverage` tool, dat je via easy-install of een andere Python installatiebron kunt verkrijgen. Voorlopig kijken we vooral naar de test-coverage in de `include/` directory. Met dit eerste commando voer je de unittests voor de includes uit, waarbij je de coverage voor de directory include gaat meten (zonder `--source` gaat 'ie '''alles''' meten, dat is langzaam en niet zo interessant te weten dat de OpenAC tests niet alles in SQLAlchemy raken). Het tweede commando drukt informatie af over wat er allemaal geraakt is, en vooran (`-m`) welke regels code niet zijn uitgevoerd (dus ook niet getest). {{{ coverage run --source include unittests.py --with-include coverage report -m }}} Uitvoer van coverage in deze vorm geeft je een filenaam, dekkingsgraad, en een lijst van gemiste regels: {{{ include/encoding 56 6 89% 35-42, 47-48 include/files 84 42 50% 29-46, 55-63, 92-94, 107-128, 165, 182-196, 226-232 }}} De dekking in de file `encoding.py` is behoorlijk goed, en een korte inspectie leert dat de gemiste regels 35-42, 47 en 48 allemaal Windows- of Mac-specifiek zijn (en deze test is gedraaid onder Linux). Bij `files.py` daarentegen is de dekking niet zo goed. Regels 29-46 betreffen de functie `homedir()`, die dus (in development r24152) helemaal niet gebruikt of getest wordt. Homepage van [http://nedbatchelder.com/code/coverage/ coverage]. Voor een standaard-uitvoering van coverage, gebruik het (shell) script `bin/run-coverage.sh` - `run-coverage.sh -a` Draait alle unittests, rapporteert over de hele codebase. Dit duurt nogal lang, maar geeft een totaalbeeld van welke code er is en wat er getest wordt. - `run-coverage -m ` Draait de unittests van een gegeven kernmodule (zoals ''d010_systeem'') en rapporteert de coverage van die specifieke module. - `run-coverage` Zonder argumenten worden de bepaalde unittests van include gedraaid en de coverage van de bijbehorende source-files weergegeven. De coverage-gegevens blijven staan na `run-coverage.sh`, dus je kunt met `coverage report -m` en dergelijke daarna de gegevens nogmaals raadplegen of filteren.