Το 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, έτσι ώστε να μπορείτε να γράφετε πιο ισχυρές δοκιμές μονάδας για σύνθετα συστήματα πραγματικού κόσμου.
Δοκιμαστική ανάπτυξη
Εάν έχετε αναπτύξει κώδικα Java για οποιαδήποτε χρονική περίοδο, πιθανώς είστε εξοικειωμένοι με την ανάπτυξη που βασίζεται σε δοκιμές, επομένως θα διατηρήσω αυτήν την ενότητα σύντομη. Είναι σημαντικό να καταλάβουμε Γιατί γράφουμε, ωστόσο, δοκιμές μονάδας, καθώς και τις στρατηγικές που χρησιμοποιούν οι προγραμματιστές κατά το σχεδιασμό δοκιμών μονάδας.
Η δοκιμαστική ανάπτυξη (TDD) είναι μια διαδικασία ανάπτυξης λογισμικού που συνδυάζει κωδικοποίηση, δοκιμές και σχεδιασμό. Πρόκειται για μια πρώτη δοκιμαστική προσέγγιση που στοχεύει στη βελτίωση της ποιότητας των εφαρμογών σας. Η δοκιμαστική ανάπτυξη καθορίζεται από τον ακόλουθο κύκλο ζωής:
- Προσθέστε μια δοκιμή.
- Εκτελέστε όλες τις δοκιμές σας και παρατηρήστε την αποτυχία του νέου τεστ.
- Εφαρμόστε τον κωδικό.
- Εκτελέστε όλες τις δοκιμές σας και παρατηρήστε την επιτυχία της νέας δοκιμής.
- Αναπαράγει τον κωδικό.
Το σχήμα 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] ------------- -------------------------------------------------- ---------