Pakketten, klassendiagrammen en unittests in Java

Leer hoe je een groter Java-programma zo indeelt dat het begrijpelijk blijft en gemakkelijk te wijzigen en te controleren is. Groepeer klassen in pakketten met imports en de juiste toegangsniveaus, schets de structuur in UML-klassendiagrammen en scheid de gebruikersinterface, de programmalogica en de gegevensopslag in lagen. Controleer daarna de logica met JUnit-tests, schrijf tests voordat je de code schrijft en laat Maven of Gradle het hele project met één opdracht compileren, testen en verpakken.

BeginnerLes 10 van 11 in Cursus Java programmeren: klassen, overerving, collecties en streams

Wat je leert

  1. Groepeer verwante klassen in pakketten, sla elk bronbestand op waar de compiler en buildtools het verwachten en gebruik klassen uit andere pakketten met imports en het juiste toegangsniveau.
    • Declareer pakketten en laat de mappenstructuur daarop aansluiten
      Schrijf een pakketdeclaratie, kies een gebruikelijke pakketnaam, sla het bestand op in de bijbehorende map en verwijs naar klassen met hun volledig gekwalificeerde naam.
    • Gebruik klassen uit andere pakketten met imports
      Importeer afzonderlijke klassen of hele pakketten, weet voor welke klassen geen import nodig is, los conflicten tussen klassen met dezelfde naam op en gebruik statische imports.
    • Regel de toegang tussen pakketten
      Kies tussen public, protected, pakkettoegang en private, zodat elk pakket alleen beschikbaar maakt wat andere pakketten nodig hebben.
  2. Lees en teken klassendiagrammen die klassen, hun leden en de relaties ertussen tonen en vertaal in beide richtingen tussen een diagram en Java-code.
    • Teken een afzonderlijke klasse en lees de weergave ervan
      Toon de naam, attributen en operaties van een klasse met typen en zichtbaarheid en markeer statische leden, abstracte klassen en interfaces.
    • Geef associaties en multipliciteit weer
      Verbind klassen met associaties, lees en schrijf multipliciteiten en navigeerbaarheid en onderscheid compositie, aggregatie en afhankelijkheid.
    • Modelleer overerving en vertaal diagrammen naar code en omgekeerd
      Toon overerving en implementatie van interfaces, schrijf Java-code op basis van een diagram en teken een diagram op basis van bestaande code.
  3. Verdeel een programma zo dat de gebruikersinterface, de applicatielogica en de gegevensopslag in afzonderlijke klassen en pakketten staan, met afhankelijkheden die in één richting wijzen.
    • Scheid de gebruikersinterface van de programmalogica
      Houd het lezen van invoer en weergeven van uitvoer in een gebruikersinterfaceklasse, bewaar de regels van het programma in logicaklassen en verbind beide in de main-methode.
    • Orden lagen, pakketten en afhankelijkheden
      Deel klassen in een presentatielaag, een logicalaag en een gegevenstoegangslaag in, laat afhankelijkheden naar beneden wijzen en verbind de lagen via interfaces.
  4. Schrijf automatische unittests met JUnit, kies testgevallen die fouten aan het licht brengen, houd tests onafhankelijk en leesbaar en schrijf tests voordat je nieuwe code ontwikkelt.
    • Schrijf en voer je eerste JUnit-test uit
      Maak een testklasse, schrijf testmethoden met asserties, voer ze uit en lees het resultaat van een geslaagde en een mislukte test.
    • Kies testgevallen die fouten opsporen
      Kies gebruikelijke invoer, grenswaarden en ongeldige invoer, test verwachte excepties en geef elke test één duidelijk doel en een duidelijke naam.
    • Houd tests onafhankelijk en onderhoudbaar
      Maak vóór elke test nieuwe testobjecten, vermijd gedeelde toestand, voer één test uit met meerdere invoerwaarden en test de logica afzonderlijk van invoer en uitvoer.
    • Ontwikkel code met TDD door eerst tests te schrijven
      Volg vanuit een testlijst de cyclus rood–groen–refactoren, neem kleine stappen en weet wat testgestuurde ontwikkeling wel en niet belooft.
  5. Gebruik een buildtool om bibliotheken te beheren, volg de standaardprojectindeling en compileer, test en verpak een programma met één opdracht.
    • Zet een Maven-project op en bouw het
      Beschrijf een project in pom.xml met coördinaten en afhankelijkheden, volg de standaardmapindeling en doorloop de buildlevenscyclus.
    • Zet een Gradle-project op en herstel buildfouten
      Schrijf het equivalente Gradle-buildscript, voer JUnit-tests uit op het JUnit Platform, vergelijk de twee tools en zoek de oorzaak van een mislukte build.

Klassendiagrammen en unittests in Java: uitleg

5 hoofdstukken

1

Deel een Java-programma in pakketten in

Naarmate een programma groeit, hebben klassenamen en mappen een structuur nodig die zowel mensen als Java kunnen volgen. Pakketten groeperen verwante typen en bepalen toegangsgrenzen. Imports maken die typen gemakkelijk te gebruiken zonder hun interne werking open te stellen. Je verbindt een declaratie met het bronpad, lost namen tussen pakketten op en bepaalt welke klassen een ander deel van het programma mag gebruiken en welke constructoren en methoden het mag aanroepen.

1.1 Pakketnamen en bronmappen

Een pakket groepeert verwante klassen en interfaces onder één naam. Het scheidt ook namen: twee klassen met de naam Parcel kunnen naast elkaar bestaan als hun pakketnamen verschillen. Een pakket biedt Java zowel een toegangsgrens als een manier om bestanden te ordenen.

Declareer het pakket eerst

Een gewoon bronbestand begint met de pakketdeclaratie, gevolgd door imports en daarna de klasse. Vóór de declaratie mag commentaar staan. Bijvoorbeeld:

package com.example.parcels.domain;

public class Parcel {
    private int weightGrams;

    public Parcel(int weightGrams) {
        this.weightGrams = weightGrams;
    }
}

Elk type op het hoogste niveau in dat bestand hoort bij com.example.parcels.domain. Een bestand heeft hoogstens één pakketdeclaratie. De publieke klasse Parcel hoort in Parcel.java; alleen de map wijzigen verandert het gedeclareerde pakket niet.

Zet punten om in mappen

De broncodemap is de map waar het pakketpad begint. In een standaard Maven- of Gradle-project begint de Java-hoofdcode bij src/main/java. Vervang elke punt in de pakketnaam door een schuine streep en voeg daarna de klassenaam toe:

src/main/java/
  com/example/parcels/domain/Parcel.java

Het pakket is com.example.parcels.domain, niet src.main.java.com.example.parcels.domain. De eerste drie mappen beschrijven de buildindeling; ze maken geen deel uit van de Java-naam. Als een IDE meldt dat een pakket niet overeenkomt, vergelijk je de declaratie met het pad onder de broncodemap. Verplaats het bestand of corrigeer de declaratie zodat ze overeenkomen.

Pakketnamen gebruiken kleine letters. Organisaties draaien vaak hun domein om: example.com wordt com.example, gevolgd door het project en het onderdeel ervan. Een project zonder domein kan parcels.domain gebruiken. Als een omgekeerd domeindeel een koppelteken bevat, vervang je dat door een liggend streepje, zoals in org.example.hyphenated_name. Vermijd de naamruimte java en javax van het platform.

Gebruik de volledige klassenaam

Een volledig gekwalificeerde naam voegt de pakketnaam en de eenvoudige klassenaam samen: com.example.parcels.domain.Parcel. Die identificeert de klasse ook als een ander pakket eveneens een Parcel heeft. Code kan een toegankelijke klasse zonder import voluit noemen:

java.util.ArrayList<String> names = new java.util.ArrayList<>();

Om een main-klasse in een pakket uit te voeren, geef je de volledige naam en een klassenpad dat naar de hoofdmap van de gecompileerde pakketboom wijst. Vanuit de projectmap:

javac -d out src/main/java/com/example/parcels/Main.java src/main/java/com/example/parcels/domain/Parcel.java
java -cp out com.example.parcels.Main

Hier maakt javac -d out de uitvoerboom en zoekt java -cp out vanaf de hoofdmap van die boom. Als je java Main vanuit een pakketmap start, ontbreekt het pakketdeel van de identiteit van de klasse. Buildtools en IDE’s beheren deze opdrachten normaal gesproken.

Onderscheid naburige pakketten

com.example.parcels en com.example.parcels.domain zijn afzonderlijke pakketten. Een gedeeld begin van de naam geeft geen extra toegangsrechten. Een bestand zonder declaratie hoort bij het naamloze pakket. Benoemde pakketten kunnen de klassen daaruit niet importeren, dus gebruik benoemde pakketten in een programma met meerdere onderdelen.

Belangrijkste punten

  • Plaats de pakketdeclaratie vóór imports en de klasse.
  • Laat het pakketpad onder de broncodemap overeenkomen met de pakketnaam; neem src/main/java niet op in de pakketnaam.
  • Gebruik kleine letters voor pakketnamen en gebruik waar nodig de volledig gekwalificeerde klassenaam.
  • Een gedeeld begin van pakketnamen geeft geen gedeelde pakkettoegang.
  • Voer klassen in pakketten uit vanaf de hoofdmap van de gecompileerde boom met hun volledig gekwalificeerde namen.

1.2 Imports en conflicten tussen klassenamen

Met een import kan één bronbestand de eenvoudige naam van een toegankelijk type gebruiken. Een import vertelt de compiler wat de naam betekent. Een import kopieert de klasse niet, downloadt geen bibliotheek en maakt een verborgen klasse niet public.

Lees het begin van een bronbestand

De gebruikersinterface hoort bij één pakket en gebruikt klassen uit twee andere:

package com.example.parcels.ui;

import com.example.parcels.domain.Parcel;
import com.example.parcels.domain.ShippingCalculator;
import java.util.Scanner;

public class TextUI {
    private Scanner scanner;
    private ShippingCalculator calculator;
}

Parcel en ShippingCalculator komen uit het domeinpakket van het programma; Scanner komt uit de Java-bibliotheek. Elke import geldt alleen voor dit bestand. Een ander bestand dat Scanner gebruikt, heeft een eigen import of de volledige naam java.util.Scanner nodig.

Kies één type of een joker

import java.util.Scanner; is een import van één type. De exacte klasse is zichtbaar voor de lezer. import java.util.*; is een jokerimport: toegankelijke typen in dat pakket kunnen bij gebruik met hun eenvoudige naam worden aangeduid. De joker laadt geen ongebruikte klassen in het programma en vertraagt het niet.

De joker stopt bij dat pakket. Hij omvat niet java.util.function.Function of java.util.stream.Collectors, die in afzonderlijke pakketten staan. Dezelfde regel geldt voor je eigen code: import com.example.parcels.*; bereikt com.example.parcels.domain.Parcel niet.

In drie gevallen is geen import nodig: voor typen in hetzelfde pakket, automatisch beschikbare typen in java.lang en typen die voluit worden genoemd. Daarom verschijnen String, System, Math, Integer en Object vaak zonder import.

Los een gedeelde eenvoudige naam op

Zowel java.util als java.awt heeft een klasse met de naam List. Twee imports van deze verschillende klassen als afzonderlijke typen veroorzaken een compilatiefout. Twee jokerimports kunnen naast elkaar bestaan, maar de eenvoudige naam List wordt bij gebruik dubbelzinnig. Een import van één type heeft voorrang op een jokerimport.

Als beide klassen nodig zijn, importeer je de ene en schrijf je de andere voluit:

import java.util.List;

class NameDisplay {
    private List<String> names;
    private java.awt.List widget;
}

Java-imports bieden geen aliassyntaxis zoals import ... as .... Met de volledig gekwalificeerde naam kun je het tweede type onderscheiden.

Importeer een statisch lid

Een statische import maakt de naam van de bijbehorende klasse overbodig bij het aanroepen van een statisch lid:

import static java.lang.Math.sqrt;

Het bestand kan nu sqrt(16.0) aanroepen. Testcode gebruikt vaak import static org.junit.jupiter.api.Assertions.assertEquals;, zodat asserties als directe controles lezen. Gebruik statische imports in gewone programmacode selectief, zodat lezers kunnen blijven zien waar operaties vandaan komen.

Volg de foutmelding

“Cannot find symbol” kan wijzen op een ontbrekende import of een verkeerd gespeld type. “Package ... does not exist” wijst op een probleem met de spelling van een pakket, de broncodemap of een afhankelijkheid. “Is not public ... cannot be accessed from outside package” betekent dat Java het type heeft gevonden, maar dat de toegang is geblokkeerd. Een extra import kan die toegangsfout niet oplossen; bekijk de klassedeclaratie en de leden die je nodig hebt.

Belangrijkste punten

  • Een import lost namen op in één bestand en verleent geen toegangsrechten.
  • Een joker omvat typen in één pakket, zonder de onderliggende pakketten.
  • Typen in hetzelfde pakket en typen uit java.lang hebben geen import nodig.
  • Gebruik één import van een type en één volledig gekwalificeerde naam als twee benodigde klassen dezelfde naam hebben.
  • Een statische import maakt een statisch lid aanroepbaar zonder de klassenaam.
  • Onderscheid een fout bij het oplossen van een naam van een toegangsfout.

1.3 Een kleine publieke interface

Een pakket moet de klassen en methoden beschikbaar maken die andere onderdelen nodig hebben en tegelijk implementatiedetails verborgen houden. Toegangsmodifiers maken die grens afdwingbaar. Een import kan een naam verkorten, maar kan niet voorbij een grens die de toegangsregels van Java afsluiten.

Kies de toegang voor een klasselid
ToegangWie kan het lid gebruiken?
publicCode die toegang heeft tot het declarerende type
protectedHetzelfde pakket, plus toegang vanuit subklassen in andere pakketten
Geen modifierPrecies hetzelfde pakket
privateDe declarerende klasse en haar geneste typen

Geen modifier betekent pakkettoegang voor gewone klasseleden en constructoren. Er is geen sleutelwoord default voor. Een klasse op het hoogste niveau heeft maar twee mogelijkheden: public of pakkettoegang. Een publieke klasse op het hoogste niveau wordt opgeslagen in een bestand met haar naam, zoals ShippingCalculator.java.

Controleer het type en de operatie

Stel dat de gebruikersinterface een pakket moet construeren. Een publieke klasse alleen is niet genoeg:

package com.example.parcels.domain;

public class Parcel {
    private int weightGrams;

    Parcel(int weightGrams) {
        this.weightGrams = weightGrams;
    }
}

De constructor heeft pakkettoegang. Code in com.example.parcels.ui kan Parcel noemen, maar kan new Parcel(1200) niet aanroepen. Maak die constructor public als het maken van pakketten deel uitmaakt van de publieke diensten van het pakket. Houd weightGrams private en bied een geschikte operatie aan in plaats van het veld open te stellen.

Bij pakkettoegang wordt de volledige pakketnaam vergeleken. Een pakket dat een bovenliggend pakket lijkt en een pakket dat een onderliggend pakket lijkt, zijn geen van beide hetzelfde pakket. Code in com.example.parcels krijgt geen speciale toegang tot hulpklassen in com.example.parcels.domain.

Begrijp beschermde toegang

protected geeft toegang vanuit niet-verwante klassen in het declarerende pakket en vanuit subklassen. Tussen pakketten verloopt de toegang tot een instantielid via de overervingscontext van de subklasse. Een subklasse kan haar eigen overgeërfde veld gebruiken, maar kan niet via een willekeurig superklasseobject de pakketgrens omzeilen.

Als Item bijvoorbeeld protected int weightGrams declareert, kan een Letter extends Item in een ander pakket this.weightGrams gebruiken. Binnen Letter staat een parameter met alleen het type Item geen item.weightGrams toe; een referentie met het type Letter staat die overgeërfde toegang wel toe. Deze beperking voorkomt dat de rechten van een subklasse algemene toegang tot elke Item worden.

Verberg hulpklassen bewust

Maak ShippingCalculator en feeInCents public als de gebruikersinterface die aanroept. Een ondersteunende klasse WeightBands kan pakkettoegang houden. Code buiten het domeinpakket is dan afhankelijk van het gedrag van de calculator en niet van de manier waarop die de tabel opslaat. Als je die hulpklasse vervangt, zijn elders minder wijzigingen nodig.

Tests kunnen dezelfde pakketnaam gebruiken als de klasse die ze testen, terwijl ze onder src/test/java staan. Dat geeft ze pakkettoegang zonder testcode tussen productiebestanden te plaatsen. Het geeft geen toegang tot private velden. Als je elk lid public maakt om een test te laten slagen, ontstaan op meer plaatsen afhankelijkheden van interne details; test in plaats daarvan publiek gedrag of een nuttige operatie op pakketniveau.

Belangrijkste punten

  • Klasseleden met pakkettoegang zijn alleen toegankelijk in precies hetzelfde pakket.
  • Een aanroeper heeft toegang nodig tot zowel een klasse als de constructor of methode die hij gebruikt.
  • Beschermde toegang omvat toegang binnen hetzelfde pakket en beperkte toegang vanuit subklassen in andere pakketten.
  • Houd velden private en maak alleen de operaties beschikbaar die een ander pakket nodig heeft.
2

Schets de programmastructuur met UML-klassendiagrammen

Een klassendiagram geeft je een compact overzicht van een programma voordat je de methodebody’s leest. Kaders beschrijven typen en operaties; lijnen beschrijven verbindingen, aantallen en subtyperelaties. Je leert die aanduidingen nauwkeurig lezen en vertalen naar Java-declaraties. Het pakketprogramma biedt voorbeelden van afzonderlijke klassen, collecties van geassocieerde objecten en onderling uitwisselbare repository-implementaties.

2.1 Wat een UML-klassekader vertelt

Een UML-klassendiagram toont de statische structuur: klassen, hun leden en relaties. Het helpt een lezer te zien welke soorten objecten bestaan en wat ze bieden. Het volgt niet de volgorde van methodeaanroepen tijdens de uitvoering.

Lees de drie vakken

Een klassekader heeft de klassenaam bovenaan, attributen in het midden en operaties onderaan. Een attribuut stelt meestal een Java-veld voor; een operatie stelt de zichtbare signatuur van een methode voor. Een schets kan vakken weglaten die niet bijdragen aan het doel.

Voor een pakket is een tekstuele weergave van het kader:

Parcel
-----------------------------
- weightGrams: int
- recipient: String
-----------------------------
+ Parcel(weightGrams: int, recipient: String)
+ getWeightGrams(): int
+ isHeavierThan(limitGrams: int): boolean

De constructor heeft de naam van de klasse en geen retourtype. De methode isHeavierThan accepteert een geheel getal met de naam limitGrams en geeft een booleaanse waarde terug.

Vertaal de volgorde van namen en typen

UML zet de naam vóór het type: weightGrams: int. Java zet het type eerst: int weightGrams. Een operatie volgt voor parameters hetzelfde patroon met eerst de naam en zet daarna het retourtype achter het sluithaakje.

private int weightGrams;

public boolean isHeavierThan(int limitGrams) {
    return weightGrams > limitGrams;
}

Deze code komt overeen met - weightGrams: int en + isHeavierThan(limitGrams: int): boolean. Een methodebody ontbreekt meestal in het diagram; de operatie vermeldt wat kan worden aangeroepen, niet het algoritme.

Behoud zichtbaarheid en speciale aanduidingen

De zichtbaarheidssymbolen zijn + public, - private, # protected en ~ pakkettoegang. Zet het symbool vóór de naam van het lid. Als een gegeven diagram een symbool weglaat, verzin dan geen zichtbaarheid; voeg het bedoelde symbool toe wanneer je code documenteert.

Een statisch lid is onderstreept. Het hoort bij de klasse en niet bij elk object. Een abstracte klasse of operatie wordt cursief geschreven of gemarkeerd met {abstract} wanneer cursief onhandig is. De abstracte markering betekent dat een subklasse ontbrekend gedrag moet leveren voordat een object van een concrete klasse kan worden gemaakt.

Een interfacekader draagt «interface» boven de naam. ParcelRepository kan bijvoorbeeld + save(parcel: Parcel): void en + findAll(): List<Parcel> aanbieden. Het kader vermeldt een contract; het kiest geen opslagimplementatie.

Kies de details die de lezer nodig heeft

Een planningsschets toont misschien alleen Parcel, Shipment en hun belangrijkste operaties. Een diagram voor het implementeren van een klasse heeft typen en zichtbaarheid nodig. Als je elke gebruikelijke getter vermeldt, kun je de relaties verbergen die het diagram moest tonen. Kies nuttige leden en houd ze in overeenstemming met de code: een privéveld in Java mag niet met een public +-symbool verschijnen en een statische methode moet worden onderstreept.

Belangrijkste punten

  • Een klassekader scheidt de naam, attributen en operaties.
  • UML gebruikt name: Type; Java gebruikt Type name.
  • Gebruik +, -, # en ~ voor public, private, protected en pakkettoegang.
  • Onderstreep statische leden; markeer abstracte elementen met cursief of {abstract}.
  • Een interfacekader toont «interface» en de operaties in het contract.

2.2 Lees de uiteinden van relatielijnen

Een associatie toont een structurele verbinding tussen objecten. In Java bewaart een object vaak een referentie naar een ander object in een veld. De lijn kan ook tonen hoeveel objecten zijn toegestaan en in welke richting de referentie kan worden gevolgd.

Tel vanaf het tegenoverliggende uiteinde

De multipliciteit staat naast de klasse waarvan die de objecten telt. Als 1..* naast Parcel staat en 1 naast Shipment, heeft één zending één of meer pakketten en hoort elk pakket bij precies één zending. Lees elk uiteinde afzonderlijk, beginnend bij één object aan het tegenoverliggende uiteinde.

MultipliciteitBetekenis
1Precies één
0..1Nul of één
* of 0..*Een willekeurig aantal, inclusief nul
1..*Minstens één
2..4Twee tot en met vier

Eén referentie kan 1 of 0..1 voorstellen; een collectie stelt een uiteinde met meerdere waarden voor. Een Java-List alleen dwingt geen ondergrens af. Een Shipment met de pakketregel 1..* moet een lege beginlijst afwijzen en voorkomen dat latere operaties de lijst leeg achterlaten. Evenzo heeft een verplichte enkele referentie een controle op null nodig.

Volg de referentie

Een open pijlpunt bij Parcel betekent dat een referentie van Shipment naar Parcel kan worden gevolgd. Een klassendiagram kan de navigeerbaarheid ongespecificeerd laten. Volgens de hier gebruikte onderwijsconventie markeren pijlen eenrichtingsreferenties; een lijn zonder aanduiding kan toegang in beide richtingen of een ongespecificeerde richting betekenen. Leid niet uitsluitend uit een pijl naar Parcel af dat er een veld terug bestaat.

Een rolnaam zoals parcels geeft aan wat de verbinding vanuit de zending betekent. Toon die referentie als associatie of als - parcels: List<Parcel> in het kader. Beide zonder reden tonen kan één veld op twee verschillende delen van de toestand laten lijken.

Kies de betekenis van de geheel-deelrelatie

Compositie gebruikt een gevulde ruit bij het geheel. Een deel hoort tegelijk bij hoogstens één samengesteld geheel en wordt normaal gesproken met dat geheel verwijderd, zoals een bestelling en de bijbehorende bestelregels. Aggregatie gebruikt een open ruit voor een lossere groepering waarvan de delen zelfstandig kunnen bestaan. Een team kan spelers groeperen zonder hun levensduur te bepalen.

Java heeft geen sleutelwoord voor een ruit en dwingt UML-eigenaarschap niet af alleen omdat er een lijst bestaat. Modelleer compositie alleen wanneer het ontwerp die eigenaarschapsregel bevat. Als het ontwerp alleen zegt dat het ene object het andere kent, is een gewone associatie duidelijker dan een ruit waarvoor geen onderbouwing bestaat.

Toon gebruik zonder eigenaarschap te beweren

Een afhankelijkheid is een onderbroken lijn met een open pijlpunt naar het type dat wordt gebruikt. Een calculator met feeInCents(Parcel parcel) is afhankelijk van Parcel, omdat de methodesignatuur dat type gebruikt. Een afhankelijkheid is een algemene gebruiksrelatie; die is niet beperkt tot code zonder velden. Een associatie geeft specifiekere structurele informatie wanneer een veld belangrijk is voor het model.

Vraag jezelf vóór het tekenen van een lijn af wat de code of eis vastlegt: een referentie, een toegestaan aantal, gebruik of eigenaarschap. Een collectie legt opslag van meerdere waarden vast, maar op zichzelf geen exclusief eigenaarschap of minimumaantal.

Belangrijkste punten

  • De multipliciteit naast een klasse telt haar objecten voor één object aan het andere uiteinde.
  • Een collectietype dwingt geen minimale of maximale multipliciteit af.
  • Een associatiepijl wijst naar het type dat vanuit de bron kan worden bereikt.
  • Plaats een compositie- of aggregatieruit aan de kant van het geheel.
  • Een afhankelijkheid is een onderbroken pijl voor gebruik; een associatie beschrijft een structurele verbinding.
  • Toon een referentie één keer, tenzij het diagram bewust beide weergaven nodig heeft.

2.3 Van een diagram naar Java-declaraties

Zowel overerving als de implementatie van een interface verbindt een specifiek type met een algemener type. UML gebruikt een open driehoek bij het algemene type, maar de lijnstijl maakt onderscheid tussen de twee relaties. Dat onderscheid vertelt je welke Java-declaratie je moet schrijven.

Lees de driehoek en de lijn

Een doorgetrokken lijn met een open driehoek bij Item betekent dat een subklasse zoals Letter de klasse Item uitbreidt. Dit is generalisatie. Een onderbroken lijn met een open driehoek bij ParcelRepository betekent dat een klasse zoals InMemoryParcelRepository die interface implementeert. Dit is realisatie.

De driehoek raakt altijd de superklasse of interface. Een onderbroken lijn met een open pijlpunt, zonder driehoek, betekent daarentegen een afhankelijkheid. De lijnstijl en de pijlpunt dragen samen de betekenis; een onderbroken lijn alleen is niet genoeg.

Bouw het skelet in een handige volgorde
  1. Maak elke klasse of interface in het bijbehorende pakket.
  2. Zet attributen om in velden en behoud namen, typen en zichtbaarheid.
  3. Zet navigeerbare associaties om in referentie- of collectievelden.
  4. Voeg extends of implements toe op basis van de generalisatie- of realisatielijnen.
  5. Zet operaties om in methodekoppen en vul daarna de body’s in.

Stel dat een interface save en findAll aanbiedt. Een implementatie heeft een associatie met meerdere Parcel-objecten met de rol parcels:

public class InMemoryParcelRepository implements ParcelRepository {
    private List<Parcel> parcels = new ArrayList<>();

    @Override
    public void save(Parcel parcel) {
        parcels.add(parcel);
    }

    @Override
    public List<Parcel> findAll() {
        return new ArrayList<>(parcels);
    }
}

De onderbroken lijn met driehoek levert implements ParcelRepository op. De associatie levert het lijstveld op. De operatiesignaturen leveren de twee publieke methoden op. De methodebody’s zijn een implementatiekeuze: hier voegt save een object achteraan toe en geeft findAll een nieuwe lijst terug, zodat aanroepers niet rechtstreeks elementen aan de opgeslagen lijst kunnen toevoegen of eruit kunnen verwijderen. De pakketobjecten zelf worden nog steeds gedeeld.

Teken in de andere richting

Begin met één kader voor elke klasse of interface. Een veld met een eenvoudige waarde wordt een attribuut. Een referentie naar een klasse in het programma wordt een associatie; een collectie duidt op een uiteinde met meerdere waarden. Bekijk controles in constructoren en methoden voordat je een ondergrens van één vastlegt. Vertaal extends naar een doorgetrokken lijn met driehoek en implements naar een onderbroken lijn met driehoek.

Overgeërfde velden en operaties blijven meestal in het kader van de superklasse. Zet nieuwe of overschreven leden in de subklasse. Voeg een afhankelijkheid toe voor een parameter, retourtype of lokaal gebruik wanneer die relatie helpt om het ontwerp uit te leggen.

Pakketten kunnen hun klassen omvatten in mapvormige kaders met een klein tabblad. Deze groepen helpen zichtbaar te maken welke onderdelen van welke andere afhankelijk zijn. Houd een documentatiediagram bij wanneer de code verandert; geef een vroege planningsschets een label zodat lezers het doel begrijpen.

Een Shipment die pakketten bevat, is geen soort Parcel. De lijst geeft een “heeft een”-relatie aan, dus teken een associatie. Gebruik overerving voor een “is een soort”-relatie die het Java-typesysteem en het gedrag daadwerkelijk ondersteunen.

Belangrijkste punten

  • Generalisatie gebruikt een doorgetrokken lijn en een open driehoek bij de superklasse.
  • Realisatie gebruikt een onderbroken lijn en een open driehoek bij de interface.
  • Vertaal velden, associaties, overerving en methodesignaturen voordat je body’s schrijft.
  • Behoud bij het vertalen de typen en zichtbaarheid uit het diagram.
  • Alleen het gedeclareerde type van een collectie legt geen minimumaantal vast.
  • Gebruik associaties voor opgenomen objecten en overerving voor subtyperelaties.
  • Houd documentatiediagrammen in overeenstemming met de code die ze beschrijven.
3

Deel een programma in lagen in

Een programma wordt gemakkelijker te testen en te wijzigen als de dialoog, regels en opslag afzonderlijke verantwoordelijkheden hebben. Je volgt een pakketopdracht door die onderdelen, verplaatst berekeningen uit consolecode en geeft samenwerkende objecten door via constructoren. Interfaces laten de logica werken met echte opslag of een vervanging in het geheugen. Door imports nauwkeurig te lezen zie je van welke typen elke laag afhankelijk is.

3.1 Houd de dialoog buiten berekeningen

De gebruikersinterface leest opdrachten en geeft resultaten weer. De applicatielogica bevat de regels en gegevens waarop die opdrachten werken. Door ze te scheiden kan een test een berekening rechtstreeks aanroepen en kan een andere interface die hergebruiken zonder de regel te kopiëren.

Geef elk onderdeel een duidelijke taak

TextUI verzorgt de dialoog: vraag om een gewicht, lees een regel met Scanner, zet de tekst om in een getal, roep de logica aan en geef het resultaat weer. ShippingCalculator accepteert een gewicht in gram en geeft een tarief in centen terug. De calculator vraagt de gebruiker niets en geeft geen antwoord weer.

Een methode met de naam printFee mengt die taken als de methode zowel berekent als uitvoer weergeeft. Verander de logicaoperatie in feeInCents(int weightGrams). De gebruikersinterface kan dan "Fee: " + calculator.feeInCents(weightGrams) + " cents" weergeven. Een test kan de teruggegeven gehele waarde met een assertie controleren zonder console-uitvoer op te vangen.

Schrijf de regel als retourwaarde

De pakketregel rekent 500 cent voor positieve gewichten tot en met 2.000 g, 900 cent tot en met 10.000 g en daarna 100 cent extra voor elke begonnen 1.000 g boven 10.000 g.

public int feeInCents(int weightGrams) {
    if (weightGrams <= 0) {
        throw new IllegalArgumentException("weight must be positive");
    }
    if (weightGrams <= 2000) {
        return 500;
    }
    if (weightGrams <= 10000) {
        return 900;
    }
    long excess = (long) weightGrams - 10000;
    long startedBlocks = (excess + 999) / 1000;
    return (int) (900 + 100 * startedBlocks);
}

De eerste twee takken die een geldig tarief teruggeven, omvatten hun bovengrens. Bij 12.500 g is het overschot 2.500 g. Geheeltallige deling na het optellen van 999 telt drie begonnen blokken en levert 1.200 cent op. De tussenberekening met long verwerkt ook grote positieve int-gewichten zonder overloop bij het optellen van 999.

Een ongeldig gewicht leidt tot een IllegalArgumentException: de methode wijst een argument af dat de regel schendt. De gebruikersinterface kan die exceptie opvangen en een leesbaar bericht kiezen. De regel hoort bij de logica; de formulering van het bericht hoort bij de interface.

Verbind de objecten in main

Geef de invoerlezer en calculator door aan de constructor van de gebruikersinterface:

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    ShippingCalculator calculator = new ShippingCalculator();
    TextUI ui = new TextUI(scanner, calculator);
    ui.start();
}

Maak één scanner voor de invoerstroom en deel die met de gebruikersinterface. Meerdere scanners op System.in kunnen elkaars gebufferde invoer verstoren. De main-methode stelt het programma samen; start voert de dialooglus uit.

Kijk bij het beoordelen van een klasse naar de verantwoordelijkheden. Een Scanner of System.out binnen een tariefcalculator laat zien dat invoer en uitvoer met regels zijn gemengd. Een tariefformule binnen de gebruikersinterface laat hetzelfde probleem van de andere kant zien. Verplaats de berekening naar een methode met expliciete invoer en een resultaat; houd het parsen van opdrachten en de weergavetekst bij elkaar in de gebruikersinterface.

Belangrijkste punten

  • De gebruikersinterface leest en geeft uitvoer weer; logicamethoden accepteren waarden en geven resultaten terug.
  • Houd tariefregels en validatie in de logica, zodat tests die rechtstreeks kunnen aanroepen.
  • Laat de gebruikersinterface bepalen hoe een resultaat of afgewezen invoer wordt weergegeven.
  • Maak samenwerkende objecten in main en geef ze door aan de constructor van de gebruikersinterface.
  • Tel een gedeeltelijk gebruikt gewichtsblok als een begonnen blok wanneer de regel dat vereist.

3.2 Verbind lagen via een repository

Een programma in lagen groepeert klassen op basis van hun verantwoordelijkheid. De presentatielaag verzorgt de dialoog, de logicalaag past regels toe en de gegevenstoegangslaag slaat informatie op en haalt die op. Een klasse gebruikt een andere laag via een duidelijke operatie in plaats van rechtstreeks bij de interne velden te komen.

Plaats de pakketklassen

Het gebruikersinterfacepakket bevat TextUI. Het domeinpakket bevat Parcel, ShippingCalculator en ParcelService. Het gegevenspakket bevat ParcelRepository en de opslagimplementaties daarvan. Main stelt de objecten boven deze lagen samen.

Een opdracht volgt deze route: de gebruiker voert “add 1200 Alex” in; de gebruikersinterface parst de opdracht en roept service.register(1200, "Alex") aan; de service valideert het gewicht, maakt een pakket, slaat het via de repository op en geeft 500 cent terug; de gebruikersinterface geeft het tarief weer. Tekst lezen, bepalen wat is toegestaan en het resultaat opslaan hebben elk een eigen plaats.

Wees afhankelijk van een contract

De service heeft opslaggedrag nodig, geen specifiek bestandsformaat. Druk dat gedrag uit via een interface:

public interface ParcelRepository {
    void save(Parcel parcel);
    List<Parcel> findAll();
}

De service bewaart een referentie van het type ParcelRepository en ontvangt die in de constructor:

public class ParcelService {
    private final ParcelRepository repository;

    public ParcelService(ParcelRepository repository) {
        this.repository = repository;
    }
}

Dit is constructorinjectie, een vorm van afhankelijkheidsinjectie: de aanroeper levert een samenwerkend object. Een service die binnen zijn eigen methoden new FileParcelRepository(...) maakt, kiest zelf voor bestandsopslag en maakt afzonderlijke tests moeilijker.

Wijzig de implementatie bij het samenstellen

Het draaiende programma kan new ParcelService(new FileParcelRepository("parcels.csv")) maken. Een test kan new InMemoryParcelRepository() leveren. Beide implementaties voldoen aan dezelfde interface. Aanroepen via de interface gebruiken polymorfisme om de methoden van het werkelijke object te bereiken.

Als het repositorycontract gelijk blijft, vereist een overstap naar andere opslag een nieuwe implementatie en een wijziging bij het samenstellen. Tariefregels en dialoogcode hoeven niet te weten hoe de gegevens worden opgeslagen.

Controleer de richting van afhankelijkheden nauwkeurig

Het neerwaartse pad waarlangs diensten worden gebruikt, loopt van gebruikersinterface naar logica naar opslag. Het domein mag geen gebruikersinterfaceklassen importeren of aanroepen. Als de logica de gebruiker een resultaat moet laten zien, geef je dat terug aan de gebruikersinterface. Als de gebruikersinterface rechtstreeks via de repository opslaat, kan ze de validatie van de service omzeilen.

Ook Java-typeverwijzingen zijn van belang: een repositorysignatuur die Parcel noemt, is afhankelijk van dat domeintype. Met ParcelRepository in het gegevenspakket en de service in het domeinpakket verwijzen die pakketten naar elkaar. Om die cyclische pakketafhankelijkheid op te heffen, plaats je de repository-interface naast de domeintypen en laat je de gegevensimplementaties die importeren. De service gebruikt nog steeds hetzelfde contract; alleen de plaats ervan verandert. Bekijk bij het controleren van het ontwerp zowel de route van methodeaanroepen als de imports.

Grotere programma’s kunnen pakketten eerst per functionaliteit groeperen, zoals bestellingen of klanten, en daarna de verantwoordelijkheden binnen elke functionaliteit scheiden. Pakketnamen helpen lezers verantwoordelijkheden te vinden; constructorparameters en methodesignaturen tonen de werkelijke afhankelijkheden.

Belangrijkste punten

  • Scheid de verantwoordelijkheden voor presentatie, logica en gegevenstoegang.
  • Geef een repository-interface door aan de constructor van de service.
  • Kies een implementatie met bestanden of met opslag in het geheugen waar het programma wordt samengesteld.
  • Laat acties van de gebruikersinterface via de logica lopen, zodat validatie wordt toegepast.
  • Geef resultaten terug aan de gebruikersinterface in plaats van vanuit de logica gebruikersinterfacecode aan te roepen.
  • Onderscheid bij het controleren van cyclische pakketafhankelijkheden de richting waarin diensten worden gebruikt van Java-typeafhankelijkheden.
4

Controleer code met JUnit-tests

Automatische tests zetten verwacht gedrag om in controles die je na elke wijziging opnieuw kunt uitvoeren. Een controle schrijven is pas het begin: nuttige invoer brengt grenswaarden aan het licht, nieuwe testopstellingen houden tests onafhankelijk en leesbare foutmeldingen helpen je de oorzaak te vinden. Je bouwt Jupiter-tests voor de pakketlogica, beheerst hun gegevens en samenwerkende objecten en gebruikt een korte cyclus waarin je eerst een test schrijft om een regel geval voor geval uit te breiden.

4.1 Een test die een uitkomst controleert

Een unittest roept een kleine eenheid code aan met gekozen invoer en controleert het resultaat automatisch. Zo wordt een eis uitvoerbaar. Door tests na een wijziging opnieuw uit te voeren, kun je een regressie vinden: een fout in gedrag dat eerder werkte.

Maak een Jupiter-test

JUnit Jupiter is het programmeermodel dat JUnit 5 en 6 gebruiken. JUnit 6 heeft Java 17 of later nodig. Testannotaties en asserties gebruiken org.junit.jupiter.api.

Plaats ShippingCalculatorTest.java onder src/test/java/com/example/parcels/domain, met dezelfde pakketdeclaratie als de calculator. De afzonderlijke broncodemap houdt tests gescheiden van de meegeleverde programmacode, terwijl toegang binnen hetzelfde pakket behouden blijft.

package com.example.parcels.domain;

import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;

class ShippingCalculatorTest {
    @Test
    void lightParcelCostsBaseFee() {
        ShippingCalculator calculator = new ShippingCalculator();

        int fee = calculator.feeInCents(1500);

        assertEquals(500, fee);
    }
}

De testklasse en -methode kunnen pakkettoegang hebben. Een testmethode geeft void terug en mag niet private zijn. Gebruik de @Test van Jupiter; org.junit.Test van JUnit 4 hoort bij een ander programmeermodel en wordt niet door de Jupiter-engine ontdekt. JUnit 4-asserties kunnen werken als hun bibliotheek beschikbaar is, maar consequent imports van Jupiter gebruiken houdt de opzet duidelijk.

Lees voorbereiden–uitvoeren–controleren

Het eerste deel bereidt een calculator voor. Het tweede deel voert uit door het tarief op te vragen. De laatste assertie vergelijkt de verwachte 500 cent met de teruggegeven waarde. Lege regels maken deze taken gemakkelijk herkenbaar. Alleen fee weergeven zou het aan een persoon overlaten om te bepalen of de waarde correct is; de assertie maakt die beslissing onderdeel van de test.

Bij assertEquals(expected, actual) staat de eis eerst en het waargenomen resultaat als tweede. Een optioneel bericht komt als laatste, zoals in assertEquals(500, fee, "fee for 1,500 g"). Als je de eerste twee argumenten omdraait, blijft voor deze waarden meestal gelijk of de test slaagt of mislukt, maar keert de betekenis van het foutrapport om.

Kies de juiste assertie

Gebruik assertTrue of assertFalse voor een booleaanse waarde en assertNull of assertNotNull wanneer de aan- of afwezigheid van een object de uitkomst is. Controles op objectgelijkheid gebruiken equals; zonder overschrijving kunnen twee verschillende objecten met overeenkomende velden toch als ongelijk worden beschouwd.

Resultaten met zwevendekommagetallen hebben een passende tolerantie nodig. De berekening 0.1 + 0.2 levert in een Java-double 0.30000000000000004 op:

assertEquals(0.3, 0.1 + 0.2, 1e-9);

Het derde argument staat een absoluut verschil tot 0.000000001 toe. Kies een tolerantie die bij de berekening past; een te grote tolerantie kan een betekenisvolle fout verbergen. Berekeningen met gehele aantallen centen hebben geen tolerantie voor kommagetallen nodig.

Lees een foutmelding

Voer een test uit vanuit de IDE of de buildtool. Een rapport zoals “expected: <500> but was: <900>” noemt het vereiste en het waargenomen resultaat; de stacktrace wijst naar de assertie. Een onverwachte exceptie laat een test ook mislukken. Groen betekent dat de gecontroleerde gevallen zijn geslaagd; het kiezen van die gevallen blijft een afzonderlijk onderdeel van testen.

Belangrijkste punten

  • Gebruik Jupiter-annotaties en -asserties uit org.junit.jupiter.api.
  • Plaats tests in src/test/java, normaal gesproken in het pakket van de geteste klasse.
  • Bereid de objecten voor, voer de bewerking uit en controleer de uitkomst met een assertie.
  • Zet de verwachte waarde eerst in assertEquals.
  • Gebruik een onderbouwde tolerantie voor vergelijkingen van zwevendekommagetallen.
  • Lees de naam van de mislukte test, de verwachte en werkelijke waarden en de locatie van de assertie.

4.2 Kies invoer aan de grenzen van regels

Een nuttige verzameling tests controleert hoe de specificatie invoer in gevallen verdeelt. Eén gewone invoerwaarde test zelden een hele regel. Grensgevallen op een grens en direct ernaast zijn vooral geschikt om een voorwaarde met de verkeerde vergelijkingsoperator te vinden.

Verdeel de mogelijke invoer

Een equivalentieklasse groepeert invoerwaarden die gelijk moeten worden behandeld. De verzendregel heeft ongeldige gewichten van nul of lager, een tariefgroep van 500 cent tot en met 2.000 g, een tariefgroep van 900 cent tot en met 10.000 g en daarboven een berekening met begonnen blokken. Kies representatieve invoerwaarden uit de klassen en bekijk daarna hun grenzen.

Gewicht in gramVerwachte uitkomst
0, -5Afwijzen met IllegalArgumentException
1, 2000500 cent
2001, 10000900 cent
10001, 110001.000 cent
110011.100 cent

Als je <= 2000 verandert in < 2000, heeft dat gevolgen voor precies 2.000 g. Een test bij 1.500 g slaagt nog steeds. Test boven 10.000 g zowel een heel blok als de eerste gram van het volgende blok om het naar boven afronden te controleren.

Verwachte waarden komen uit de eis en een berekening met de hand. Bij 12.500 g is het overschot 2.500 g. Daarvoor zijn drie begonnen blokken nodig, dus het verwachte resultaat is 1.200 cent. Als je de methode uitvoert en het antwoord naar de test kopieert, kopieer je ook eventuele fouten.

Test afgewezen invoer

Een exceptietest geeft JUnit de operatie die moet worden uitgevoerd:

assertThrows(IllegalArgumentException.class,
    () -> calculator.feeInCents(0));

De expressie () -> ... is een lambda: een klein stuk code dat aan JUnit wordt doorgegeven. JUnit voert die binnen de controle uit. Als je de methode aanroept voordat je die aan de assertie doorgeeft, ontsnapt de exceptie voordat de controle die kan onderzoeken.

assertThrows accepteert het genoemde exceptietype of een subklasse daarvan. De assertie mislukt als de operatie normaal wordt voltooid of een onverenigbaar type opwerpt. De assertie geeft de exceptie terug, zodat een vereist bericht kan worden gecontroleerd:

IllegalArgumentException error = assertThrows(
    IllegalArgumentException.class,
    () -> calculator.feeInCents(0));
assertEquals("weight must be positive", error.getMessage());
Geef één scenario een leesbare naam

Namen zoals zeroWeightIsRejected en feeIsBaseFeeAtExactly2000Grams duiden het bedoelde gedrag aan. Een test kan meerdere asserties hebben als die dezelfde uitkomst controleren. Nadat pakketten van 1.200 g en 800 g aan een zending zijn toegevoegd, beschrijft de controle op een aantal van twee en een totaalgewicht van 2.000 g dat ene scenario. assertAll kan controles groeperen, zodat alle mislukkingen worden gemeld.

Controleer gewijzigde objecten via hun gedrag, zoals parcelCount() en totalWeightGrams(), in plaats van private velden beschikbaar te maken voor tests. Bij refactoring kunnen opslagdetails dan veranderen terwijl de controles behouden blijven. Volledige regeldekking betekent dat de tests de regels hebben uitgevoerd; toch kan een verkeerde grens of niet-geteste ongeldige invoer worden gemist.

Belangrijkste punten

  • Verdeel invoer volgens de specificatie en test representatieve gevallen en grenswaarden.
  • Controleer een grens zelf en nabijgelegen waarden om één-ernaastfouten te vinden.
  • Bereken verwachte resultaten onafhankelijk van de implementatie.
  • Geef de operatie als lambda door aan assertThrows.
  • Geef per test één scenario een naam; meerdere samenhangende asserties kunnen dat scenario controleren.
  • Test gewijzigde toestand via gedrag dat aanroepers kunnen waarnemen.

4.3 Tests die afzonderlijk en in elke volgorde werken

Elke test moet alles voorbereiden wat hij nodig heeft. Een test die alleen slaagt nadat een andere test gegevens heeft toegevoegd, is afhankelijk van een verborgen voorgeschiedenis. Zulke tests zijn moeilijk te vertrouwen wanneer een testuitvoerder de volgorde verandert of slechts één methode uitvoert.

Maak een nieuwe testopstelling

Een testopstelling is de verzameling objecten en gegevens die een test gebruikt. Plaats herhaalde voorbereiding per test in @BeforeEach:

class ShipmentTest {
    private Shipment shipment;

    @BeforeEach
    void setUp() {
        shipment = new Shipment();
    }

    @Test
    void newShipmentIsEmpty() {
        assertEquals(0, shipment.parcelCount());
    }

    @Test
    void addingParcelIncreasesCount() {
        shipment.add(new Parcel(1200, "Alex"));
        assertEquals(1, shipment.parcelCount());
    }
}

De voorbereiding wordt vóór elke methode uitgevoerd. De tweede test verandert de zending niet die de eerste gebruikt. Standaard maakt Jupiter ook voor elke testmethode een nieuwe instantie van de testklasse. Veranderlijke statische velden blijven gedeeld, dus een statische lijst kan nog steeds toestand tussen die instanties laten doorlekken.

@AfterEach ruimt na elke test op. @BeforeAll en @AfterAll worden één keer voor de klasse uitgevoerd en zijn onder de standaardlevenscyclus statisch. Gedeelde referentiegegevens die alleen worden gelezen, kunnen geschikt zijn voor een eenmalige voorbereiding per klasse; veranderlijke objecten moeten meestal voor elke test opnieuw worden gemaakt. Tests moeten afzonderlijk werken zonder afhankelijk te zijn van de volgorde in het bronbestand.

Gebruik één testbody voor een tabel

Een geparametriseerde test voert dezelfde gedragscontrole uit voor meerdere rijen:

@ParameterizedTest
@CsvSource({"1, 500", "2000, 500", "2001, 900",
            "10000, 900", "10001, 1000"})
void feeMatchesTable(int grams, int expected) {
    assertEquals(expected,
        new ShippingCalculator().feeInCents(grams));
}

Elke rij is een afzonderlijke aanroep met een eigen rapportage. @ValueSource(ints = {0, -5}) levert één geheel getal per aanroep voor tests van ongeldige invoer. Annotaties voor geparametriseerde tests komen uit org.junit.jupiter.params; CsvSource en ValueSource staan in het pakket provider daarvan. De verzamelafhankelijkheid junit-jupiter bevat de module voor geparametriseerde tests.

@DisplayName voegt een leesbaar rapportlabel toe. @Disabled legt een overgeslagen test met een reden vast; een overgeslagen controle levert geen bewijs dat het te testen gedrag werkt.

Beheers de omgeving

Een servicetest kan een InMemoryParcelRepository ontvangen, een werkende vereenvoudigde testimplementatie die pakketten in het geheugen opslaat. Dit vermijdt echte bestandsinhoud en isoleert de logica. Geef bij het testen van de invoerverwerking een Scanner die uit een bekende string is opgebouwd door aan een tekstinterface. Een vaste datum of een Random met een vaste beginwaarde maakt externe invoer eveneens voorspelbaar.

Een instabiele test geeft bij verschillende uitvoeringen een ander resultaat zonder relevante codewijziging. Gedeelde veranderlijke gegevens, de huidige tijd, onbeheerde willekeurige waarden en achtergebleven bestanden zijn veelvoorkomende oorzaken. Geef de test controle over die invoer. Houd hulpmethoden klein en de voorbereiding leesbaar; een testopstelling die veel niet-verwante objecten maakt, kan laten zien dat de geteste klasse te veel taken heeft.

Belangrijkste punten

  • Gebruik @BeforeEach om tests nieuwe testopstellingen te geven.
  • Een nieuwe instantie van de testklasse zet veranderlijke statische toestand niet terug.
  • Geparametriseerde tests voeren één methode uit met afzonderlijk gerapporteerde invoerrijen.
  • Gebruik vereenvoudigde testimplementaties en aangeleverde invoer om logicatests onafhankelijk te houden van echte opslag en de klok.
  • Zorg dat elke test afzonderlijk uitvoerbaar is, zonder vereiste methodevolgorde.

4.4 Bouw gedrag op via rood, groen en refactoren

Testgestuurde ontwikkeling (TDD) begint een gedragswijziging met een test. Een korte cyclus maakt het ontbrekende gedrag zichtbaar, implementeert het en verbetert daarna de structuur van de code. Een testlijst bevat gevallen die nog moeten worden onderzocht, zonder dat ze allemaal tegelijk hoeven te worden geschreven.

Kies één punt uit de testlijst

Noteer voor een controle op schrikkeljaren in de gregoriaanse kalender vier gedragingen: een gewoon jaar dat geen schrikkeljaar is, een jaar dat deelbaar is door vier, een eeuwjaar dat niet deelbaar is door 400 en een eeuwjaar dat deelbaar is door 400. Deze gevallen vertegenwoordigen verschillende delen van de regel.

Rood: schrijf één test en voer die uit. Een ontbrekende methode kan een compilatiefout veroorzaken. Controleer zodra de methode bestaat of de test mislukt vanwege het gedrag dat je wilt toevoegen. Een ontbrekende assertie, de verkeerde methodeaanroep of een niet-ontdekte test kan een schijnbaar groen resultaat misleidend maken. Een nieuw geval kan ook al door bestaande code worden afgehandeld; onderzoek dat resultaat voordat je de implementatie wijzigt.

Groen: voeg de eenvoudigste code toe waarmee de nieuwe test en alle eerdere tests slagen. Refactoren: verbeter de structuur zonder het gedrag te veranderen en voer de tests na elke kleine wijziging opnieuw uit. Kies daarna het volgende geval.

Laat gevallen de regel ontwikkelen

De eerste controle is assertFalse(LeapYears.isLeapYear(2023)). Als je de methode maakt met return false;, slaagt dit geval. Het is een bewuste eerste stap, geen voltooide kalenderregel.

De volgende controle, assertTrue(LeapYears.isLeapYear(2024)), mislukt. Verander de methode in return year % 4 == 0;. Beide gevallen slagen nu.

Daarna brengt assertFalse(LeapYears.isLeapYear(1900)) de uitzondering voor eeuwjaren aan het licht. Als je de expressie verandert in year % 4 == 0 && year % 100 != 0, slagen die drie tests.

Tot slot brengt assertTrue(LeapYears.isLeapYear(2000)) de uitzondering op de eeuwjaarregel aan het licht. De implementatie wordt:

public static boolean isLeapYear(int year) {
    return year % 400 == 0
        || (year % 4 == 0 && year % 100 != 0);
}

Voer na de wijziging alle vier controles uit. Elke nieuwe mislukking wees op één ontbrekend deel van het gedrag, in plaats van je te laten zoeken naar fouten in een grote onvoltooide implementatie.

Verbeter de structuur terwijl de tests groen zijn

Refactoring verandert namen of indeling terwijl het gedrag behouden blijft. Een methode hernoemen, een hulpmethode afsplitsen, herhaalde logica verwijderen en een klasse naar een beter pakket verplaatsen zijn refactorings als het waarneembare gedrag gelijk blijft. Het tarief op een grenswaarde wijzigen is een gedragswijziging en heeft een test nodig die die wijziging beschrijft.

Als een opschoning een test laat mislukken, herstel dan het bedoelde gedrag voordat je een volgende functionaliteit toevoegt. Kleine wijzigingen helpen de oorzaak van een mislukking te vinden. Als je opschoning overslaat, blijft code over die wel werkt maar moeilijker uit te breiden wordt; als je een grote implementatie voor veel toekomstige gevallen schrijft, ontstaat meer code om te onderzoeken wanneer de volgende controle mislukt.

TDD hangt af van de kwaliteit van de eisen en de gekozen gevallen. Een verkeerd begrepen regel kan zowel in de test als in de implementatie voorkomen. Logica met duidelijke invoer en uitvoer is een nuttig startpunt, omdat het verwachte resultaat onafhankelijk kan worden vastgesteld en snel kan worden gecontroleerd.

Belangrijkste punten

  • Houd een testlijst bij en implementeer steeds één gedraging.
  • Rood maakt het ontbrekende gedrag zichtbaar; groen laat alle huidige tests slagen.
  • Onderzoek een onverwacht geslaagde test voordat je aanneemt dat die de bedoelde wijziging heeft gecontroleerd.
  • Refactor de structuur met behoud van gedrag en voer de tests opnieuw uit.
  • Gebruik opeenvolgende gevallen om gewone regels en uitzonderingen aan het licht te brengen.
5

Bouw en test projecten met Maven of Gradle

Een project heeft meer nodig dan bronbestanden: het heeft bibliotheken, compilatie-instellingen, een testuitvoerder en een manier om het resultaat te verpakken nodig. Maven en Gradle beschrijven die keuzes, zodat een opdracht de build kan herhalen. Je gebruikt hun standaardmappen, declareert Jupiter-afhankelijkheden, voert de meegeleverde wrapper uit en onderscheidt een codefout van een probleem met testdetectie of afhankelijkheden door de builduitvoer te lezen.

5.1 Bouw een Maven-project vanuit de beschrijving

Een buildtool zet bronbestanden om in een gecontroleerd, verpakt resultaat. Maven leest een projectbeschrijving, downloadt gedeclareerde bibliotheken, compileert code, voert tests uit en maakt een artefact. Dezelfde beschrijving geeft de opdrachtregel en IDE een gedeeld beeld van het project.

Gebruik de standaardindeling

Zet Java-productiecode in src/main/java, tests in src/test/java, productiebestanden zonder code in src/main/resources en testgegevens in src/test/resources. Pakketmappen beginnen onder elke Java-broncodemap. Maven schrijft gegenereerde uitvoer onder target; bewerk de bronbestanden, niet de gecompileerde kopieën in die map.

Het bestand pom.xml in de hoofdmap van het project, het Project Object Model, identificeert het project met artefactcoördinaten: groupId, artifactId en version. Zo identificeert com.example:parcels:1.0-SNAPSHOT een ontwikkelversie van het pakketprogramma. Een bibliotheekafhankelijkheid heeft ook coördinaten.

Declareer JUnit voor tests

Hieronder staat een volledige kleine Maven-configuratie. Die compileert voor Java 17 en kiest een testuitvoerder die Jupiter ondersteunt:

<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>parcels</artifactId>
  <version>1.0-SNAPSHOT</version>
  <properties>
    <maven.compiler.release>17</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>6.1.3</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.14.1</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.5.4</version>
      </plugin>
    </plugins>
  </build>
</project>

Maven haalt afhankelijkheden uit repositories, normaal gesproken Maven Central, en bewaart ze in een cache in .m2/repository onder je persoonlijke map. Bibliotheken die een afhankelijkheid nodig heeft, zijn transitieve afhankelijkheden. Het testbereik maakt JUnit beschikbaar voor het compileren en uitvoeren van tests, niet voor het compileren van de hoofdcode of het klassenpad van het programma tijdens de uitvoering. Een gewoon JAR-bestand houdt de JAR-bestanden van afhankelijkheden afzonderlijk, tenzij de verpakking is ingesteld om ze te bundelen.

Vraag een levenscyclusfase aan

De hoofdlevenscyclus doorloopt onder andere de fasen compile, test, package en install. Als je package aanvraagt, worden ook eerdere fasen uitgevoerd. Met de wrapper compileert en test ./mvnw test; ./mvnw package maakt ook het JAR-bestand onder target; ./mvnw install plaatst het artefact bovendien in de lokale repository. De afzonderlijke levenscyclus voor opschonen, ./mvnw clean, verwijdert gegenereerde uitvoer.

Een mislukte test stopt de normale build voordat een nieuw artefact wordt verpakt. Een ouder JAR-bestand kan nog aanwezig zijn totdat je opschoont, dus de aanwezigheid ervan bewijst niet dat de laatste build is geslaagd.

Maak testdetectie zichtbaar

De standaardpatronen van Surefire omvatten namen die beginnen met Test of eindigen op Test, Tests of TestCase. ShippingChecks voldoet daar niet aan; gebruik ShippingCalculatorTest en bekijk het gerapporteerde aantal tests. Tests onder src/main/java kunnen geen afhankelijkheid met testbereik gebruiken.

De Maven Wrapper, mvnw of op Windows mvnw.cmd, voert de Maven-versie uit die het project opgeeft. Gebruik een project dat de wrapperbestanden bevat; een POM maakt de scripts niet zelf. Vanuit de hoofdmap biedt de wrapper een consistente buildopdracht zonder afzonderlijke Maven-installatie.

Belangrijkste punten

  • Houd hoofdcode, tests en bestanden zonder code in hun standaardbronmappen.
  • Identificeer projecten en bibliotheken met coördinaten voor groep, artefact en versie.
  • Declareer JUnit met testbereik en gebruik een compatibele Surefire-testuitvoerder.
  • Een aangevraagde Maven-fase voert de eerdere fasen in de levenscyclus uit.
  • Controleer aantallen tests; een geslaagde build kan toch een test met een verkeerde naam hebben gemist.
  • Gegenereerde uitvoer hoort in target en kan opnieuw worden opgebouwd.
  • De wrapper voert de Maven-versie uit die het project heeft gekozen.

5.2 Voer Gradle-tests uit en volg buildfouten

Gradle beschrijft een build met een script en voert taken met hun afhankelijkheden uit. Een Kotlin-script heet build.gradle.kts; een Groovy-script heet build.gradle. Plug-ins leveren taken en afhankelijkheidsconfiguraties geven aan waar bibliotheken nodig zijn.

Configureer een applicatie en Jupiter

Een settings.gradle.kts in de hoofdmap van het project kan rootProject.name = "parcels" bevatten. Een bijpassend Kotlin-buildscript is:

plugins {
    application
}

repositories {
    mavenCentral()
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

dependencies {
    testImplementation(platform("org.junit:junit-bom:6.1.3"))
    testImplementation("org.junit.jupiter:junit-jupiter")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

application {
    mainClass = "com.example.parcels.Main"
}

tasks.test {
    useJUnitPlatform()
}

De plug-in application levert Java-buildtaken en voegt run toe. mavenCentral() kiest een repository voor afhankelijkheden. De BOM stemt de versies van JUnit-modules op elkaar af, zodat de versie één keer voorkomt. testImplementation levert bibliotheken die nodig zijn om tests te compileren en uit te voeren. testRuntimeOnly levert de launcher alleen tijdens het uitvoeren ervan. Bibliotheken voor de hoofdcode gebruiken implementation, waardoor ze ook beschikbaar zijn voor testcode.

useJUnitPlatform() kiest het platform dat Jupiter-tests uitvoert. Als je Jupiter-bibliotheken toevoegt zonder de testuitvoerder te configureren, worden die tests mogelijk niet ontdekt. De huidige Gradle-versie meldt bij de standaardinstelling een fout als er testbroncode bestaat maar geen tests worden ontdekt; bekijk de uitvoer en configuratie van de testtaak in plaats van de controle te verwijderen.

Voer taken uit met de wrapper

Gebruik de wrapper die bij het project is meegeleverd: ./gradlew test voert tests uit; ./gradlew build compileert, test en verpakt; ./gradlew run start de geconfigureerde main-klasse; en ./gradlew clean verwijdert build. Windows gebruikt gradlew.bat. Gradle gebruikt dezelfde bronindeling voor hoofdcode en tests als Maven en schrijft gegenereerde uitvoer naar build.

Een taak met de markering UP-TO-DATE heeft ongewijzigde bijgehouden invoer en uitvoer, zodat Gradle het eerdere resultaat hergebruikt. Die melding verschilt van een nieuw uitgevoerde testrun. Het HTML-testrapport staat in build/reports/tests/test/index.html.

Zoek de fase die mislukte

Begin bij de eerste bruikbare fout. “Cannot find symbol” tijdens het compileren van de hoofdcode wijst op de genoemde code, import of afhankelijkheid. Een ontbrekend org.junit.jupiter.api tijdens het compileren van tests wijst op de instelling van testafhankelijkheden; dezelfde fout tijdens het compileren van de hoofdcode kan betekenen dat een test in de bronmap voor hoofdcode is geplaatst. Een fout bij het oplossen van een afhankelijkheid wijst op coördinaten, toegang tot de repository of netwerkbeschikbaarheid.

Een mislukking tijdens het uitvoeren van een test is anders: de test is gecompileerd en ontdekt, maar de controle is mislukt. Als “expected: <500> but was: <900>” verschijnt bij de tariefgrens van 2.000 g, bekijk dan de vergelijking die de tariefgroep kiest. Afhankelijkheden wijzigen herstelt die berekening niet.

Maven biedt een declaratieve XML-beschrijving met vaste levenscyclusfasen. Gradle biedt configureerbare scripts en taken, waaronder het overslaan van actueel werk. Beide automatiseren herhaalbare Java-builds; gebruik de tool die voor het project is gekozen en lees de eerste fout waarop je kunt reageren voordat je code wijzigt.

Belangrijkste punten

  • Plug-ins voegen Gradle-taken toe; configuraties bepalen waar afhankelijkheden beschikbaar zijn.
  • Stem JUnit-versies af met een BOM en voer Jupiter uit op het JUnit Platform.
  • Gebruik de meegeleverde Gradle Wrapper voor de taken test, build, run en clean.
  • Onderscheid taakresultaten uit de cache, compilatiefouten, afhankelijkheidsfouten en mislukte asserties.
  • Lees de naam van de mislukte test en de verwachte en werkelijke waarden voordat je de implementatie wijzigt.

Klassendiagrammen en unittests in Java: voorbeelden

5 Walkthroughs

1

Verplaats een klasse voor pakketten naar een Java-pakket

13 stappen
Verbind een bronpad, imports en toegangscontroles terwijl je gebruikersinterfacecode van domeincode scheidt.
  1. 1
    IntroductieGeef de klasse een duidelijke plaats

    Een pakketprogramma heeft een calculator en een tekstinterface. Als deze klassen naar afzonderlijke pakketten worden verplaatst, heeft Java namen, mappen en toegangsniveaus nodig die overeenkomen.

    Volg de verplaatsing in drie stappen: plaats de klasse, laat een ander bestand de naam gebruiken en controleer daarna of die aanroeper de constructor en methoden kan gebruiken.

  2. 2
    VoorbeeldBegin onder de broncodemap

    De broncodemap is src/main/java. Zet voor Parcel in com.example.parcels.domain de punten in de pakketnaam om in mappen en voeg de bestandsnaam toe:

    src/main/java/com/example/parcels/domain/Parcel.java
    

    Schrijf package com.example.parcels.domain; aan het begin van het bestand. De mappen src/main/java van de broncodemap beschrijven de buildindeling en blijven buiten de pakketdeclaratie.

  3. 3
    UitlegHoud de identiteit van de klasse intact

    De eenvoudige naam is Parcel; de volledig gekwalificeerde naam is com.example.parcels.domain.Parcel. Het verplaatsen van het bestand verandert die identiteit op zichzelf niet.

    Een publieke klasse met de naam Parcel hoort in Parcel.java. Voer een main-klasse in een pakket uit vanaf de hoofdmap van de gecompileerde boom met een opdracht zoals java -cp out com.example.parcels.Main, zodat Java de pakketmappen onder out kan vinden.

  4. 4
    UitdagingHerstel een niet-overeenkomende locatie

    Een publieke klasse met de naam Receipt declareert package com.example.parcels.domain;, maar is opgeslagen onder src/main/java/com/example/parcels/ui/. Het pakket moet ongewijzigd blijven. Waar moet het bestand naartoe?

    ---reveal---

    Verplaats het naar src/main/java/com/example/parcels/domain/Receipt.java. Het pakketpad begint onder de broncodemap en de bestandsnaam komt overeen met de publieke klasse. Een import elders wijzigen zou de bronindeling niet herstellen.

  5. 5
    VoorbeeldMaak de typenaam gemakkelijk te gebruiken

    De gebruikersinterface heeft een eigen declaratie en importeert daarna het domeintype:

    package com.example.parcels.ui;
    
    import com.example.parcels.domain.Parcel;
    

    De klassebody kan nu de eenvoudige naam Parcel gebruiken. De import geldt alleen voor dit bestand. De import lost een naam op; die kopieert geen klasse en maakt geen afhankelijkheid beschikbaar voor de build.

  6. 6
    KernpuntEen joker stopt bij één pakket

    import com.example.parcels.*; omvat het domeinpakket niet. Het gedeelde begin van de naam lijkt op een stamboom, maar elke volledige pakketnaam duidt een afzonderlijk pakket aan.

    Dezelfde regel verklaart waarom java.util.* geen java.util.function.Function levert. Gebruik een import van één type, de jokerimport van het juiste pakket of een volledig gekwalificeerde naam voor dat type.

  7. 7
    UitdagingHaal twee lijsten uit elkaar

    Een bestand heeft zowel java.util.List als java.awt.List nodig. Het heeft momenteel imports van beide afzonderlijke typen en de compilatie mislukt. Hoe kan het bestand beide typen zonder dubbelzinnigheid noemen?

    ---reveal---

    Behoud één import van een type, zoals import java.util.List;, en schrijf java.awt.List voluit waar dat type nodig is. Twee verschillende imports van één type kunnen in één bestand niet dezelfde eenvoudige naam twee betekenissen geven.

  8. 8
    TipHerken namen die geen import nodig hebben

    Een bestand kan typen in zijn eigen pakket gebruiken zonder ze te importeren. Toegankelijke typen uit java.lang, waaronder String, Math en System, zijn ook automatisch beschikbaar.

    Een statische import heeft een andere taak: als je Assertions.assertEquals statisch importeert, kun je assertEquals(...) direct aanroepen. Die import verkort een verwijzing naar een statisch lid, geen pakketnaam.

  9. 9
    VoorbeeldControleer ook de constructor

    Het type is public, maar de constructor heeft geen modifier:

    public class Parcel {
        Parcel(int grams, String recipient) { }
    }
    

    Die constructor heeft pakkettoegang. De gebruikersinterface kan Parcel via de import oplossen, maar kan vanuit het gebruikersinterfacepakket geen object ervan construeren. Markeer de constructor als public als het maken van pakketten tot de bedoelde publieke operaties van het domein behoort; houd de velden private.

  10. 10
    UitdagingMaak de operatie toegankelijk en houd de hulpklasse verborgen

    De gebruikersinterface moet de methode feeInCents van een publieke calculator aanroepen. Die methode is momenteel zonder toegangsmodifier gedeclareerd. Alleen de calculator gebruikt de hulpklasse WeightBands met pakkettoegang. Welke toegang moet veranderen?

    ---reveal---

    Maak feeInCents public en laat WeightBands pakkettoegang houden. De gebruikersinterface heeft de berekeningsoperatie nodig, niet de hulpklasse voor de tabel. Het importeren van een van beide klassen verandert de toegangsrechten niet.

  11. 11
    UitlegEen subklasse heeft specifieke rechten

    Protected leden zijn toegankelijk voor het hele declarerende pakket en voor subklassen volgens de regels voor overerving tussen pakketten. In een ander pakket kan een subklasse haar eigen overgeërfde instantielid gebruiken.

    Ze kan geen referentie met het type van de superklasse gebruiken om dat lid op een willekeurig superklasseobject te benaderen. Protected toegang is dus ruimer dan toegang voor alleen subklassen, maar beperkter dan public toegang.

  12. 12
    UitdagingOnderzoek het hele toegangspad

    TextUI importeert een publieke Parcel correct. Bij de aanroep new Parcel(800, "Jo") wordt gemeld dat de constructor niet van buiten het pakket toegankelijk is. De constructor heeft geen modifier. Wat herstelt deze bedoelde constructie vanuit een ander pakket?

    ---reveal---

    Maak de constructor public. Het pakket en de import lossen het type al op, dus het bestand verplaatsen of een jokerimport toevoegen verhelpt de gemelde toegangsfout niet. Private pakketvelden kunnen private blijven.

  13. 13
    SamenvattingGebruik drie afzonderlijke controles

    Controleer de locatie door de declaratie te vergelijken met mappen onder de broncodemap. Controleer de naamgeving via imports of volledig gekwalificeerde namen. Controleer de toegang tot het type en de operatie die de aanroeper daadwerkelijk gebruikt.

    Deze volgorde helpt een vaag probleem als “Java kan deze klasse niet gebruiken” om te zetten in een gerichte correctie van de bronindeling, naamoplossing of zichtbaarheid.

2

Lees een pakketmodel en bouw het Java-skelet

13 stappen
Vertaal UML-kaders, relatie-uiteinden en subtypelijnen naar consistente Java-declaraties.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieLees de structuur voordat je body’s schrijft
  2. 2
    VoorbeeldVertaal één veld en één methode
  3. 3
    UitlegHerken een lid op klasseniveau
  4. 4
    UitdagingBehoud de symbolen
  5. 5
    VoorbeeldTel pakketten vanuit één zending
  6. 6
    VoorbeeldKies een representatie en dwing de regel af
  7. 7
    KernpuntVolg een verbinding in één richting
  8. 8
    UitdagingControleer een minimumaantal
  9. 9
    UitlegZet eigenaarschap aan het juiste uiteinde
  10. 10
    VoorbeeldLees twee lijnen met driehoeken
  11. 11
    TipBouw de declaraties op volgorde
  12. 12
    UitdagingCombineer een contract en een collectie
  13. 13
    SamenvattingHoud de taak van elk symbool apart
3

Volg één opdracht door afzonderlijke verantwoordelijkheden

11 stappen
Verplaats een tariefberekening uit de gebruikersinterface en verbind een service met vervangbare opslag.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieVolg een opdracht door het programma
  2. 2
    VoorbeeldHaal de regel uit consolecode
  3. 3
    VoorbeeldBereken een begonnen blok
  4. 4
    KernpuntHoud validatie bij de regel
  5. 5
    UitdagingMaak een berekening herbruikbaar
  6. 6
    VoorbeeldGeef opslag door aan de service
  7. 7
    VerbandVervang bestanden door een werkende testimplementatie
  8. 8
    TipControleer aanroepen en imports afzonderlijk
  9. 9
    UitdagingHerstel een omweg buiten de service om
  10. 10
    UitdagingSluit een beheerste servicetest aan
  11. 11
    SamenvattingPlaats elke wijziging bij de bijbehorende taak
4

Bouw een nuttige verzameling Jupiter-tests op

19 stappen
Schrijf een eerste assertie, kies grenswaarden, isoleer toestand en ontwikkel een regel met kleine wijzigingen waarbij je eerst tests schrijft.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieZet eisen om in uitvoerbare controles
  2. 2
    VoorbeeldBereid voor, voer uit en controleer
  3. 3
    UitlegLees verwacht en werkelijk correct
  4. 4
    TipSta bewust numerieke afronding toe
  5. 5
    UitdagingHerstel een test die alleen uitvoer weergeeft
  6. 6
    VoorbeeldZoek de tariefovergangen
  7. 7
    KernpuntHoud de verwachting onafhankelijk
  8. 8
    VoorbeeldPlaats de afgewezen aanroep binnen de controle
  9. 9
    UitdagingKies het geval dat de wijziging blootlegt
  10. 10
    VoorbeeldGeef elke test een nieuwe zending
  11. 11
    VoorbeeldVoer een tabel door één testbody
  12. 12
    VerbandBeheers het samenwerkende object, niet de testvolgorde
  13. 13
    UitdagingOnderzoek gegevens die een andere test achterliet
  14. 14
    VoorbeeldBegin de cyclus met een gewoon jaar
  15. 15
    VoorbeeldVoeg de algemene regel voor deelbaarheid door vier toe
  16. 16
    VoorbeeldVerfijn de voorwaarde met eeuwjaren
  17. 17
    UitdagingKies een volgende stap en behoud de controles
  18. 18
    UitdagingBreng gevalskeuze en isolatie samen
  19. 19
    SamenvattingVertrouw op specifiek, herhaalbaar bewijs
5

Van buildbestand naar testrapport

13 stappen
Configureer afhankelijkheden en testdetectie, voer wrappers uit en onderzoek fouten in de fase waarin ze optreden.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieHerhaal de build met één opdracht
  2. 2
    UitlegScheid bronbestanden van gegenereerde uitvoer
  3. 3
    VoorbeeldGeef het project en de testbibliotheek een naam
  4. 4
    VoorbeeldVraag de fase aan die je nodig hebt
  5. 5
    TipVerwar nul tests niet met gecontroleerd gedrag
  6. 6
    UitdagingZoek een verkeerd geplaatste test
  7. 7
    UitlegKies de Gradle-taken
  8. 8
    VoorbeeldHoud Jupiter-modules op elkaar afgestemd
  9. 9
    VoorbeeldGeef de testtaak aan hoe Jupiter moet worden uitgevoerd
  10. 10
    UitdagingHerstel testdetectie
  11. 11
    VoorbeeldVolg de mislukking van een uitgevoerde test
  12. 12
    UitdagingCombineer detectie met een nieuwe build
  13. 13
    SamenvattingLees de build als bewijs in fasen

Klassendiagrammen en unittests in Java: oefenvragen

50 Oefenvragen in 5 sets

Set 1: Namen, mappen en toegang in Java10 Oefenvragen

Vraag 1 van 10

Een publieke klasse Ticket declareert package org.example.events.model;. Welk pad moet die onder de Java-broncodemap voor de hoofdcode hebben?
  1. org/example/events/Ticket/model.java
  2. org/example/events/model/Ticket.java
  3. src/main/java/org/example/events/model/Ticket.java
  4. org.example.events.model/Ticket.java
Toon antwoordVerberg antwoord
Antwoord: B
Elke punt wordt een map onder de broncodemap en de naam van de publieke klasse levert Ticket.java op. De broncodemap is al de beginlocatie, dus als je src/main/java opnieuw toevoegt, verdubbel je het deel van het pad dat bij de buildindeling hoort.

Vraag 2 van 10

Een klasse in pakket shop.ui heeft een hulpklasse nodig die momenteel geen pakketdeclaratie heeft. Hoe moet de hulpklasse worden ingedeeld zodat de gebruikersinterface die kan importeren?
  1. Laat de klasse naamloos en voeg import Helper; toe aan het bronbestand van de gebruikersinterface.
  2. Plaats de klasse in een map met de naam shop zonder een pakketdeclaratie toe te voegen.
  3. Laat de klasse naamloos en kwalificeer de naam met het pad van de broncodemap voor de hoofdcode.
  4. Plaats de klasse in een benoemd pakket met toegankelijke declaraties.
Toon antwoordVerberg antwoord
Antwoord: D
Een klasse in een benoemd pakket kan geen type uit het naamloze pakket importeren. Geef de hulpklasse een pakketdeclaratie en een bijbehorende locatie en zorg daarna voor de toegang die de aanroeper nodig heeft; alleen het bestand verplaatsen geeft de klasse geen identiteit in een benoemd pakket.

Vraag 3 van 10

Een bestand heeft import java.util.*; en gebruikt java.util.function.Predicate met de eenvoudige naam. Welke extra import van één type levert die naam?
  1. import java.util.function.Predicate;
  2. import java.util.Predicate;
  3. import java.util.function;
  4. import static java.util.function.Predicate;
Toon antwoordVerberg antwoord
Antwoord: A
Predicate staat in het afzonderlijke pakket java.util.function, dus de volledige typenaam is nodig in de import van één type. De bestaande jokerimport java.util.* bereikt geen onderliggende pakketten en een statische import is voor statische leden.

Vraag 4 van 10

Een bestand importeert java.util.* en java.awt.* en declareert daarna List<String> names. Hoe kan het List naar de collectie-interface laten verwijzen terwijl beide jokerimports behouden blijven?
  1. Voeg import static java.util.List; toe.
  2. Vervang het type in de declaratie door Object.
  3. Voeg import java.util.List; toe.
  4. Verplaats beide jokerimports vóór de pakketdeclaratie.
Toon antwoordVerberg antwoord
Antwoord: C
Een expliciete import van één type heeft voorrang op jokerimports en laat List zo verwijzen naar java.util.List. De twee jokerimports kunnen blijven staan; een statische import zou dit type op het hoogste niveau niet als statisch lid identificeren.

Vraag 5 van 10

Een Jupiter-test roept assertTrue(valid) aan zonder de klassenaam Assertions ervoor. Welke import maakt die directe aanroep mogelijk?
  1. import org.junit.jupiter.api.Assertions.assertTrue;
  2. import static org.junit.jupiter.api.Test.assertTrue;
  3. import org.junit.jupiter.api.Assertions;
  4. import static org.junit.jupiter.api.Assertions.assertTrue;
Toon antwoordVerberg antwoord
Antwoord: D
assertTrue is een statisch lid van Assertions, dus een statische import maakt het lid direct aanroepbaar. Alleen de klasse importeren staat Assertions.assertTrue(...) toe, maar haalt de naam van de bijbehorende klasse niet uit de aanroep.

Vraag 6 van 10

book.data.Record is public en heeft een constructor die is gedeclareerd als Record(int id). Een niet-verwante klasse in book.ui importeert de klasse, maar kan geen object ervan construeren. Welke toegangswijziging maakt die aanroep mogelijk?
  1. Maak de constructor public.
  2. Maak de constructor protected.
  3. Importeer ook book.data.*.
  4. Hernoem het gebruikersinterfacepakket naar book.data.ui.
Toon antwoordVerberg antwoord
Antwoord: A
Door de ontbrekende modifier heeft de constructor pakkettoegang en book.ui is een ander pakket. Public toegang maakt deze aanroep door een niet-verwante klasse mogelijk; protected toegang niet, omdat de aanroeper zich niet in het declarerende pakket bevindt en geen rechten als subklasse gebruikt.

Vraag 7 van 10

Een gewone hulpklasse die is gedeclareerd als class RateTable heeft geen toegangsmodifier. Waar kunnen andere klassen op het hoogste niveau die gebruiken?
  1. In pakketten met dezelfde naam op het hoogste niveau.
  2. Alleen in precies het declarerende pakket.
  3. In elk pakket na een expliciete import.
  4. Alleen in subklassen, in elk pakket.
Toon antwoordVerberg antwoord
Antwoord: B
Een klasse op het hoogste niveau zonder public heeft pakkettoegang, waardoor het gebruik beperkt is tot precies hetzelfde pakket. Imports lossen namen op zonder toegang te verlenen, dus een expliciete import vanuit een ander pakket kan de toegang tot deze hulpklasse niet openen.

Vraag 8 van 10

Een klassekader bevat # threshold: int en een onderstreepte + reset(): void. Hoe worden die leden naar Java vertaald?
  1. Een privéveld voor een geheel getal en een publieke abstracte resetmethode.
  2. Een veld met pakkettoegang voor een geheel getal en een publieke statische resetmethode.
  3. Een protected veld voor een geheel getal en een publieke statische resetmethode.
  4. Een protected veld voor een geheel getal en een publieke resetinstantiemethode.
Toon antwoordVerberg antwoord
Antwoord: C
Het symbool # markeert protected zichtbaarheid, terwijl onderstrepen een statisch lid markeert. Het plusteken maakt reset public en het retourtype is void; onderstrepen betekent niet abstract, waarvoor cursief of een abstracte markering wordt gebruikt.

Vraag 9 van 10

Welke UML-operatie beschrijft public boolean accepts(int grams)?
  1. + boolean accepts(int: grams)
  2. - accepts(grams: int): boolean
  3. + accepts(grams: boolean): int
  4. + accepts(grams: int): boolean
Toon antwoordVerberg antwoord
Antwoord: D
UML houdt de operatienaam vooraan, schrijft elke parameter als naam gevolgd door type en zet het retourtype na de haakjes. Het plusteken behoudt de public zichtbaarheid; als je de typen van de parameter en het resultaat omdraait, beschrijf je een andere methode.

Vraag 10 van 10

Pakket base heeft de klasse Engine met pakkettoegang. De expliciet gedeclareerde constructor en de methode run() hebben beide geen toegangsmodifier. Een niet-verwante Screen in base.ui moet new Engine().run() aanroepen. Welke minimale zichtbaarheidswijzigingen maken die aanroep mogelijk?
  1. Maak Engine public; laat de rest pakkettoegang houden.
  2. Maak Engine en run public, maar de constructor niet.
  3. Maak Engine, de constructor en run public.
  4. Maak alleen de constructor en run public.
Toon antwoordVerberg antwoord
Antwoord: C
De gebruikersinterface moet toegang hebben tot het type, de constructor kunnen aanroepen en de methode kunnen uitvoeren. Alle drie hebben momenteel pakkettoegang en base.ui is een afzonderlijk pakket, dus alle drie hebben public toegang nodig voor deze niet-verwante aanroeper. Als je alleen het type en de methode toegankelijk maakt, blijft de constructie geblokkeerd.
Set 2: Klasserelaties en verantwoordelijkheden in lagen10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Bij een associatie tussen bibliotheek en boek staat 0..* naast Book. Met hoeveel boeken mag één bibliotheek verbonden zijn?
  1. Met minstens één boek.
  2. Met precies één boek.
  3. Met nul of meer boeken.
  4. Met nul of één boek.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Een cursus moet één of meer bijeenkomsten hebben. Het veld List<Session> begint leeg en geen enkele methode controleert de grootte. Wat ontbreekt in de implementatie?
  1. Controles die het minimumaantal bijeenkomsten vastleggen en behouden.
  2. Een andere lijstimport zodat de collectie een minimum afdwingt.
  3. Een statisch bijeenkomstveld dat elke cursusinstantie deelt.
  4. Een protected lijstveld dat bijeenkomsten direct kunnen vullen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Een mapmodel groepeert documenten die gedeeld mogen worden en blijven bestaan nadat de map is verwijderd. Ondersteunt de eis compositie van documenten door die map?
  1. Ja; elke collectie referenties legt samengesteld eigenaarschap vast.
  2. Ja; een map is het geheel, dus de levensduur van documenten telt niet.
  3. Nee; compositie is voorbehouden aan klassen zonder collectievelden.
  4. Nee; gedeelde zelfstandige documenten hebben geen samengesteld eigenaar.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Een klasse heeft een onderbroken UML-lijn die eindigt met een open driehoek bij Printable. Welke Java-subtypedeclaratie stelt die lijn voor?
  1. extends Printable
  2. implements Printable
  3. imports Printable
  4. new Printable()
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Basket bevat private List<Product> products; en breidt Product niet uit. Hoe moet de hoofdrelatie worden weergegeven?
  1. Een associatie naar Product, met een Product-uiteinde dat veel waarden toestaat.
  2. Een doorgetrokken generalisatielijn vanuit Basket met de driehoek bij Product.
  3. Een onderbroken realisatielijn vanuit Basket met de driehoek bij Product.
  4. Een navigeerbare associatie van Product naar Basket, met een Basket-uiteinde dat veel waarden toestaat.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een cijfercalculator berekent een gemiddelde en geeft dat weer. Een webinterface heeft de berekening nodig. Welke wijziging maakt die logica direct herbruikbaar?
  1. Kopieer de berekening naar de webinterface.
  2. Voeg een webuitvoerformaat toe aan de calculator.
  3. Geef het gemiddelde terug; laat interfaces het opmaken.
  4. Laat de calculator eerst de interfacenaam lezen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een tekstinterface wijst niet-numerieke invoer af voordat die een calculator aanroept. De calculator moet ook voor elke aanroeper negatieve numerieke waarden afwijzen. Waar hoort die regel thuis?
  1. Alleen in de tekstinterface, naast het parsen van getallen.
  2. In de calculator, waarbij de interface berichten verzorgt.
  3. In de main-methode, na het maken van de calculator.
  4. In de repository, na het opslaan van het berekende resultaat.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

De constructor van een service accepteert een interface CatalogRepository. Hoe kan een test de echte database vermijden?
  1. Geef een databasebestandsnaam aan de bestaande echte implementatie.
  2. Laat de serviceconstructor de databaserepository maken.
  3. Verplaats de service voor de test naar het presentatiepakket.
  4. Injecteer een CatalogRepository-implementatie in het geheugen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een boekingsscherm slaat rechtstreeks op via een repository. De service bevat een controle op het maximale aantal plaatsen en maakt zijn eigen bestandsrepository. Tests moeten die grens controleren zonder schijftoegang. Welke combinatie van wijzigingen ondersteunt zowel het scherm als de tests?
  1. Laat schermverzoeken via de service lopen en injecteer de repository daarin.
  2. Laat het scherm direct opslaan en injecteer de repository alleen in het scherm.
  3. Laat schermverzoeken via de service lopen en deel één statische bestandsrepository.
  4. Verplaats de plaatscontrole naar het scherm en laat de service bestanden blijven kiezen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

rules.OrderService importeert storage.OrderRepository. Die interface noemt rules.Order in haar signaturen en opslagimplementaties implementeren de interface. Hoe kan het contract worden verplaatst om de cyclische afhankelijkheid tussen de pakketten rules en storage op te heffen en tegelijk uitwisselbare opslag te behouden?
  1. Verplaats Order naar storage; OrderService importeert een concrete opslagklasse.
  2. Verplaats de interface naar rules; storage implementeert dat contract.
  3. Verplaats OrderRepository naar de gebruikersinterface; beide lagere lagen importeren die.
  4. Behoud de indeling; vervang de imports door volledig gekwalificeerde namen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 3: Asserties, gevalskeuze en onafhankelijke tests10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Een methode geeft actual terug en het vereiste resultaat is 42. Welke Jupiter-gelijkheidsassertie geeft de waarden de juiste labels?
  1. assertEquals(actual, 42);
  2. assertTrue(actual);
  3. assertNotNull(42);
  4. assertEquals(42, actual);
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Een correcte double-berekening wijkt ongeveer 0,0000000000000001 af van 0,6. De vereiste numerieke nauwkeurigheid is 0,000001. Hoe moet het verwachte resultaat worden gecontroleerd?
  1. assertEquals(0.6, actual);
  2. assertEquals(0.6, actual, 1.0);
  3. assertEquals(0.6, actual, 1e-6);
  4. assertNotNull(actual);
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Een grotteregel accepteert gehele getallen van 1 tot en met 500. Is 0 een geldige invoer, een geldige invoer op de grens of een ongeldige invoer?
  1. Een ongeldige invoer.
  2. Een geldige invoer op de grens.
  3. Een geldige invoer.
  4. Een niet-gespecificeerde invoer.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Een sensor accepteert gehele meetwaarden tot en met 800. Een ontwikkelaar verandert de bovenste controle van reading <= 800 in reading < 800. Welke geldige meetwaarde legt de fout bloot?
  1. 799
  2. 800
  3. 801
  4. 400
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Een methode moet een lege string afwijzen met IllegalArgumentException. Hoe moet een Jupiter-test de aanroep uitvoeren?
  1. assertThrows(IllegalArgumentException.class, () -> parser.parse(""));
  2. assertThrows(IllegalArgumentException.class, parser.parse(""));
  3. assertEquals(IllegalArgumentException.class, parser.parse(""));
  4. assertNotNull(() -> parser.parse(""));
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een tarief rekent 250 cent tot en met 3.000 g en daarna 50 cent per begonnen 500 g daarboven. De implementatie gebruikt 250 + ((grams - 3000) / 500) * 50 boven de grens. Welke nieuwe test legt de afrondingsfout het directst bloot?
  1. 3.500 g moet 300 cent kosten.
  2. 4.000 g moet 350 cent kosten.
  3. 3.001 g moet 300 cent kosten.
  4. 3.000 g moet 250 cent kosten.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Voor elke test wordt een nieuwe instantie van de testklasse gemaakt, maar een statische veranderlijke lijst behoudt elementen uit een eerdere test. Welke opzet geeft elk scenario een eigen lijst?
  1. Hernoem de testmethoden om een alfabetische volgorde af te dwingen.
  2. Gebruik een niet-statische lijst die voor elke test wordt geïnitialiseerd.
  3. Maak één lijst in een statische BeforeAll-methode.
  4. Behoud de statische lijst en voeg DisplayName-labels toe.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een kortingstest leest de huidige datum en gebruikt een statische repository met eerdere klanten. De test slaagt afzonderlijk, maar geeft verschillende resultaten bij uitvoeringen van de testverzameling. Hoe moet de opzet worden aangepast?
  1. Behoud de huidige datum en voer de test in elke verzameling als eerste uit.
  2. Gebruik een vaste datum en hergebruik de gedeelde gevulde repository.
  3. Maak alleen de service opnieuw en laat beide externe invoerbronnen ongewijzigd.
  4. Lever voor elk scenario een vaste datum en een nieuwe repository.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een tariefregel heeft acht rijen met invoer en verwacht resultaat die dezelfde assertie gebruiken. Hoe kan Jupiter de gevallen afzonderlijk blijven rapporteren zonder acht gekopieerde methodebody’s?
  1. Gebruik een geparametriseerde test met de rijen als argumentbron.
  2. Gebruik één test die elk resultaat weergeeft maar alleen het eerste controleert.
  3. Gebruik BeforeAll om de tariefberekening één keer voor de hele klasse uit te voeren.
  4. Gebruik Disabled op de herhaalde methoden en behoud één actieve methode.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Alle huidige tests slagen. Je wilt een hulpmethode hernoemen en herhaalde logica afsplitsen zonder uitvoer te veranderen. Welke TDD-activiteit is dit?
  1. Een nieuw rood geval voor andere functionaliteit toevoegen.
  2. Verwachte resultaten aanpassen aan de uitvoer van de hulpmethode.
  3. Refactoren terwijl je de geslaagde tests opnieuw uitvoert.
  4. Eerdere tests overslaan totdat ander gedrag is geïmplementeerd.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 4: Buildconfiguratie en foutrapporten10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Waar plaatst een standaard Maven-project Java-bronbestanden voor unittests?
  1. src/test/java
  2. src/main/java
  3. target/test-classes
  4. src/test/resources
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Een project gebruikt JUnit alleen in tests. Welk Maven-afhankelijkheidsbereik houdt JUnit beschikbaar voor het compileren en uitvoeren van tests zonder het aan de klassenpaden van de hoofdcode toe te voegen?
  1. compile
  2. test
  3. runtime
  4. provided
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Wat gebeurt er bij een normale Maven-levenscyclus zonder ingestelde overslagen voordat de fase package een JAR-bestand maakt?
  1. Alleen de hoofdcode compileert; tests worden afzonderlijk uitgevoerd.
  2. Een ouder geïnstalleerd JAR-bestand wordt naar de bronbestanden gekopieerd.
  3. Hoofd- en testcode compileren en de tests worden uitgevoerd.
  4. De levenscyclus clean verwijdert altijd eerst de uitvoer.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Een Maven-project met Java 17 declareert alleen JUnit Jupiter 6.1.3, maar configureert Surefire 2.12.4. Het testbestand TotalsChecks.java importeert org.junit.Test. De opnamepatronen van Surefire zijn ongewijzigd. Welke combinatie van correcties laat de klasse compileren en als ontdekte Jupiter-test uitvoeren?
  1. Hernoem de klasse naar TotalsTest en gebruik Jupiter @Test, maar behoud Surefire 2.12.4.
  2. Behoud TotalsChecks, gebruik Jupiter @Test en kies Surefire 3.5.4.
  3. Hernoem de klasse naar TotalsTest en kies Surefire 3.5.4, maar behoud org.junit.Test.
  4. Hernoem de klasse naar TotalsTest, gebruik Jupiter @Test en kies Surefire 3.5.4.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Wat is de gebruikelijke bestandsnaam voor een Gradle-buildscript dat in Kotlin is geschreven?
  1. settings.gradle.kts
  2. build.gradle.kts
  3. build.gradle
  4. pom.xml
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een Gradle-project heeft Jupiter-tests en declareert de bijbehorende bibliotheken. Welke aanroep binnen de testtaak kiest het platform waarop die tests worden uitgevoerd?
  1. mavenCentral()
  2. testImplementation()
  3. mainClass.set()
  4. useJUnitPlatform()
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een JUnit-BOM wordt gedeclareerd met Gradles platform(...). Waarom kunnen declaraties van bijbehorende JUnit-afhankelijkheden afzonderlijke versienummers weglaten?
  1. De Java-compiler leidt de nieuwste geïnstalleerde versie van elke module af.
  2. De wrapper vervangt elke afhankelijkheidsversie door zijn Gradle-versie.
  3. De BOM levert op elkaar afgestemde versiebeperkingen voor de JUnit-modules.
  4. De testtaak downloadt elke module waarvan een klassenaam in een test staat.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een Gradle-project kan een test niet compileren omdat org.junit.jupiter.api ontbreekt. De enige Jupiter-declaratie is testRuntimeOnly("org.junit.jupiter:junit-jupiter") en de testtaak gebruikt al het JUnit Platform. Welke configuratiecorrectie verhelpt het probleem in deze fase?
  1. Declareer Jupiter met testImplementation zodat de testcompilatie de API kan zien.
  2. Voeg nog een useJUnitPlatform-aanroep toe zonder de afhankelijkheid te wijzigen.
  3. Verplaats de test naar de hoofdbroncode en laat Jupiter alleen voor runtime staan.
  4. Behoud Jupiter alleen voor runtime en voeg een DisplayName-annotatie toe aan de test.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een servicetest hergebruikt een statische bestandsrepository. De test slaagt in de IDE na een reset, maar mislukt tijdens de volledige Gradle-testverzameling omdat eerdere tests extra records hebben opgeslagen. De service accepteert al een repository-interface. Welke correctie isoleert het gedrag van de service?
  1. Injecteer een nieuwe repository in het geheugen met alleen de records van deze test.
  2. Gebruik BeforeAll om dezelfde bestandsrepository in de hele testverzameling te delen.
  3. Voer Gradle clean uit en behoud de aannames over gegevens in de gedeelde repository.
  4. Maak de test public en vertrouw op de huidige uitvoeringsvolgorde van de volledige verzameling.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een nieuwe TDD-controle slaagt onmiddellijk, hoewel je wilde dat die ontbrekend gedrag blootlegde. Wat is het juiste volgende onderzoek?
  1. Voeg productiecode toe totdat de geslaagde test begint te mislukken.
  2. Controleer of de test draait en het bedoelde resultaat controleert, en of de code al aan het geval voldoet.
  3. Verwijder de assertie omdat een geslaagd resultaat bewijst dat het nieuwe gedrag ontbreekt.
  4. Refactor niet-verwante klassen voordat je controleert welke methode de test aanroept.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 5: Onderzoek een volledige Java-wijziging10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Een uitgevoerde test meldt “expected: <18> but was: <21>” bij een gelijkheidsassertie met de juiste argumentvolgorde. Welke waarde gaf de methode terug?
  1. 18
  2. 3
  3. 21
  4. 39
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Een associatie toont 1 naast Department en 0..* naast Employee. Wat staat het model toe voor één afdeling en wat vereist het voor één werknemer?
  1. Een afdeling vereist één werknemer; elke werknemer mag geen afdeling hebben.
  2. Een afdeling mag geen werknemers hebben; elke werknemer heeft precies één afdeling.
  3. Een afdeling vereist veel werknemers; elke werknemer heeft precies één afdeling.
  4. Een afdeling mag geen werknemers hebben; elke werknemer mag veel afdelingen hebben.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Een methode geeft een lijst met drie opgegeven waarden terug, maar het interne collectietype kan veranderen. Hoe moet de test het resultaat controleren?
  1. Lees de private onderliggende array en controleer de exacte capaciteit met een assertie.
  2. Controleer met een assertie of de concrete collectieklasse overeenkomt met de huidige.
  3. Geef de elementen weer en laat de testuitvoerder ze automatisch vergelijken.
  4. Vergelijk de teruggegeven inhoud met de drie vereiste waarden.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Klasse base.Item heeft een protected instantieveld weight. In een ander pakket heeft Special extends Item een instantiemethode met parameters Item a en Special b. Welke expressies kunnen binnen die methode rechtstreeks toegang krijgen tot het overgeërfde veld?
  1. this.weight en b.weight
  2. this.weight en a.weight
  3. a.weight en b.weight
  4. this.weight, a.weight en b.weight
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Een diagram toont dat MemoryArchive de interface Archive realiseert en via een navigeerbare associatie één of meer Entry-objecten bevat. Een gegenereerde klasse vermeldt momenteel extends Archive en maakt een lege lijst zonder controles. Welke combinatie van correcties behoudt het model?
  1. Gebruik implements Archive maar behoud de ongecontroleerde lege lijst, want List dwingt één of meer af.
  2. Gebruik implements Archive en wijs lege constructorinvoer af, maar sta verwijdering van het laatste element toe.
  3. Gebruik implements Archive en dwing bij constructie en wijzigingen een niet-lege collectie elementen af.
  4. Gebruik implements Archive en vervang de collectie door één optionele Entry-referentie.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een rapportageservice importeert opmaakklassen van de gebruikersinterface en maakt zijn eigen bestandsopslag. Een tweede interface en tests zonder schijftoegang hebben de berekening nodig. Welke ontwerpwijziging pakt beide afhankelijkheden aan?
  1. Geef opgemaakte consoletekst terug en laat de service de bestandsopslag blijven maken.
  2. Verplaats de bestandsopslag naar het interfacepakket en behoud de opmaakaanroepen in de service.
  3. Maak de opmaakklasse en bestandsopslag van de service statisch zodat beide interfaces ze kunnen delen.
  4. Geef rapportgegevens terug, houd de opmaak in de interfaces en injecteer een opslagcontract.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een schrikkeljaarcontrole die met TDD wordt ontwikkeld, geeft momenteel year % 4 == 0 terug en slaagt voor tests met 2031 en 2032. Een nieuwe test voor 2100 moet dat jaar afwijzen en een latere test voor 2400 moet het accepteren. Welke volgorde van gedragswijzigingen past bij de openstaande gevallen?
  1. Sluit voor 2100 jaren uit die deelbaar zijn door 400 en sta voor 2400 weer jaren toe die deelbaar zijn door 100.
  2. Sluit voor 2100 jaren uit die deelbaar zijn door 100 en sta voor 2400 weer jaren toe die deelbaar zijn door 400.
  3. Sluit eerst jaren uit die deelbaar zijn door 100 en vereis daarna voor elk schrikkeljaar deelbaarheid door 400.
  4. Sluit eerst jaren uit die deelbaar zijn door 100 en sta daarna veelvouden van 400 alleen toe als ze niet deelbaar zijn door 100.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een verpakkingsregel wijst niet-positieve gewichten af, rekent 400 cent tot en met 2.500 g en daarboven 700 cent. De tests dekken alleen 1.000 g. Door een wijziging wordt nul per ongeluk geaccepteerd en wordt < 2500 voor de lagere tariefgroep gebruikt. Welk extra paar controles legt beide fouten bloot?
  1. Nul moet een exceptie opwerpen; 2.500 g moet 400 cent teruggeven.
  2. Nul moet een exceptie opwerpen; 2.501 g moet 700 cent teruggeven.
  3. 1 g moet 400 cent teruggeven; 2.501 g moet 700 cent teruggeven.
  4. −1 g moet een exceptie opwerpen; 2.499 g moet 400 cent teruggeven.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een Gradle-script bevat testRuntimeOnly("org.junit.jupiter:junit-jupiter:6.1.3") en tasks.test { }. Een Jupiter-testklasse staat onder src/main/java en de annotatie compileert niet. Met welke wijzigingen kan de klasse compileren en onder Jupiter worden uitgevoerd?
  1. Houd de klasse in de hoofdbroncode, gebruik testImplementation voor Jupiter en kies het JUnit Platform.
  2. Verplaats de klasse naar de testbroncode, behoud Jupiter alleen voor runtime en kies het JUnit Platform.
  3. Verplaats de klasse naar de testbroncode, gebruik testImplementation voor Jupiter en behoud de standaardtestuitvoerder.
  4. Verplaats de klasse naar de testbroncode, gebruik testImplementation voor Jupiter en kies het JUnit Platform.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een normale Maven-uitvoering van package stopt bij een mislukte test, maar een JAR-bestand van gisteren blijft in target staan. Wat bewijst dat bestand over de broncode van vandaag?
  1. Het bewijst dat de broncode van vandaag de tests heeft doorstaan.
  2. Het bewijst dat het verpakken de mislukking heeft genegeerd.
  3. Het bewijst alleen dat een ouder artefact bestaat.
  4. Het bewijst dat Maven de broncode van gisteren opnieuw heeft gebouwd.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Kom je niet uit je eigen opgave? Huiswerkhulp

Kernbegrippen

73 Kernbegrippen, elk met een definitie

A

Afhankelijkheid
Een afhankelijkheid is een gebruiksrelatie waarbij één element afhankelijk is van een ander, getekend als een onderbroken lijn met een open pijlpunt.
Een tariefoperatie met een parameter Parcel creëert een afhankelijkheid van de definitie van het type voor een verzendpakket.
Afhankelijkheidsbereik
Een afhankelijkheidsbereik bepaalt welke Maven-klassenpaden een bibliotheek bevatten en scheidt vereisten voor alleen tests van vereisten voor het hoofdprogramma.
Testbereik maakt JUnit beschikbaar onder src/test/java zonder er een afhankelijkheid van de hoofdcode van te maken.
Afhankelijkheidsconfiguratie
Een afhankelijkheidsconfiguratie bepaalt waar een Gradle-afhankelijkheid beschikbaar is, zoals voor de hoofdimplementatie, testcompilatie of testuitvoering.
Een platformlauncher kan testRuntimeOnly gebruiken omdat testbronbestanden die tijdens de compilatie niet nodig hebben.
Afhankelijkheidsinjectie
Afhankelijkheidsinjectie levert de samenwerkende objecten van een object van buitenaf aan, zodat de aanroeper hun implementaties of invoer kan kiezen.
Door een repository aan de serviceconstructor door te geven, kan de test beheerste opslag kiezen.
Aggregatie
Aggregatie is een losse geheel-deelgroepering, weergegeven met een open ruit, waarvan de gedetailleerde betekenis afhangt van het gemodelleerde ontwerp.
Een team kan spelers groeperen die ook zelfstandig bestaan, zonder hun levensduur te bepalen.
Applicatielogica
Applicatielogica is de code die de regels en bewerkingen van een programma op gegevens implementeert, onafhankelijk van de dialoog met de gebruiker.
Een tariefcalculator kiest de juiste tariefgroep zonder om invoer te vragen of naar de console te schrijven.
Artefactcoördinaten
Artefactcoördinaten identificeren een Maven-project of bibliotheek met de groep, artefactnaam en versie, zodat de build het bedoelde artefact kan vinden.
De coördinaten onderscheiden de ene versie van een JUnit-bibliotheek van een andere zonder JAR-bestanden met de hand te kopiëren.
Assertie
Een assertie in een test is een uitvoerbare controle die de test laat mislukken wanneer het waargenomen resultaat niet aan de opgegeven verwachting voldoet.
Een gelijkheidsassertie laat een test bij een verkeerd tarief automatisch mislukken in plaats van het tarief alleen in de uitvoer achter te laten.
Associatie
Een associatie is een structurele relatie die objecten van verschillende typen verbindt en vaak wordt geïmplementeerd met opgeslagen referentievelden.
De pakketlijst van een zending kan worden getekend als een associatie met de rolnaam parcels aan de kant van het pakket.
Attribuut
Een attribuut is een benoemde eigenschap in een UML-klassekader, die vaak overeenkomt met een Java-veld met een type en zichtbaarheid.
De vermelding - weightGrams: int stelt een privéveld voor een geheel getal voor dat het gewicht van het pakket bevat.

B

Beschermde toegang
Beschermde toegang staat gebruik binnen het declarerende pakket toe en beperkte toegang via overerving door subklassen in andere pakketten.
Een subklasse in een ander pakket kan haar eigen overgeërfde protected veld lezen zonder dat veld public te maken.
Bill of materials
Een bill of materials is afhankelijkheidsmetadata die verwante moduleversies op elkaar afstemt, zodat een build een familie van bibliotheken consistent kan houden.
De JUnit-BOM levert bijpassende moduleversies nadat één gemeenschappelijke versie is gekozen.
Broncodemap
Een broncodemap is de map waaronder Java-pakketpaden beginnen, zoals src/main/java voor productiecode in een standaardbuild.
Voor parcels.domain.Parcel is het pad onder de broncodemap parcels/domain/Parcel.java.
Buildlevenscyclus
Een buildlevenscyclus is de geordende reeks fasen van Maven, waarbij het aanvragen van een latere fase de eerdere fasen in die levenscyclus uitvoert.
Als je package aanvraagt, wordt normaal gesproken de testfase uitgevoerd voordat het verpakte artefact van het project wordt gemaakt.
Buildtool
Een buildtool automatiseert projecttaken zoals afhankelijkheden oplossen, broncode compileren, tests uitvoeren en artefacten verpakken.
Eén Maven-opdracht kan een pakketproject compileren, de tests uitvoeren en het resulterende JAR-bestand maken.

C

Compositie
Compositie is een geheel-deelassociatie waarbij een deel tegelijk hoogstens één samengesteld eigenaar heeft, weergegeven met een gevulde ruit bij het geheel.
Een bestelling en de bestelregels kunnen compositie gebruiken wanneer het verwijderen van de bestelling ook die delen verwijdert.
Cyclische pakketafhankelijkheid
Een cyclische pakketafhankelijkheid bestaat wanneer een keten van typeafhankelijkheden vanuit een pakket terugleidt naar datzelfde pakket en de definities van de betrokken pakketten in beide richtingen verbindt.
Door de repository-interface naast domeintypen te plaatsen, kun je een importcyclus tussen domein en gegevens verwijderen.

E

Equivalentieklasse
Een equivalentieklasse bij testen is een groep invoerwaarden die de specificatie gelijk behandelt en die wordt vertegenwoordigd door nuttige voorbeeldgevallen en grenzen.
Meerdere positieve gewichten in dezelfde gewichtsklasse hebben hetzelfde tarief, terwijl nul tot een klasse met ongeldige invoer behoort.
Exceptieassertie
Een exceptieassertie voert een aangeleverde operatie uit en controleert of die het vereiste exceptietype of een geaccepteerd subtype daarvan opwerpt.
assertThrows kan controleren of een gewicht van nul wordt afgewezen en de exceptie teruggeven om het bericht te controleren.

G

Gebruikersinterface
Een gebruikersinterface is het deel van een programma dat acties van gebruikers ontvangt en resultaten presenteert, zoals een dialoog met tekstopdrachten.
Een tekstinterface voor een tariefcalculator vraagt om grammen, leest het antwoord, roept de calculator aan en geeft het teruggegeven tarief weer.
Gegevenstoegangslaag
De gegevenstoegangslaag implementeert het opslaan en ophalen van persistente gegevens en houdt opslagdetails gescheiden van applicatieregels.
Een bestandsrepository verzorgt het bestandsformaat, terwijl een pakketservice geldige pakketgewichten afhandelt.
Generalisatie
Generalisatie is een UML-subtyperelatie die wordt getekend met een doorgetrokken lijn en een open driehoek die naar het algemenere type wijst.
Een lijn van Letter naar Item wordt vertaald naar een Java-declaratie waarin Letter de klasse Item uitbreidt.
Geparametriseerde test
Een geparametriseerde test voert één testmethode uit voor meerdere aangeleverde argumentverzamelingen en rapporteert elke aanroep als een afzonderlijk geval.
Een tabel met gewichten en tarieven kan meerdere grenzen controleren zonder de body van de assertiemethode te herhalen.
Gradle-taak
Een Gradle-taak is een eenheid buildwerk, zoals compilatie of testen, die afhankelijk kan zijn van andere taken en ongewijzigde resultaten kan hergebruiken.
Als je de buildtaak uitvoert, worden ook de compilatie- en testwerkzaamheden opgenomen die voor die build nodig zijn.
Grensgeval
Een grensgeval gebruikt invoer op of naast de grens van een gespecificeerde regel, waar een kleine wijziging het verwachte gedrag kan veranderen.
Als een tariefgroep eindigt bij 2.000 g, legt een test met precies 2.000 g een onjuist strikte vergelijking bloot die een test met 1.500 g zou missen.

I

Import van één type
Een import van één type maakt één toegankelijk type onder de eenvoudige naam beschikbaar en vermeldt expliciet welk pakket het type levert.
De declaratie import java.util.ArrayList; geeft precies aan welke ArrayList het bestand gebruikt.
Importdeclaratie
Met een importdeclaratie kan één Java-bronbestand een type of statisch lid met een kortere naam gebruiken zonder de toegankelijkheid ervan te veranderen.
Een gebruikersinterfaceklasse die Scanner importeert, kan de eenvoudige naam in de velddeclaratie gebruiken in plaats van java.util.Scanner.
Instabiele test
Een instabiele test geeft wisselende resultaten zonder relevante codewijziging, vaak doordat tijd, gedeelde toestand of externe gegevens niet worden beheerst.
Een test die de huidige datum leest, kan een andere uitkomst geven wanneer de kalender ’s nachts een dag verder gaat.
Interfacecontract
Een interfacecontract specificeert operaties die een implementatie aanbiedt, zodat aanroepers het interfacetype kunnen gebruiken zonder de implementatie te kiezen.
Een service kan save via het repositorycontract aanroepen, ongeacht of de opslag geheugen of een bestand gebruikt.

J

Jokerimport
Een jokerimport maakt toegankelijke typen in één pakket op aanvraag beschikbaar, zonder typen in onderliggende pakketten beschikbaar te maken.
Na het importeren van java.util.* heeft java.util.function.Function nog steeds een eigen import of volledige naam nodig.
JUnit Jupiter
JUnit Jupiter is het JUnit-programmeermodel en de testengine die moderne Java-testannotaties, asserties en uitbreidingen biedt.
Door de @Test van Jupiter te gebruiken, blijft de testannotatie in overeenstemming met de engine die de build heeft gekozen.
JUnit Platform
Het JUnit Platform is de basis die ondersteunde testengines ontdekt en start, waaronder de engine voor Jupiter-tests.
Gradle kiest het platform met useJUnitPlatform() wanneer de tests van het project Jupiter-annotaties gebruiken.

L

Laag
Een laag groepeert klassen met een gemeenschappelijk soort verantwoordelijkheid en biedt via een vastgelegde grens operaties aan andere onderdelen.
Een pakketservice past validatie toe voordat die een opslaglaag vraagt een nieuw pakket op te slaan.
Lambda-expressie
Een lambda-expressie levert een kleine operatie als waarde, zodat andere code kan bepalen wanneer en hoe die operatie wordt uitgevoerd.
Met de lambda () -> calculator.feeInCents(0) kan JUnit de ongeldige aanroep binnen de assertie uitvoeren.
Levenscyclusmethode
Een levenscyclusmethode is een methode die het testframework op een vastgelegd moment uitvoert, zoals vóór elke test of één keer vóór de hele klasse.
@BeforeEach bouwt veranderlijke objecten opnieuw op, terwijl @AfterEach tijdelijke testbronnen kan verwijderen.

M

Multipliciteit
Multipliciteit geeft het toegestane aantal verbonden objecten aan één uiteinde van een relatie aan voor één object aan het tegenoverliggende uiteinde.
Als 1..* naast Parcel staat, moet elke zending minstens één verbonden pakket behouden.

N

Naamconflict
Een naamconflict ontstaat wanneer een eenvoudige naam naar tegenstrijdige declaraties kan verwijzen, zodat de code het bedoelde type moet onderscheiden.
Een bestand dat twee typen met de naam List nodig heeft, kan het ene importeren en voor het andere de volledig gekwalificeerde naam gebruiken.
Naamloos pakket
Het naamloze pakket bevat typen uit bronbestanden zonder pakketdeclaratie; benoemde pakketten kunnen die typen niet importeren.
Als je gedeelde hulpklassen naar een benoemd pakket verplaatst, kunnen de andere benoemde onderdelen van een project ernaar verwijzen.
Navigeerbaarheid
Navigeerbaarheid beschrijft of een object een relatie naar een ander object kan volgen en wordt vaak met een open pijlpunt getoond.
Een pijl van zending naar pakket toont dat een zending haar pakketten via referenties kan bereiken.

O

Operatie
Een operatie is gedrag dat in een UML-type wordt gedeclareerd en wordt beschreven door de naam, parametertypen, zichtbaarheid en het retourtype.
De operatie + totalWeightGrams(): int beschrijft een publieke methode zonder het algoritme te geven.

P

Pakket
Een pakket is een benoemde groep Java-typen die verwante code ordent, typenamen scheidt en een toegangsgrens biedt.
Afzonderlijke pakketten voor gebruikersinterface en domein houden de afhandeling van opdrachten gescheiden van de klassen die pakkettarieven berekenen.
Pakketdeclaratie
Een pakketdeclaratie wijst de typen in een bronbestand toe aan een benoemd pakket en staat vóór de imports en klassedeclaraties.
Als je package com.example.parcels.domain; schrijft, wordt dat pakket onderdeel van de volledige naam van de klasse.
Pakketgroepering
Een pakketgroepering is een mapvormig gebied in een UML-diagram dat typekaders ordent volgens het pakket waartoe ze behoren.
Door gebruikersinterface- en domeinklassen afzonderlijk te groeperen, kun je verwijzingen tussen die gebieden gemakkelijker onderzoeken.
Pakkettoegang
Pakkettoegang staat toe dat een gewoon klasselid of type op het hoogste niveau alleen vanuit precies het declarerende pakket wordt gebruikt.
Een hulpklasse zonder public modifier blijft ontoegankelijk voor een gebruikersinterfaceklasse in een naburig pakket, ook met een import.
Plug-in
Een plug-in voegt buildmogelijkheden en configuratie toe en levert vaak taken en conventies voor een bepaald soort project.
De application-plug-in van Gradle voegt een run-taak toe voor de geconfigureerde main-klasse van het pakketprogramma.
Presentatielaag
De presentatielaag vertaalt gebruikersinteractie naar verzoeken aan het programma en presenteert de teruggegeven resultaten aan de gebruiker.
Een tekstinterface kan een tarief in een zin opmaken zonder die zin in de code te plaatsen die het tarief berekent.
Publieke interface
De publieke interface van een pakket is de verzameling toegankelijke typen en operaties die het pakket bewust aan code buiten het pakket aanbiedt.
Een domeinpakket kan een verzendcalculator beschikbaar maken terwijl het de hulpklasse verbergt die de gewichtsklassen opslaat.

R

Realisatie
Realisatie is een relatie waarin een type aan een interfacecontract voldoet, weergegeven met een onderbroken lijn en een open driehoek.
Een repository in het geheugen realiseert dezelfde repository-interface als een repository die bestanden gebruikt.
Refactoring
Refactoring verandert de interne organisatie van code met behoud van waarneembaar gedrag, bijvoorbeeld door een methode te hernoemen of af te splitsen.
Een tariefhulpmethode naar een beter pakket verplaatsen is refactoring als de calculator nog dezelfde resultaten geeft.
Regressie
Een regressie is een fout die is ontstaan in gedrag dat eerder werkte, vaak na een wijziging in een ander deel van de code.
Een bestaande test van een tariefgrens vangt een regressie wanneer een inclusieve vergelijking in een strikte vergelijking verandert.
Repository
Een repository biedt operaties voor het opslaan en ophalen van domeinobjecten achter een contract dat verschillende implementaties kan hebben.
Een test levert een repository in het geheugen om afhankelijkheid van een echt pakketgegevensbestand te vermijden.
Rood–groen–refactoren
Rood–groen–refactoren is de TDD-cyclus waarin je ontbrekend gedrag blootlegt, alle huidige tests laat slagen en de structuur met behoud van gedrag verbetert.
Nadat het nieuwe tariefgeval slaagt, hoort het afsplitsen van herhaalde berekeningscode bij de refactorstap.

S

Samenstelling van objecten
Samenstelling van objecten is het maken en verbinden van samenwerkende programmaobjecten, meestal in de buurt van het startpunt van de applicatie.
Een main-methode maakt één scanner en een calculator, geeft die door aan een gebruikersinterfaceobject en start de dialoog.
Scheiding van verantwoordelijkheden
Scheiding van verantwoordelijkheden wijst verschillende taken toe aan verschillende delen van een programma, zodat elk deel gemakkelijker te begrijpen, te wijzigen en te testen is.
Een wijziging in de formulering van een vraag zou geen aanpassing van de methode moeten vereisen die het tarief van een pakket berekent.
Statisch lid
Een statisch lid hoort bij een klasse in plaats van bij een afzonderlijk object en wordt getoond door de UML-vermelding van het attribuut of de operatie te onderstrepen.
Door een constante voor het maximumgewicht te onderstrepen, onderscheid je één gedeelde klassewaarde van het gewicht van elk pakket.
Statische import
Met een statische import kan een bronbestand een toegankelijk statisch lid direct noemen, zonder de naam van de klasse die het declareert.
JUnit-tests importeren assertEquals vaak statisch, zodat elke controle leest als een directe assertieaanroep.
Superklasse
Een superklasse is de klasse waarvan een subklasse leden en gedrag erft; UML plaatst de generalisatiedriehoek bij deze klasse.
Zowel brieven als pakketten kunnen gemeenschappelijk itemgedrag erven zonder dat in elk subklassekader te herhalen.
Surefire
Surefire is de Maven-plug-in die tijdens de testfase unittests uitvoert met geconfigureerde testframeworks en detectieregels.
Een testklasse met de naam ShippingCalculatorTest voldoet aan een standaard opnamepatroon van Surefire.

T

Testgestuurde ontwikkeling
Testgestuurde ontwikkeling is een ontwikkelmethode die een mislukte test gebruikt om een kleine gedragswijziging te sturen voordat de structuur van de code wordt verbeterd.
Een mislukt geval met een eeuwjaar stuurt de volgende wijziging aan een schrikkeljaarcontrole in plaats van een grote herschrijving.
Testlijst
Een testlijst legt gedrag en gevallen vast die nog moeten worden gedekt, zodat de ontwikkelaar één kleine volgende stap kan kiezen en ontdekkingen waar nodig kan toevoegen.
Een lijst voor schrikkeljaren bevat gewone jaren, jaren die deelbaar zijn door vier en de twee gevallen met eeuwjaren.
Testonafhankelijkheid
Testonafhankelijkheid betekent dat elke test de eigen benodigde toestand verzorgt en correct kan slagen of mislukken wanneer die afzonderlijk of in willekeurige volgorde wordt uitgevoerd.
Een test van het aantal maakt eigen pakketten in plaats van te vertrouwen op een eerdere methode die ze toevoegt.
Testopstelling
Een testopstelling is de verzameling objecten en gegevens die voor een test is voorbereid en de beheerste begintoestand biedt die het scenario nodig heeft.
Een nieuwe lege zending vóór elke test voorkomt dat toegevoegde pakketten naar een ander scenario doorlekken.
Toegangsmodifier
Een toegangsmodifier is een sleutelwoord dat bepaalt welke code een declaratie mag gebruiken, zoals een klasse, constructor, veld of methode.
Een publieke klasse kan toch een constructor hebben die een gebruikersinterfaceklasse in een ander pakket niet kan aanroepen.
Tolerantie
Een tolerantie is een toegestaan numeriek verschil dat een assertie gebruikt wanneer exacte gelijkheid ongeschikt is voor een berekening met zwevendekommagetallen.
Een kleine onderbouwde tolerantie accepteert een afrondingsverschil zonder een wezenlijk verkeerd resultaat te accepteren.
Transitieve afhankelijkheid
Een transitieve afhankelijkheid is een bibliotheek die een andere afhankelijkheid nodig heeft en die de buildtool samen met het rechtstreeks aangevraagde artefact ophaalt.
De declaratie van de Jupiter-verzamelafhankelijkheid haalt ook de modules binnen die nodig zijn voor de testfunctionaliteit.

U

UML-klassendiagram
Een UML-klassendiagram modelleert de statische softwarestructuur met typekaders, attributen, operaties en relaties tussen typen.
Een diagram van het pakketprogramma toont wat een zending opslaat zonder de volgorde te volgen van opdrachten die een gebruiker invoert.
Unittest
Een unittest is een automatische controle van een kleine eenheid code die waargenomen gedrag vergelijkt met een onafhankelijk gespecificeerde uitkomst.
Een calculatortest roept de tariefmethode aan en controleert het verwachte aantal centen met een assertie, zonder de gebruikersinterface te starten.

V

Vereenvoudigde testimplementatie
Een vereenvoudigde testimplementatie is een eenvoudige werkende implementatie van een afhankelijkheid voor tests, die bruikbaar gedrag biedt met beheerste middelen.
Een repository in het geheugen slaat pakketten op voor servicetests zonder een echt bestand te openen of te wijzigen.
Verwachte uitkomst
Een verwachte uitkomst is het resultaat of gedrag dat de specificatie vereist, bepaald onafhankelijk van de code die wordt getest.
Wanneer voor zware pakketten per begonnen gewichtsblok wordt gerekend, komt het verwachte tarief uit het tellen van blokken, niet uit het kopiëren van de uitvoer van de methode.
Volledig gekwalificeerde naam
Een volledig gekwalificeerde naam identificeert een Java-type door de pakketnaam met de eenvoudige naam te combineren en neemt zo dubbelzinnigheid tussen pakketten weg.
Met java.util.List onderscheid je de collectie-interface van een ander type dat ook List heet.
Voorbereiden–uitvoeren–controleren
Voorbereiden–uitvoeren–controleren is een patroon voor het schrijven van tests dat het voorbereiden van een scenario, het uitvoeren van de actie en het controleren van de uitkomst scheidt.
Een zending maken, één pakket toevoegen en het aantal controleren geven de test drie duidelijke delen.

W

Wrapper
Een wrapper is een met het project meegeleverde starter die de door het project gekozen buildtoolversie ophaalt en uitvoert voor herhaalbare opdrachten.
Met een in versiebeheer opgenomen Maven-wrapper kunnen bijdragers de opgegeven Maven-versie uitvoeren met ./mvnw test.

Z

Zichtbaarheidssymbool
Een zichtbaarheidssymbool is een UML-teken vóór een lid dat de toegang markeert: + public, - private, # protected of ~ pakkettoegang.
Een veld dat met - wordt getoond, moet private zichtbaarheid behouden wanneer het diagram naar Java wordt vertaald.

Over deze les

Door StudeliFout melden

Maak je eigen Studiemateriaal

Maak van je aantekeningen, slides en documenten Leermodules, Oefenvragen, Flashcards en 6 andere tools.

Aan de slag