Προγραμματισμός

Εκμάθηση JUnit 5, μέρος 1: Δοκιμή μονάδας με JUnit 5, Mockito και Hamcrest

Το JUnit 5 είναι το νέο de facto πρότυπο για την ανάπτυξη δοκιμαστικών μονάδων στην Java. Αυτή η νεότερη έκδοση άφησε πίσω τους περιορισμούς του Java 5 και ενσωμάτωσε πολλές δυνατότητες από το Java 8, κυρίως υποστήριξη για εκφράσεις lambda.

Σε αυτό το πρώτο μισό της εισαγωγής δύο μερών στο JUnit 5, θα ξεκινήσετε τις δοκιμές με το JUnit 5. Θα σας δείξω πώς να διαμορφώσετε ένα έργο Maven ώστε να χρησιμοποιεί το JUnit 5, πώς να γράφετε δοκιμές χρησιμοποιώντας το @Δοκιμή και @ParameterizedTest σχολιασμοί και πώς μπορείτε να εργαστείτε με τους νέους σχολιασμούς κύκλου ζωής στο JUnit 5. Θα δείτε επίσης ένα σύντομο παράδειγμα χρήσης ετικετών φίλτρου και θα σας δείξω πώς να ενσωματώσετε το JUnit 5 με μια βιβλιοθήκη ισχυρισμών τρίτου μέρους — σε αυτήν την περίπτωση , Χάμρεστ. Τέλος, θα λάβετε μια γρήγορη, εισαγωγική εισαγωγή στην ενσωμάτωση του JUnit 5 με το Mockito, έτσι ώστε να μπορείτε να γράφετε πιο ισχυρές δοκιμές μονάδας για σύνθετα συστήματα πραγματικού κόσμου.

λήψη Λάβετε τον κωδικό Λάβετε τον πηγαίο κώδικα για παραδείγματα σε αυτό το σεμινάριο. Δημιουργήθηκε από τον Steven Haines για το JavaWorld.

Δοκιμαστική ανάπτυξη

Εάν έχετε αναπτύξει κώδικα Java για οποιαδήποτε χρονική περίοδο, πιθανώς είστε εξοικειωμένοι με την ανάπτυξη που βασίζεται σε δοκιμές, επομένως θα διατηρήσω αυτήν την ενότητα σύντομη. Είναι σημαντικό να καταλάβουμε Γιατί γράφουμε, ωστόσο, δοκιμές μονάδας, καθώς και τις στρατηγικές που χρησιμοποιούν οι προγραμματιστές κατά το σχεδιασμό δοκιμών μονάδας.

Η δοκιμαστική ανάπτυξη (TDD) είναι μια διαδικασία ανάπτυξης λογισμικού που συνδυάζει κωδικοποίηση, δοκιμές και σχεδιασμό. Πρόκειται για μια πρώτη δοκιμαστική προσέγγιση που στοχεύει στη βελτίωση της ποιότητας των εφαρμογών σας. Η δοκιμαστική ανάπτυξη καθορίζεται από τον ακόλουθο κύκλο ζωής:

  1. Προσθέστε μια δοκιμή.
  2. Εκτελέστε όλες τις δοκιμές σας και παρατηρήστε την αποτυχία του νέου τεστ.
  3. Εφαρμόστε τον κωδικό.
  4. Εκτελέστε όλες τις δοκιμές σας και παρατηρήστε την επιτυχία της νέας δοκιμής.
  5. Αναπαράγει τον κωδικό.

Το σχήμα 1 δείχνει αυτόν τον κύκλο ζωής TDD.

Στίβεν Χάινς

Υπάρχει διπλός σκοπός για τη σύνταξη τεστ πριν από τη σύνταξη του κωδικού σας. Πρώτον, σε αναγκάζει να σκεφτείς το επιχειρηματικό πρόβλημα που προσπαθείς να λύσεις. Για παράδειγμα, πώς πρέπει να συμπεριφέρονται επιτυχημένα σενάρια; Ποιες συνθήκες πρέπει να αποτύχουν; Πώς πρέπει να αποτύχουν; Δεύτερον, η πρώτη δοκιμή σας δίνει μεγαλύτερη εμπιστοσύνη στις δοκιμές σας. Κάθε φορά που γράφω δοκιμές μετά τη σύνταξη κώδικα, πρέπει πάντα να τις σπάζω για να βεβαιωθώ ότι έχουν πραγματικά λάθη. Το γραπτό τεστ αποφεύγει πρώτα αυτό το επιπλέον βήμα.

Η συγγραφή δοκιμών για το ευτυχισμένο μονοπάτι είναι συνήθως εύκολη: Λαμβάνοντας υπόψη την καλή συμβολή, η τάξη θα πρέπει να επιστρέψει μια ντετερμινιστική απάντηση. Ωστόσο, η σύνταξη αρνητικών (ή αποτυχιών) δοκιμαστικών περιπτώσεων, ειδικά για σύνθετα στοιχεία, μπορεί να είναι πιο περίπλοκη.

Για παράδειγμα, σκεφτείτε να γράψετε δοκιμές για ένα αποθετήριο βάσης δεδομένων. Στο χαρούμενο μονοπάτι, εισάγουμε μια εγγραφή στη βάση δεδομένων και λαμβάνουμε πίσω το δημιουργημένο αντικείμενο, συμπεριλαμβανομένων τυχόν δημιουργημένων κλειδιών. Στην πραγματικότητα, πρέπει επίσης να εξετάσουμε την πιθανότητα σύγκρουσης, όπως η εισαγωγή μιας εγγραφής με μια μοναδική τιμή στήλης που διατηρείται ήδη από άλλη εγγραφή. Επιπλέον, τι συμβαίνει όταν το αποθετήριο δεν μπορεί να συνδεθεί στη βάση δεδομένων, ίσως επειδή έχει αλλάξει το όνομα χρήστη ή ο κωδικός πρόσβασης; Τι συμβαίνει εάν υπάρχει σφάλμα δικτύου κατά τη μεταφορά; Τι θα συμβεί εάν το αίτημα δεν ολοκληρωθεί στο καθορισμένο όριο χρονικού ορίου;

Για να δημιουργήσετε ένα ισχυρό στοιχείο, πρέπει να λάβετε υπόψη όλα τα πιθανά και απίθανα σενάρια, να αναπτύξετε δοκιμές για αυτά και να γράψετε τον κωδικό σας για να ικανοποιήσετε αυτές τις δοκιμές. Αργότερα στο άρθρο, θα εξετάσουμε στρατηγικές για τη δημιουργία διαφορετικών σεναρίων αποτυχίας, μαζί με ορισμένες από τις νέες δυνατότητες στο JUnit 5 που μπορούν να σας βοηθήσουν να δοκιμάσετε αυτά τα σενάρια.

Υιοθέτηση του JUnit 5

Εάν χρησιμοποιείτε το JUnit για λίγο, μερικές από τις αλλαγές στο JUnit 5 θα είναι μια προσαρμογή. Ακολουθεί μια περίληψη υψηλού επιπέδου για το τι διαφέρει μεταξύ των δύο εκδόσεων:

  • Το JUnit 5 είναι τώρα συσκευασμένο στο org.junit.jupiter ομάδα, η οποία αλλάζει τον τρόπο με τον οποίο θα την συμπεριλάβετε στα έργα Maven και Gradle.
  • Το JUnit 4 απαιτούσε ένα ελάχιστο JDK JDK 5. Το JUnit 5 απαιτεί τουλάχιστον JDK 8.
  • JUnit 4's @Πριν, @Πριν το μάθημα, @Μετά, και @Μετά το μάθημα οι σχολιασμοί αντικαταστάθηκαν από το @BeforeEach, @BeforeAll, @AfterEach, και @AfterAll, αντίστοιχα.
  • JUnit 4's @Αγνοώ ο σχολιασμός αντικαταστάθηκε από το @Ατομα με ειδικές ανάγκες σχόλιο.
  • ο @Κατηγορία ο σχολιασμός αντικαταστάθηκε από το @Ετικέτα σχόλιο.
  • Το JUnit 5 προσθέτει ένα νέο σύνολο μεθόδων διεκδίκησης.
  • Οι δρομείς έχουν αντικατασταθεί με επεκτάσεις, με ένα νέο API για εφαρμογές επέκτασης.
  • Το JUnit 5 εισάγει παραδοχές που εμποδίζουν την εκτέλεση μιας δοκιμής.
  • Το JUnit 5 υποστηρίζει ένθετες και δυναμικές τάξεις δοκιμών.

Θα διερευνήσουμε τις περισσότερες από αυτές τις νέες δυνατότητες σε αυτό το άρθρο.

Δοκιμή μονάδας με JUnit 5

Ας ξεκινήσουμε απλό, με ένα παράδειγμα από άκρο σε άκρο διαμόρφωσης ενός έργου για χρήση του JUnit 5 για δοκιμή μονάδας. Η λίστα 1 δείχνει α MathTools κλάση της οποίας η μέθοδος μετατρέπει έναν αριθμητή και έναν παρονομαστή σε a διπλό.

Λίστα 1. Ένα παράδειγμα έργου JUnit 5 (MathTools.java)

 πακέτο com.javaworld.geekcap.math; δημόσια τάξη MathTools {public static double convertToDecimal (int numerator, int denominator) {if (παρονομαστής == 0) {ρίξτε νέο IllegalArgumentException ("Ο παρονομαστής δεν πρέπει να είναι 0"); } επιστροφή (διπλός) αριθμητής / (διπλός) παρονομαστής; }}

Έχουμε δύο βασικά σενάρια για τη δοκιμή του MathTools τάξη και η μέθοδος της:

  • ΕΝΑ έγκυρη δοκιμή, στον οποίο περνάμε μηδενικούς ακέραιους αριθμούς και αριθμητές.
  • ΕΝΑ σενάριο αποτυχίας, στην οποία περνάμε μηδενική τιμή για τον παρονομαστή.

Η λίστα 2 δείχνει μια κλάση δοκιμής JUnit 5 για τη δοκιμή αυτών των δύο σεναρίων.

Λίστα 2. Μια τάξη δοκιμής JUnit 5 (MathToolsTest.java)

 πακέτο com.javaworld.geekcap.math; εισαγωγή java.lang.IllegalArgumentException; εισαγωγή org.junit.jupiter.api.Asersions; εισαγωγή org.junit.jupiter.api.Test; class MathToolsTest {@Test void testConvertToDecimalSuccess () {διπλό αποτέλεσμα = MathTools.convertToDecimal (3, 4); Assertions.assertEquals (0,75, αποτέλεσμα); } @Test void testConvertToDecimalInvalidDenominator () {Assertions.assertThrows (IllegalArgumentException.class, () -> MathTools.convertToDecimal (3, 0)); }}

Στη λίστα 2, το testConvertToDecimalInvalidDenominator η μέθοδος εκτελεί το MathTools :: convertToDecimal μέθοδο μέσα σε ένα διεκδικεί κλήση. Το πρώτο επιχείρημα είναι ο αναμενόμενος τύπος εξαίρεσης που θα απορριφθεί. Το δεύτερο επιχείρημα είναι μια συνάρτηση που θα ρίξει αυτήν την εξαίρεση. ο διεκδικεί Η μέθοδος εκτελεί τη συνάρτηση και επιβεβαιώνει ότι ρίχνεται ο αναμενόμενος τύπος εξαίρεσης.

Η κατηγορία Assertions και οι μέθοδοι της

οorg.junit.jupiter.api. Δοκιμή ο σχολιασμός υποδηλώνει μια μέθοδο δοκιμής. Σημειώστε ότι το @Δοκιμή Ο σχολιασμός προέρχεται πλέον από το πακέτο API JUnit 5 Jupiter αντί για JUnit 4's org.junit πακέτο. ο testConvertToDecimalSuccess πρώτη μέθοδος εκτελεί το MathTools :: convertToDecimal μέθοδο με έναν αριθμητή 3 και έναν παρονομαστή 4, στη συνέχεια ισχυρίζεται ότι το αποτέλεσμα είναι ίσο με 0,75. ο org.junit.jupiter.api. Εκθέσεις τάξη παρέχει ένα σύνολο στατικός μεθόδους σύγκρισης πραγματικών και αναμενόμενων αποτελεσμάτων. ο Ισχυρισμοί Η κλάση έχει τις ακόλουθες μεθόδους, οι οποίες καλύπτουν τους περισσότερους από τους πρωτόγονους τύπους δεδομένων:

  • assertArrayEquals συγκρίνει τα περιεχόμενα ενός πραγματικού πίνακα με έναν αναμενόμενο πίνακα.
  • assertEquals συγκρίνει μια πραγματική τιμή με μια αναμενόμενη τιμή.
  • assertNotEquals συγκρίνει δύο τιμές για να επιβεβαιώσει ότι δεν είναι ίσες.
  • assertTrue επικυρώνει ότι η παρεχόμενη τιμή είναι αληθής.
  • ισχυρίστε False επικυρώνει ότι η παρεχόμενη τιμή είναι ψευδής.
  • assertLinesMatch συγκρίνει δύο λίστες Σειράμικρό.
  • επιβεβαιώνωNull επιβεβαιώνει ότι η παρεχόμενη τιμή είναι μηδενική.
  • assertNotNull επικυρώνει ότι η παρεχόμενη τιμή δεν είναι μηδενική.
  • assameSame επικυρώνει ότι δύο τιμές αναφέρονται στο ίδιο αντικείμενο.
  • assertNotSame επικυρώνει ότι δύο τιμές δεν αναφέρονται στο ίδιο αντικείμενο.
  • διεκδικεί επικυρώνει ότι η εκτέλεση μιας μεθόδου ρίχνει μια αναμενόμενη εξαίρεση (μπορείτε να το δείτε στο testConvertToDecimalInvalidDenominator παραπάνω παράδειγμα).
  • assertTimeout επικυρώνει ότι μια παρεχόμενη συνάρτηση ολοκληρώνεται εντός καθορισμένου χρονικού ορίου.
  • assertTimeoutPreemptively επικυρώνει ότι μια παρεχόμενη συνάρτηση ολοκληρώνεται εντός ενός καθορισμένου χρονικού ορίου, αλλά μόλις επιτευχθεί το χρονικό όριο, σκοτώνει την εκτέλεση της συνάρτησης.

Εάν κάποια από αυτές τις μεθόδους επιβεβαίωσης αποτύχει, η δοκιμή μονάδας επισημαίνεται ως αποτυχημένη. Αυτή η ειδοποίηση αποτυχίας θα γραφτεί στην οθόνη όταν εκτελέσετε τη δοκιμή και, στη συνέχεια, θα αποθηκευτεί σε ένα αρχείο αναφοράς.

Χρήση του δέλτα με assertEquals

Οταν χρησιμοποιείτε φλοτέρ και διπλό τιμές σε ένα assertEquals, μπορείτε επίσης να καθορίσετε ένα δέλτα που αντιπροσωπεύει ένα κατώφλι διαφοράς μεταξύ των δύο. Στο παράδειγμά μας θα μπορούσαμε να προσθέσουμε ένα δέλτα 0,001, στην περίπτωση που το 0,75 επιστράφηκε πραγματικά ως 0,750001.

Ανάλυση των αποτελεσμάτων των δοκιμών σας

Εκτός από την επικύρωση μιας τιμής ή συμπεριφοράς, το διεκδικώ Οι μέθοδοι μπορούν επίσης να αποδεχτούν μια περιγραφή κειμένου του σφάλματος, η οποία μπορεί να σας βοηθήσει να διαγνώσετε αστοχίες. Για παράδειγμα:

 Assertions.assertEquals (0,75, αποτέλεσμα, "Η τιμή MathTools :: convertToDecimal δεν επέστρεψε τη σωστή τιμή 0,75 για 3/4"); Assertions.assertEquals (0,75, αποτέλεσμα, () -> "Η τιμή MathTools :: convertToDecimal δεν επέστρεψε τη σωστή τιμή 0,75 για 3/4"); 

Η έξοδος θα δείξει την αναμενόμενη τιμή 0,75 και την πραγματική τιμή. Θα εμφανίσει επίσης το καθορισμένο μήνυμα, το οποίο μπορεί να σας βοηθήσει να κατανοήσετε το πλαίσιο του σφάλματος. Η διαφορά μεταξύ των δύο παραλλαγών είναι ότι η πρώτη δημιουργεί πάντα το μήνυμα, ακόμη και αν δεν εμφανίζεται, ενώ η δεύτερη κατασκευάζει το μήνυμα μόνο εάν ο ισχυρισμός αποτύχει. Σε αυτήν την περίπτωση, η κατασκευή του μηνύματος είναι ασήμαντη, οπότε δεν έχει σημασία. Ωστόσο, δεν χρειάζεται να δημιουργήσετε ένα μήνυμα σφάλματος για μια δοκιμή που περνά, οπότε είναι συνήθως μια καλύτερη πρακτική να χρησιμοποιήσετε το δεύτερο στυλ.

Τέλος, εάν χρησιμοποιείτε ένα IDE όπως το IntelliJ για να εκτελέσετε τις δοκιμές σας, κάθε μέθοδος δοκιμής θα εμφανίζεται με το όνομα της μεθόδου. Αυτό είναι καλό εάν τα ονόματα της μεθόδου σας είναι αναγνώσιμα, αλλά μπορείτε επίσης να προσθέσετε ένα @DisplayName σχολιασμός των μεθόδων δοκιμής σας για τον καλύτερο προσδιορισμό των δοκιμών:

@Test @DisplayName ("Δοκιμή επιτυχούς δεκαδικής μετατροπής") άκυρο testConvertToDecimalSuccess () {double result = MathTools.convertToDecimal (3, 4); Assertions.assertEquals (0,751, αποτέλεσμα); }

Εκτέλεση του τεστ μονάδας

Για να εκτελέσετε τις δοκιμές JUnit 5 από ένα έργο Maven, πρέπει να συμπεριλάβετε το maven-surefire-plugin στο Maven pom.xml αρχείο και προσθέστε μια νέα εξάρτηση. Η λίστα 3 δείχνει το pom.xml αρχείο για αυτό το έργο.

Λίστα 3. Maven pom.xml για ένα παράδειγμα έργου JUnit 5

  4.0.0 com.javaworld.geekcap junit5 jar 1.0-SNAPSHOT org.apache.maven.plugins maven-compiler-plugin 3.8.1 8 8 org.apache.maven.plugins maven-surefire-plugin 3.0.0-M4 junit5 // maven.apache.org org.junit.jupiter δοκιμή junit-jupiter 5.6.0 

Εξαρτήσεις JUnit 5

Το JUnit 5 συσκευάζει τα συστατικά του στο org.junit.jupiter ομάδα και πρέπει να προσθέσουμε το χιτώνας-Δίας artifact, το οποίο είναι ένα τεχνητό άθροισμα που εισάγει τις ακόλουθες εξαρτήσεις:

  • junit-jupiter-api ορίζει το API για τη σύνταξη δοκιμών και επεκτάσεων.
  • κινητήρας junit-jupiter είναι η εφαρμογή δοκιμαστικής μηχανής που εκτελεί τις δοκιμές μονάδας.
  • junit-jupiter-params παρέχει υποστήριξη για παραμετροποιημένες δοκιμές.

Στη συνέχεια, πρέπει να προσθέσουμε το maven-surefire-plugin build plug-in για να εκτελέσετε τις δοκιμές.

Τέλος, φροντίστε να συμπεριλάβετε το maven-compiler-plugin με μια έκδοση του Java 8 ή νεότερη έκδοση, έτσι ώστε να μπορείτε να χρησιμοποιήσετε τις λειτουργίες Java 8 όπως lambda.

Τρέξτο!

Χρησιμοποιήστε την ακόλουθη εντολή για να εκτελέσετε την τάξη δοκιμής από το IDE σας ή από το Maven:

καθαρή δοκιμή mvn

Εάν είστε επιτυχής, θα πρέπει να βλέπετε έξοδο παρόμοιο με το ακόλουθο:

 [ΠΛΗΡΟΦΟΡΙΕΣ] ----------------------------------------------- -------- [ΠΛΗΡΟΦΟΡΙΕΣ] ΔΟΚΙΜΕΣ [ΠΛΗΡΟΦΟΡΙΕΣ] ----------------------------------- -------------------- [INFO] Εκτέλεση com.javaworld.geekcap.math.MathToolsTest [INFO] Εκτέλεση δοκιμών: 2, Αποτυχίες: 0, Σφάλματα: 0, Παράλειψη : 0, Χρόνος που πέρασε: 0,04 s - στο com.javaworld.geekcap.math.MathToolsTest [INFO] [INFO] Αποτελέσματα: [INFO] [INFO] Δοκιμές εκτελέστηκαν: 2, Αποτυχίες: 0, Σφάλματα: 0, Παράλειψη: 0 [ ΠΛΗΡΟΦΟΡΙΕΣ] [ΠΛΗΡΟΦΟΡΙΕΣ] --------------------------------------------- --------------------------- [ΠΛΗΡΟΦΟΡΙΕΣ] ΚΑΤΑΣΚΕΥΗ ΕΠΙΤΥΧΙΑΣ [ΠΛΗΡΟΦΟΡΙΕΣ] -------------------------------------------------- ------- [INFO] Συνολικός χρόνος: 3.832 s [INFO] Ολοκληρώθηκε στις: 2020-02-16T08: 21: 15-05: 00 [INFO] ------------- -------------------------------------------------- ---------