Gids voor de bibliotheek met systeemregels

1. Overzicht

Soms moeten we bij het schrijven van unit-tests code testen die rechtstreeks samenwerkt met de Systeem klasse. Meestal in toepassingen zoals opdrachtregelprogramma's die System.exit rechtstreeks of lees argumenten met System.in.

In deze tutorial we zullen de meest voorkomende kenmerken bekijken van een nette externe bibliotheek genaamd System Rules die een set JUnit-regels biedt voor het testen van code die de Systeem klasse.

2. Maven afhankelijkheden

Laten we eerst de afhankelijkheid van systeemregels toevoegen aan onze pom.xml:

 com.github.stefanbirkner systeemregels 1.19.0 

We zullen ook System Lambda-afhankelijkheid toevoegen die ook beschikbaar is via Maven Central:

 com.github.stefanbirkner systeem-lambda 1.1.0 

Omdat systeemregels niet rechtstreeks ondersteunen JUnit5hebben we de laatste afhankelijkheid toegevoegd. Dit biedt de System Lambda-wrapper-methoden om in tests te gebruiken. Er is een op extensies gebaseerd alternatief voor dit genaamd System Stubs.

3. Werken met systeemeigenschappen

Om snel samen te vatten, gebruikt het Java-platform een Eigendommen bezwaar maken om informatie te verstrekken over het lokale systeem en de configuratie. We kunnen de eigenschappen gemakkelijk uitprinten:

System.getProperties () .forEach ((sleutel, waarde) -> System.out.println (sleutel + ":" + waarde));

Zoals we kunnen zien, bevatten eigenschappen informatie zoals de huidige gebruiker, de huidige versie van de Java-runtime en bestandspadnaamscheidingsteken:

java.version: 1.8.0_221 file.separator: / user.home: / Users / baeldung os.name: Mac OS X ...

We kunnen ook onze eigen systeemeigenschappen instellen door de System.setProperty methode. Voorzichtigheid is geboden bij het werken met systeemeigenschappen uit onze tests, aangezien deze eigenschappen JVM-globaal zijn.

Als we bijvoorbeeld een systeemeigenschap instellen, moeten we ervoor zorgen dat we de eigenschap naar de oorspronkelijke waarde herstellen wanneer onze test is voltooid of als er een fout optreedt. Dit kan soms leiden tot omslachtig instellen en afbreken van code. Als we dit echter nalaten, kan dit leiden tot onverwachte bijwerkingen bij onze tests.

In de volgende sectie zullen we zien hoe we systeemeigenschappen kunnen leveren, opschonen en ervoor zorgen dat we systeemeigenschappen herstellen nadat onze tests zijn voltooid op een beknopte en eenvoudige manier.

4. Systeemeigenschappen verstrekken

Laten we ons voorstellen dat we een systeemeigenschap hebben log_dir die de locatie bevat waar onze logs moeten worden geschreven en onze applicatie stelt deze locatie in wanneer deze opstart:

System.setProperty ("log_dir", "/ tmp / baeldung / logs");

4.1. Zorg voor een enkele eigenschap

Laten we nu eens kijken dat we uit onze unit-test een andere waarde willen geven. We kunnen dit doen met behulp van de ProvideSystemProperty regel:

openbare klasse ProvidesSystemPropertyWithRuleUnitTest {@Rule openbare finale ProvideSystemProperty biedtSystemPropertyRule = nieuwe ProvideSystemProperty ("log_dir", "test / resources"); @Test openbare ongeldig gegevenProvideSystemProperty_whenGetLogDir_thenLogDirIsProvidedSuccessfully () {assertEquals ("log_dir moet worden opgegeven", "test / resources", System.getProperty ("log_dir")); } // definitie van eenheidstest gaat verder} 

De ... gebruiken ProvideSystemProperty regel, kunnen we een willekeurige waarde instellen voor een gegeven systeemeigenschap voor gebruik in onze tests. In dit voorbeeld stellen we de log_dir eigendom aan onze test / bronnen directory, en bevestig vanuit onze unit-test eenvoudig dat de waarde van de testeigenschap met succes is opgegeven.

Als we dan de waarde van de log_dir property wanneer onze testklasse is voltooid:

@AfterClass openbare statische leegte tearDownAfterClass () gooit uitzondering {System.out.println (System.getProperty ("log_dir")); } 

We kunnen zien dat de waarde van het onroerend goed is hersteld naar de oorspronkelijke waarde:

/ tmp / baeldung / logs

4.2. Meerdere eigenschappen bieden

Als we meerdere eigendommen moeten opgeven, kunnen we de en methode om zoveel eigenschapswaarden aan elkaar te koppelen als we nodig hebben voor onze test:

@Rule public final ProvideSystemProperty biedtSystemPropertyRule = nieuwe ProvideSystemProperty ("log_dir", "test / resources"). En ("another_property", "another_value")

4.3. Eigenschappen uit een bestand opgeven

Evenzo hebben we ook de mogelijkheid om eigenschappen van een bestand of klassepadresource op te geven met behulp van de ProvideSystemProperty regel:

@Rule public final ProvideSystemProperty offersSystemPropertyFromFileRule = ProvideSystemProperty.fromResource ("/ test.properties"); @Test openbare ongeldig gegevenProvideSystemPropertyFromFile_whenGetName_thenNameIsProvidedSuccessfully () {assertEquals ("naam moet worden opgegeven", "baeldung", System.getProperty ("naam")); assertEquals ("versie moet worden verstrekt", "1.0", System.getProperty ("versie")); }

In het bovenstaande voorbeeld gaan we ervan uit dat we een test.properties bestand op het klassenpad:

naam = baeldung versie = 1.0

4.4. Eigenschappen bieden met JUnit5 en Lambdas

Zoals we eerder vermeldden, zouden we ook de System Lambda-versie van de bibliotheek kunnen gebruiken om tests te implementeren die compatibel zijn met JUnit5.

Laten we eens kijken hoe we onze test kunnen implementeren met deze versie van de bibliotheek:

@BeforeAll static void setUpBeforeClass () gooit uitzondering {System.setProperty ("log_dir", "/ tmp / baeldung / logs"); } @Test void givenSetSystemProperty_whenGetLogDir_thenLogDirIsProvidedSuccessfully () gooit uitzondering {restoreSystemProperties (() -> {System.setProperty ("log_dir", "test / resources"); assertEquals ("log_dir testProperty", "test / resources" moet worden verstrekt ", System.get / resources", ("log_dir"));}); assertEquals ("log_dir moet worden opgegeven", "/ tmp / baeldung / logs", System.getProperty ("log_dir")); }

In deze versie kunnen we de restoreSystemProperties methode om een ​​gegeven instructie uit te voeren. Binnen deze verklaring kunnen we de waarden instellen en verstrekken die we nodig hebben voor onze systeemeigenschappen. Zoals we kunnen zien nadat deze methode de uitvoering heeft voltooid, is de waarde van log_dir is hetzelfde als voorheen / tmp / baeldung / logs.

Helaas is er geen ingebouwde ondersteuning voor het leveren van eigenschappen van bestanden met behulp van de restoreSystemProperties methode.

5. Systeemeigenschappen wissen

Soms willen we misschien een set systeemeigenschappen wissen wanneer onze test start en hun oorspronkelijke waarden herstellen wanneer de test is voltooid, ongeacht of deze slaagt of mislukt.

We kunnen de ClearSystemProperties regel voor dit doel:

@Rule public final ClearSystemProperties userNameIsClearedRule = nieuw ClearSystemProperties ("user.name"); @Test openbare ongeldig gegevenClearUsernameProperty_whenGetUserName_thenNull () {assertNull (System.getProperty ("user.name")); }

De systeemeigenschap gebruiker.name is een van de vooraf gedefinieerde systeemeigenschappen, die de naam van het gebruikersaccount bevat. Zoals verwacht in de bovenstaande unit-test, wissen we deze eigenschap en controleren of deze leeg is in onze test.

Handig is dat we ook meerdere eigenschapsnamen kunnen doorgeven aan het ClearSystemProperties constructeur.

6. Spottend System.in

Van tijd tot tijd kunnen we interactieve opdrachtregelapplicaties maken die voorlezen van System.in.

Voor deze sectie gebruiken we een heel eenvoudig voorbeeld dat een voor- en achternaam uit de standaardinvoer leest en deze samenvoegt:

private String getFullname () {try (Scanner scanner = nieuwe scanner (System.in)) {String firstName = scanner.next (); String achternaam = scanner.next (); return String.join ("", voornaam, achternaam); }}

Systeemregels bevat de TextFromStandardInputStream regel die we kunnen gebruiken om de regels te specificeren die moeten worden opgegeven bij het aanroepen System.in:

@Rule openbare finale TextFromStandardInputStream systemInMock = emptyStandardInputStream (); @Test openbare leegte gegevenTwoNames_whenSystemInMock_thenNamesJoinedTogether () {systemInMock.provideLines ("Jonathan", "Cook"); assertEquals ("Namen moeten worden samengevoegd", "Jonathan Cook", getFullname ()); }

We bereiken dit door de biedtLines methode, die een varargs-parameter nodig heeft om het specificeren van meer dan één waarde mogelijk te maken.

In dit voorbeeld geven we twee waarden voordat we de getFullname methode, waar System.in wordt verwezen. Onze twee opgegeven regelwaarden worden elke keer dat we bellen, geretourneerd scanner.next ().

Laten we eens kijken hoe we hetzelfde kunnen bereiken in een JUnit 5-versie van de test met System Lambda:

@Test ongeldig gegevenTwoNames_whenSystemInMock_thenNamesJoinedTogether () gooit uitzondering {withTextFromSystemIn ("Jonathan", "Cook"). Execute (() -> {assertEquals ("Namen moeten worden samengevoegd", "Jonathan Cook", getFullname ());}); }

In deze variant gebruiken we de gelijknamige naam withTextFromSystemIn methode, waarmee we het opgegeven System.in waarden.

Het is belangrijk om in beide gevallen te vermelden dat na afloop van de test de oorspronkelijke waarde van System.in zal worden hersteld.

7. Testen System.out en System.err

In een eerdere zelfstudie hebben we gezien hoe u systeemregels gebruikt om eenheidstesten te maken System.out.println ().

Handig is dat we een bijna identieke benadering kunnen toepassen voor het testen van code die interageert met de standaard foutenstroom. Deze keer gebruiken we de SystemErrRule:

@Rule publieke finale SystemErrRule systemErrRule = nieuwe SystemErrRule (). EnableLog (); @Test openbare leegte gegevenSystemErrRule_whenInvokePrintln_thenLogSuccess () {printError ("Er is een fout opgetreden bij Baeldung-lezers !!"); Assert.assertEquals ("Er is een fout opgetreden Baeldung Readers !!", systemErrRule.getLog (). Trim ()); } private void printError (String-uitvoer) {System.err.println (uitvoer); }

Leuk! De ... gebruiken SystemErrRulekunnen we de schrijfopdrachten onderscheppen System.err. Eerst beginnen we met het loggen van alles waarnaar wordt geschreven System.err door het enableLog methode op onze regel. Dan bellen we gewoon getLog om de tekst naar System.err geschreven te krijgen sinds we gebeld hebben enableLog.

Laten we nu de JUnit5-versie van onze test implementeren:

@Test ongeldig gegevenTapSystemErr_whenInvokePrintln_thenOutputIsReturnedSuccessfully () genereert uitzondering {String text = tapSystemErr (() -> {printError ("Er is een fout opgetreden Baeldung Readers !!");}); Assert.assertEquals ("Er is een fout opgetreden Baeldung Readers !!", text.trim ()); }

In deze versie we maken gebruik van de tapSystemErr methode, die de instructie uitvoert en ons in staat stelt de inhoud vast te leggen die wordt doorgegeven System.err.

8. Behandeling System.exit

Opdrachtregeltoepassingen worden meestal beëindigd door te bellen System.exit. Als we een dergelijke applicatie willen testen, is het waarschijnlijk dat onze test abnormaal wordt beëindigd voordat deze is voltooid wanneer deze de code tegenkomt die aanroept System.exit.

Gelukkig bieden systeemregels een handige oplossing om dit aan te pakken met behulp van de ExpectedSystemExit regel:

@Rule openbare finale ExpectedSystemExit exitRule = ExpectedSystemExit.none (); @Test openbare leegte gegevenSystemExitRule_whenAppCallsSystemExit_thenExitRuleWorkssAsExpected () {exitRule.expectSystemExitWithStatus (1); Uitgang(); } private void exit () {System.exit (1); }

De ... gebruiken ExpectedSystemExit regel stelt ons in staat om uit onze test het verwachte te specificeren System.exit () bellen. In dit eenvoudige voorbeeld controleren we ook de verwachte statuscode met de verwachtenSystemExitWithStatus methode.

We kunnen iets soortgelijks bereiken in onze JUnit 5-versie met behulp van de catchSystemExit methode:

@Test ongeldig gegevenCatchSystemExit_whenAppCallsSystemExit_thenStatusIsReturnedSuccessfully () gooit uitzondering {int statusCode = catchSystemExit (() -> {exit ();}); assertEquals ("statuscode moet 1 zijn:", 1, statusCode); }

9. Conclusie

Samenvattend hebben we in deze zelfstudie de bibliotheek met systeemregels in detail onderzocht.

Allereerst hebben we uitgelegd hoe u code test die systeemeigenschappen gebruikt. Vervolgens hebben we gekeken hoe we de standaarduitvoer en de standaardinvoer konden testen. Ten slotte hebben we gekeken hoe we code die aanroepen moet afhandelen System.exit van onze tests.

De bibliotheek met systeemregels biedt ook ondersteuning voor het verstrekken van omgevingsvariabelen en speciale beveiligingsmanagers uit onze tests. Bekijk de volledige documentatie voor details.

Zoals altijd is de volledige broncode van het artikel beschikbaar op GitHub.