Schrijf professionele eenheidstests in Python

Testen is de basis van solide softwareontwikkeling. Er zijn veel soorten testen, maar het belangrijkste type is unit testing. Eenheidstesten geven u veel vertrouwen dat u goed geteste stukken als primitieven kunt gebruiken en erop kunt vertrouwen wanneer u ze samenstelt om uw programma te maken. Ze vergroten uw voorraad vertrouwde code buiten de ingebouwde en standaardbibliotheek van uw taal. Bovendien biedt Python uitstekende ondersteuning bij het schrijven van eenheidsbepalingen.

Lopend voorbeeld

Voordat we in alle principes, heuristieken en richtlijnen duiken, laten we een representatieve eenheids test in actie zien. De SelfDrivingCar Klasse is een gedeeltelijke implementatie van de drijvende logica van een zelfrijdende auto. Het gaat meestal over het regelen van de snelheid van de auto. Hij is op de hoogte van objecten ervoor, de snelheidslimiet en of deze al dan niet op zijn bestemming is aangekomen. 

class SelfDrivingCar (object): def __init __ (self): self.speed = 0 self.destination = None def _accelerate (self): self.speed + = 1 def _decelerate (self): if self.speed> 0: self.speed - = 1 def _advance_to_destination (self): distance = self._calculate_distance_to_object_in_front () if distance < 10: self.stop() elif distance < self.speed / 2: self._decelerate() elif self.speed < self._get_speed_limit(): self._accelerate() def _has_arrived(self): pass def _calculate_distance_to_object_in_front(self): pass def _get_speed_limit(self): pass def stop(self): self.speed = 0 def drive(self, destination): self.destination = destination while not self._has_arrived(): self._advance_to_destination() self.stop() def __init__(self): self.speed = 0 self.destination = None def _accelerate(self): self.speed += 1 def _decelerate(self): if self.speed > 0: self.speed - = 1 def _advance_to_destination (self): distance = self._calculate_distance_to_object_in_front () if distance < 10: self.stop() elif distance < self.speed / 2: self._decelerate() elif self.speed < self._get_speed_limit(): self._accelerate() def _has_arrived(self): pass def _calculate_distance_to_object_in_front(self): pass def _get_speed_limit(self): pass def stop(self): self.speed = 0 def drive(self, destination): self.destination = destination while not self._has_arrived(): self._advance_to_destination() self.stop() 

Hier is een eenheidscontrole voor de hou op() methode om je eetlust te wekken. Ik zal later in de details ingaan. 

van unittest importeren TestCase-klasse SelfDrivingCarTest (TestCase): def setUp (self): self.car = SelfDrivingCar () def test_stop (self): self.car.speed = 5 self.car.stop () # Controleer of de snelheid 0 is na stoppen self.assertEqual (0, self.car.speed) # Controleer of het Ok is om opnieuw te stoppen als de auto al is gestopt self.car.stop () self.assertEqual (0, self.car.speed)

Richtlijnen voor het testen van eenheden

plegen

Goede unit tests schrijven is hard werken. Het schrijven van unit-tests kost tijd. Wanneer u wijzigingen in uw code aanbrengt, moet u meestal ook uw tests wijzigen. Soms heb je fouten in je testcode. Dat betekent dat je echt toegewijd moet zijn. De voordelen zijn enorm, zelfs voor kleine projecten, maar ze zijn niet gratis.

Gedisciplineerd zijn

Je moet gedisciplineerd zijn. Wees consistent. Zorg ervoor dat de tests altijd slagen. Laat de tests niet verbroken worden omdat u "weet" dat de code in orde is.

Automatiseer

Om u te helpen gedisciplineerd te zijn, moet u uw unit-tests automatiseren. De tests moeten automatisch worden uitgevoerd op belangrijke punten zoals pre-commit of pre-deployment. In het ideale geval verwerpt uw ​​beheersysteem voor bronbesturing de code die niet alle tests heeft doorstaan.

Niet-geteste code is per definitie defect

Als je het niet hebt getest, kun je niet zeggen dat het werkt. Dit betekent dat je het als kapot moet beschouwen. Als het een kritieke code is, gebruik deze dan niet voor productie.

Achtergrond

Wat is een eenheid?

Een eenheid voor het testen van eenheden is een bestand / module met een reeks verwante functies of een klasse. Als u een bestand met meerdere klassen hebt, moet u een eenheidscontrole voor elke klas schrijven.

Naar TDD of Niet naar TDD

Testgestuurde ontwikkeling is een praktijk waarbij u de tests schrijft voordat u de code schrijft. Deze aanpak heeft verschillende voordelen, maar ik raad aan om dit te vermijden als je de discipline hebt om later de juiste tests te schrijven. 

De reden is dat ik ontwerp met code. Ik schrijf code, kijk ernaar, herschrijf het, bekijk het opnieuw en herschrijf het heel snel opnieuw. Het schrijven van tests beperkt me eerst en vertraagt ​​me. 

Zodra ik klaar ben met het eerste ontwerp, zal ik de tests onmiddellijk schrijven, voordat ik met de rest van het systeem kan integreren. Dat gezegd hebbende, het is een geweldige manier om jezelf voor te stellen aan unit-tests en het zorgt ervoor dat al je code tests zal hebben.

De Unittest-module

De unittest-module wordt geleverd met de standaardbibliotheek van Python. Het biedt een klasse genaamd testcase, waar je je klas van kunt afleiden. Dan kunt u een overschrijven opstelling() methode om vóór elke test een testopstelling te maken en / of een classSetUp () klassemethode om een ​​testopstelling voor alle tests voor te bereiden (niet teruggezet tussen individuele tests). Er zijn overeenkomstige scheuren() en classTearDown () methoden die u ook kunt overschrijven.

Hier zijn de relevante delen van onze SelfDrivingCarTest klasse. Ik gebruik alleen de opstelling() methode. Ik maak een vers SelfDrivingCar voorbeeld en sla het op self.car dus het is beschikbaar voor elke test.

van ongemachtigde import TestCase-klasse SelfDrivingCarTest (TestCase): def setUp (self): self.car = SelfDrivingCar ()

De volgende stap is om specifieke testmethoden te schrijven om die code tijdens de test te testen-de SelfDrivingCar klasse in dit geval - doet wat het moet doen. De structuur van een testmethode is vrij standaard:

  • De omgeving voorbereiden (optioneel).
  • Bereid het verwachte resultaat voor.
  • Bel de code die getest wordt.
  • Stel dat het werkelijke resultaat overeenkomt met het verwachte resultaat.

Merk op dat het resultaat niet de uitvoer van een methode hoeft te zijn. Het kan een statusverandering van een klasse zijn, een bijwerking zoals het toevoegen van een nieuwe rij in een database, het schrijven van een bestand of het verzenden van een e-mail.

Bijvoorbeeld de hou op() methode van de SelfDrivingCar class retourneert niets, maar verandert de interne status door de snelheid in te stellen op 0. De assertEqual () methode geboden door de testcase basisklasse wordt hier gebruikt om dat gesprek te verifiëren hou op() werkte zoals verwacht.

def test_stop (self): self.car.speed = 5 self.car.stop () # Controleer of de snelheid 0 is na het stoppen van self.assertEqual (0, self.car.speed) # Controleer of het Ok is om te stoppen als de auto is al gestopt self.car.stop () self.assertEqual (0, self.car.speed)

Er zijn hier eigenlijk twee tests. De eerste test is om ervoor te zorgen dat als de snelheid van de auto 5 is en hou op() wordt genoemd, wordt de snelheid 0. Dan is een andere test om ervoor te zorgen dat er niets misgaat bij het bellen hou op() opnieuw wanneer de auto al is gestopt.

Later zal ik nog een aantal tests introduceren voor extra functionaliteit.

De Doctest Module

De doctest-module is behoorlijk interessant. Hiermee kunt u interactieve codevoorbeelden in uw docstring gebruiken en de resultaten verifiëren, inclusief de verhoogde uitzonderingen. 

Ik gebruik of beveel doctest niet aan voor grootschalige systemen. Een goede unit-test vergt veel werk. De testcode is meestal veel groter dan de code die wordt getest. Docstrings zijn gewoon niet het juiste medium voor het schrijven van uitgebreide tests. Ze zijn echter cool. Hier is wat a factorial function with doc tests ziet eruit als:

import math def factorie (n): "" "Retourneer de faculteit van n, een exact geheel getal> = 0. Als het resultaat klein genoeg is om in een int te passen, retourneer dan een int. Anders retourneer een lange. >>> [faculteit (n) voor n binnen het bereik (6)] [1, 1, 2, 6, 24, 120] >>> [faculteit (lang (n)) voor n binnen het bereik (6)] [1, 1, 2, 6, 24, 120] >>> faculteit (30) 265252859812191058636308480000000L >>> faculteit (30L) 265252859812191058636308480000000L >>> faculteit (-1) Traceback (meest recente oproep laatste): ... Waarde Fout: n moet> = 0 zijn Facturen van drijvers OK, maar de float moet een exact geheel getal zijn: >>> faculteit (30.1) Traceback (meest recente oproep laatste): ... ValueError: n moet exact integer >>> faculteit (30.0) 265252859812191058636308480000000L Het mag ook niet belachelijk groot zijn : >>> faculteit (1e100) Traceback (meest recente oproep laatste): ... OverflowError: n te grote "" "if not n> = 0: verhoog ValueError (" n moet zijn> = 0 ") als math.floor (n )! = n: verhoog ValueError ("n moet exact geheel getal zijn") als n + 1 == n: # een waarde opneemt zoals 1e300 verhogen OverflowE rror ("n te groot") result = 1 factor = 2 while factor <= n: result *= factor factor += 1 return result if __name__ == "__main__": import doctest doctest.testmod()

Zoals u kunt zien, is de docstring veel groter dan de functiecode. Het bevordert de leesbaarheid niet.

Tests uitvoeren

OK. Je hebt je unit tests geschreven. Voor een groot systeem heb je tientallen / honderden / duizenden modules en klassen over mogelijk meerdere mappen. Hoe voer je al deze tests uit??

De unittest-module biedt verschillende faciliteiten om tests te groeperen en programmatisch uit te voeren. Bekijk Tests laden en uitvoeren. Maar de gemakkelijkste manier is testdetectie. Deze optie is alleen toegevoegd in Python 2.7. Pre-2.7 u zou neus kunnen gebruiken om tests te ontdekken en uit te voeren. Neus heeft een paar andere voordelen, zoals het uitvoeren van testfuncties zonder een klasse te hoeven maken voor uw testcases. Maar voor het doel van dit artikel, laten we het bij de ongeschoolden houden.

Om uw testen zonder opgave te vinden en uit te voeren, typt u eenvoudigweg op de opdrachtregel:

python -m unittest ontdekken

unittest zal alle bestanden en submappen scannen, alle gevonden tests uitvoeren en een mooi rapport en runtime leveren. Als u wilt zien welke tests het uitvoert, kunt u de vlag -v toevoegen:

python -m unittest discover -v

Er zijn verschillende vlaggen die de bewerking besturen:

python -m unittest -h Gebruik: python -m unittest [opties] [tests] Opties: -h, --help Dit bericht weergeven -v, --verbose Verbose output -q, --quiet Minimale uitvoer -f, - failfast Stop bij eerste mislukking -c, --catch Catch control-C en weergave resultaten -b, --buffer Buffer stdout en stderr tijdens testruns Voorbeelden: python -m unittest test_module - testruns van test_module python -m unittest module.TestClass - voer tests uit van module.TestClass python -m unittest module.Class.test_method - voer gespecificeerde testmethode uit [tests] kan een lijst zijn van een willekeurig aantal testmodules, klassen en testmethoden. Alternatief gebruik: python -m unittest discover [opties] Opties: -v, --verbose Uitgebreide uitvoer -f, --failfast Stop bij eerste fout -c, --catch Catch-controle-C en weergave resultaten -b, --buffer Buffer stdout en stderr tijdens testruns -s directory Directory om detectie te starten ('.' Standaard) -p patroon Patroon om testbestanden aan te passen ('test * .py' standaard) -t map Hoogste niveau map van project (standaard om directory te starten ) Voor testdetectie moeten alle testmodules geïmporteerd kunnen worden uit de hoofddirectory van het project.

Test dekking

Testverslag is een vaak verwaarloosd veld. Dekking betekent hoeveel van uw code daadwerkelijk is getest door uw tests. Als u bijvoorbeeld een functie hebt met een if-else verklaring en test u alleen de als tak, dan weet je niet of het anders branch werkt of niet. In het volgende codevoorbeeld, de functie toevoegen() controleert het type van zijn argumenten. Als beide gehele getallen zijn, worden deze gewoon toegevoegd. 

Als beide strings zijn, probeert deze te converteren naar gehele getallen en ze toe te voegen. Anders roept het een uitzondering op. De test_add () functie test de toevoegen() functie met argumenten die zowel gehele getallen als argumenten zijn die zwevend zijn en in beide gevallen het juiste gedrag verifiëren. Maar de testdekking is onvolledig. Het geval van stringargumenten is niet getest. Als resultaat slaagt de test met succes, maar de typfout in de branch waar de argumenten beide strings zijn, is niet ontdekt (zie de 'intg' daar?).

import unittest def add (a, b): "" "Deze functie voegt twee getallen a, b toe en geeft hun som a terug en b mag gehele getallen" "" als isinstance (a, int) en isinstance (b, int): retourneer een + b elseif isinstance (a, str) en isinstance (b, str): return int (a) + intg (b) else: verhoog Uitzondering ('Invalid arguments') class Test (unittest.TestCase): def test_add (self) : self.assertEqual (5, add (2, 3)) self.assertEqual (15, add (-6, 21)) self.assertRaises (Exception, add, 4.0, 5.0) unittest.main () 

Dit is de uitvoer:

---------------------------------------------------------------------- Ran 1-test in 0,000s OK Proces beëindigd met exitcode 0

Hands-on unittests

Het schrijven van testen met een industriële sterkte is niet eenvoudig of eenvoudig. Er zijn verschillende dingen om te overwegen en afwegingen te maken.

Ontwerp voor testbaarheid

Als je code formeel spaghetti-code of een grote bolletje modder is, waarbij verschillende niveaus van abstractie met elkaar worden gemengd en elk stuk code afhankelijk is van elk ander stuk code, kun je het moeilijk testen. Wanneer u iets wijzigt, moet u ook een aantal tests updaten.

Het goede nieuws is dat algemeen correct softwareontwerp precies is wat u nodig heeft voor testbaarheid. Met name goed gefundeerde modulaire code, waarbij elk onderdeel een duidelijke verantwoordelijkheid heeft en interageert met andere componenten via goed gedefinieerde interfaces, maakt het schrijven van goede eenheidscontroles een plezier.

Bijvoorbeeld onze SelfDrivingCar De klasse is verantwoordelijk voor de bediening op hoog niveau van de auto: starten, stoppen, navigeren. Het heeft een calculate_distance_to_object_in_front () methode die nog niet is geïmplementeerd. Deze functionaliteit moet waarschijnlijk worden geïmplementeerd door een volledig afzonderlijk subsysteem. Het kan het lezen van gegevens van verschillende sensoren omvatten, in wisselwerking staan ​​met andere zelfrijdende auto's, een hele machine vision-stack om afbeeldingen van meerdere camera's te analyseren.

Laten we kijken hoe dit in de praktijk werkt. De SelfDrivingCar accepteert een argument genaamd object_detector dat heeft een methode genaamd calculate_distance_to_object_in_front (), en het zal deze functionaliteit aan dit object delegeren. Nu is het niet nodig om dit te testen, omdat het object_detector is verantwoordelijk (en moet getest worden) ervoor. Je wilt nog steeds een eenheid testen of je de object_detector naar behoren.

class SelfDrivingCar (object): def __init __ (self, object_detector): self.object_detector self.speed = 0 self.destination = Geen def _calculate_distance_to_object_in_front (self): return self.object_detector.calculate_distance_to_object_in_front ()

Kosten / Baten

De hoeveelheid moeite die u in het testen steekt, moet worden gecorreleerd met de kosten van het falen, hoe stabiel de code is en hoe gemakkelijk het is om te repareren als problemen langs de lijn worden gedetecteerd..

Onze zelfrijdende autoklasse is bijvoorbeeld super kritisch. Als het hou op() methode werkt niet goed, onze zelfrijdende auto kan mensen doden, eigendommen vernietigen en de hele markt van zelfrijdende auto's laten ontsporen. Als u een zelfrijdende auto ontwikkelt, vermoed ik dat uw unit tests uitvoert voor de hou op() methode zal iets rigoureuzer zijn dan de mijne. 

Aan de andere kant, als een enkele knop in uw webtoepassing op een pagina die drie niveaus onder uw hoofdpagina begraven is een beetje flikkert wanneer iemand erop klikt, kunt u deze mogelijk repareren, maar waarschijnlijk zal u geen speciale unit-test voor deze case toevoegen. De economie rechtvaardigt het gewoon niet. 

Mindset testen

Het testen van mindset is belangrijk. Eén principe dat ik gebruik, is dat elk stukje code ten minste twee gebruikers heeft: de andere code die het gebruikt en de test die het test. Deze eenvoudige regel helpt veel met ontwerp en afhankelijkheden. Als je onthoudt dat je een test voor je code moet schrijven, voeg je niet veel afhankelijkheden toe die moeilijk te reconstrueren zijn tijdens het testen..

Stel dat uw code iets moet berekenen. Om dat te doen, moet het enkele gegevens uit een database laden, een configuratiebestand lezen en dynamisch een aantal REST API raadplegen voor actuele informatie. Dit kan allemaal om verschillende redenen nodig zijn, maar als je dat alles in een enkele functie plaatst, wordt het vrij moeilijk om een ​​eenheidstest uit te voeren. Het is nog steeds mogelijk met spot, maar het is veel beter om je code goed te structureren.

Pure functies

De eenvoudigste code om te testen is pure functies. Pure functies zijn functies die alleen toegang hebben tot de waarden van hun parameters, geen bijwerkingen hebben en hetzelfde resultaat teruggeven wanneer ze met dezelfde argumenten worden aangeroepen. Ze veranderen de status van uw programma niet, hebben geen toegang tot het bestandssysteem of het netwerk. Hun voordelen zijn te veel om hier te tellen. 

Waarom zijn ze gemakkelijk te testen? Omdat het niet nodig is om een ​​speciale omgeving in te stellen om te testen. Je geeft gewoon argumenten door en test het resultaat. U weet ook dat zolang de te testen code niet verandert, uw test niet hoeft te veranderen. 

Vergelijk het met een functie die een XML-configuratiebestand leest. Uw test moet een XML-bestand maken en de bestandsnaam doorgeven aan de code die wordt getest. Geen probleem. Maar stel dat iemand heeft besloten dat XML afschuwelijk is en dat alle configuratiebestanden zich in JSON moeten bevinden. Ze gaan aan de slag en converteren alle configuratiebestanden naar JSON. Ze voeren alle tests uit inclusief uw tests en zij allemaal voorbij lopen! 

Waarom? Omdat de code niet is veranderd. Het verwacht nog steeds een XML-configuratiebestand en uw test maakt er nog steeds een XML-bestand voor. Maar in productie krijgt uw code een JSON-bestand, dat niet kan worden ontleed.

Testen van foutafhandeling

Foutafhandeling is iets anders dat cruciaal is om te testen. Het maakt ook deel uit van het ontwerp. Wie is verantwoordelijk voor de juistheid van input? Elke functie en methode moet er duidelijk over zijn. Als het de verantwoordelijkheid van de functie is, moet het de invoer verifiëren, maar als het de verantwoordelijkheid van de beller is, kan de functie gewoon zijn gang gaan en aannemen dat de invoer juist is. De algehele correctheid van het systeem wordt gewaarborgd door tests te laten uitvoeren door de beller om te controleren of deze alleen de correcte invoer naar uw functie doorgeeft.

Meestal wilt u de invoer van de openbare interface naar uw code verifiëren, omdat u niet noodzakelijk weet wie uw code gaat bellen. Laten we naar de rijden() methode van de zelfrijdende auto. Deze methode verwacht een 'bestemming'-parameter. De parameter 'bestemming' wordt later in de navigatie gebruikt, maar de schijfmethode doet niets om te controleren of deze correct is. 

Laten we aannemen dat de bestemming een tupel van lengte- en breedtegraad zou moeten zijn. Er zijn allerlei soorten tests die kunnen worden uitgevoerd om te controleren of deze geldig is (bijvoorbeeld de bestemming in het midden van de zee). Laten we er voor onze doeleinden alleen voor zorgen dat het een tupel van drijvers is in het bereik van 0,0 tot 90,0 voor breedtegraad en -180,0 tot 180,0 voor lengtegraad.

Hier is de bijgewerkte SelfDrivingCar klasse. Ik heb een aantal van de niet-geïmplementeerde methoden triviaal geïmplementeerd, omdat het rijden() methode noemt sommige van deze methoden direct of indirect.

class SelfDrivingCar (object): def __init __ (self, object_detector): self.object_detector = object_detector self.speed = 0 self.destination = Geen def _accelerate (self): self.speed + = 1 def _decelerate (self): if self. snelheid> 0: self.speed - = 1 def _advance_to_destination (self): distance = self._calculate_distance_to_object_in_front () if distance < 10: self.stop() elif distance < self.speed / 2: self._decelerate() elif self.speed < self._get_speed_limit(): self._accelerate() def _has_arrived(self): return True def _calculate_distance_to_object_in_front(self): return self.object_detector.calculate_distance_to_object_in_front() def _get_speed_limit(self): return 65 def stop(self): self.speed = 0 def drive(self, destination): self.destination = destination while not self._has_arrived(): self._advance_to_destination() self.stop()

Om foutafhandeling in de test te testen, zal ik ongeldige argumenten doorgeven en verifiëren dat ze correct zijn afgewezen. U kunt dit doen met behulp van de self.assertRaises () methode van unittest.TestCase. Deze methode slaagt als de geteste code inderdaad een uitzondering oplevert.

Laten we het in actie zien. De testrit() methode passeert breedtegraad en lengtegraad buiten het geldige bereik en verwacht de rijden() methode om een ​​uitzondering te maken.

van unittest importeren TestCase van self_driving_car importeren SelfDrivingCar-klasse MockObjectDetector (object): def calculation_distance_to_object_in_front (self): return 20 class SelfDrivingCarTest (TestCase): def setUp (self): self.car = SelfDrivingCar (MockObjectDetector ()) def test_stop (self): self.car.speed = 5 self.car.stop () # Controleer of de snelheid 0 is na het stoppen van het zelf .assertEqual (0, self.car.speed) # Controleer of het OK is om opnieuw te stoppen als de auto al zelf gestopt is. auto.stop () self.assertEqual (0, self.car.speed) def test_drive (self): # Geldige bestemming self.car.drive ((55.0, 66.0)) # Ongeldige bestemming verkeerd bereik self.assertRaises (Uitzondering, zelf .car.drive, (-55.0, 200.0))

De test mislukt, omdat de rijden() methode controleert zijn argumenten niet op geldigheid en roept geen uitzondering op. Je krijgt een mooi rapport met volledige informatie over wat is mislukt, waar en waarom.

python -m unittest discover -v test_drive (untitled.test_self_driving_car.SelfDrivingCarTest) ... FAIL test_stop (untitled.test_self_driving_car.SelfDrivingCarTest) ... ok ======================= ================ FAIL: test_drive (untitled.test_self_driving_car.SelfDrivingCarTest) ------------------------------------------- --------------------------- Traceback (laatste oproep laatste): Bestand "/Users/gigi/PycharmProjects/untitled/test_self_driving_car.py" , regel 29, in test_drive self.assertRaises (Exception, self.car.drive, (-55.0, 200.0)) AssertionError: uitzondering niet verhoogd -------------------- -------------------------------------------------- Ran 2-tests in 0,000s FAILED (mislukkingen = 1)

Om het te verhelpen, gaan we het updaten rijden() methode om het bereik van zijn argumenten daadwerkelijk te controleren:

def drive (self, destination): lat, lon = destination if not (0.0 <= lat <= 90.0): raise Exception('Latitude out of range') if not (-180.0 <= lon <= 180.0): raise Exception('Latitude out of range') self.destination = destination while not self._has_arrived(): self._advance_to_destination() self.stop()

Nu gaan alle tests voorbij.

python -m unittest discover -v test_drive (untitled.test_self_driving_car.SelfDrivingCarTest) ... ok test_stop (untitled.test_self_driving_car.SelfDrivingCarTest) ... ok ----------------------- ----------------------------------------------- Ran 2-testen in 0,000s OK 

Privémethoden testen

Moet u elke functie en methode testen? Moet u in het bijzonder privémethoden testen die alleen door uw code worden aangeroepen? Het typisch onbevredigende antwoord is: "Het hangt ervan af". 

Ik zal proberen hier handig te zijn en je vertellen waar het van afhangt. U weet precies wie uw privémethode belt - het is uw eigen code. Als uw tests voor de openbare methoden die uw privémethode aanroepen uitgebreid zijn, test u uw privémethoden al uitputtend. Maar als een privémethode erg gecompliceerd is, wilt u deze misschien onafhankelijk testen. Gebruik je oordeel.

Hoe u uw apparaattests organiseert

In een groot systeem is het niet altijd duidelijk hoe u uw tests moet organiseren. Moet u één groot bestand hebben met alle tests voor een pakket of één testbestand voor elke klas? Mochten de tests zich in hetzelfde bestand bevinden als de te testen code of in dezelfde map?

Dit is het systeem dat ik gebruik. Tests moeten volledig los staan ​​van de code die wordt getest (vandaar dat ik geen doctest gebruik). Idealiter moet uw code in een pakket zitten. De tests voor elk pakket moeten in een sibling-map van uw pakket staan. In de testdirectory moet er één bestand zijn voor elke module van uw pakket genaamd test_

Als u bijvoorbeeld drie modules in uw pakket hebt: module_1.py, module_2.py en module_3.py, je zou drie testbestanden moeten hebben: test_module_1.py, test_module_2.py en test_module_3.py onder de testdirectory. 

Deze conventie heeft verschillende voordelen. Het maakt het duidelijk door gewoon door mappen te bladeren die je niet vergeten bent om een ​​module volledig te testen. Het helpt ook om de tests in redelijk grote brokken te organiseren. Ervan uitgaande dat uw modules redelijk groot zijn, zal de testcode voor elke module in een eigen bestand staan, dat een beetje groter kan zijn dan de module die getest wordt, maar toch iets dat comfortabel in één bestand past.. 

Conclusie

Eenheidstests vormen de basis van solide code. In deze zelfstudie heb ik enkele principes en richtlijnen voor het testen van eenheden onderzocht en de redenering achter verschillende beste werkwijzen toegelicht. Hoe groter het systeem dat u aan het bouwen bent, des te belangrijker unit tests worden. Maar unit tests zijn niet genoeg. Andere soorten tests zijn ook nodig voor grootschalige systemen: integratietests, prestatietests, belastingtests, penetratietests, acceptatietests, enz..