Το Spring MVC είναι ένα από τα πιο δημοφιλή πλαίσια Java για την οικοδόμηση εταιρικών εφαρμογών Java και προσφέρεται πολύ για δοκιμές. Σχετικά με το σχεδιασμό, το Spring MVC προωθεί τον διαχωρισμό των ανησυχιών και ενθαρρύνει την κωδικοποίηση έναντι διεπαφών. Αυτές οι ιδιότητες, μαζί με την εφαρμογή της εξάρτησης από την Spring, κάνουν τις εφαρμογές Spring πολύ δοκιμές.
Αυτό το σεμινάριο είναι το δεύτερο μισό της εισαγωγής μου στις δοκιμές μονάδων με το JUnit 5. Θα σας δείξω πώς να ενσωματώσετε το JUnit 5 με το Spring και, στη συνέχεια, να σας παρουσιάσω τρία εργαλεία που μπορείτε να χρησιμοποιήσετε για να δοκιμάσετε τους ελεγκτές, τις υπηρεσίες και τα αποθετήρια Spring MVC.
λήψη Λήψη του κώδικα Λήψη του πηγαίου κώδικα για παράδειγμα εφαρμογές που χρησιμοποιούνται σε αυτό το σεμινάριο. Δημιουργήθηκε από τον Steven Haines για το JavaWorld.Ενσωμάτωση JUnit 5 με Spring 5
Για αυτό το σεμινάριο, χρησιμοποιούμε το Maven και το Spring Boot, οπότε το πρώτο πράγμα που πρέπει να κάνουμε είναι να προσθέσουμε την εξάρτηση JUnit 5 στο αρχείο Maven POM:
org.junit.jupiter junit-jupiter 5.6.0 τεστ
Όπως και στο Μέρος 1, θα χρησιμοποιήσουμε το Mockito για αυτό το παράδειγμα. Λοιπόν, θα πρέπει να προσθέσουμε τη βιβλιοθήκη JUnit 5 Mockito:
δοκιμή org.mockito mockito-junit-jupiter 3.2.4
@ExtendWith και η τάξη SpringExtension
Το JUnit 5 ορίζει ένα διεπαφή επέκτασης, μέσω των οποίων τάξεις μπορούν να ενσωματωθούν με δοκιμές JUnit σε διάφορα στάδια του κύκλου ζωής εκτέλεσης. Μπορούμε να ενεργοποιήσουμε τις επεκτάσεις προσθέτοντας το @ExtendWith
σχολιασμός στις τάξεις δοκιμής μας και καθορισμός της κλάσης επέκτασης που θα φορτωθεί. Η επέκταση μπορεί στη συνέχεια να εφαρμόσει διάφορες διεπαφές επανάκλησης, οι οποίες θα κληθούν σε ολόκληρο τον κύκλο ζωής δοκιμής: πριν από την εκτέλεση όλων των δοκιμών, πριν από κάθε δοκιμή, μετά από κάθε δοκιμή και μετά την εκτέλεση όλων των δοκιμών.
Η άνοιξη ορίζει α Επέκταση άνοιξη
τάξη που εγγράφεται στις ειδοποιήσεις κύκλου ζωής JUnit 5 για τη δημιουργία και τη διατήρηση ενός "περιβάλλοντος δοκιμής". Θυμηθείτε ότι το πλαίσιο εφαρμογής του Spring περιέχει όλα τα Spring bean σε μια εφαρμογή και ότι εκτελεί ένεση εξάρτησης για να συνδέσει μαζί μια εφαρμογή και τις εξαρτήσεις της. Το Spring χρησιμοποιεί το μοντέλο επέκτασης JUnit 5 για να διατηρήσει το περιβάλλον εφαρμογής του τεστ, το οποίο καθιστά απλές τις δοκιμές μονάδας γραφής με το Spring.
Αφού προσθέσουμε τη βιβλιοθήκη JUnit 5 στο αρχείο Maven POM, μπορούμε να χρησιμοποιήσουμε το SpringExtension.class
για να επεκτείνουμε τις τάξεις δοκιμής JUnit 5:
@ExtendWith (SpringExtension.class) τάξη MyTests {// ...}
Το παράδειγμα, σε αυτήν την περίπτωση, είναι μια εφαρμογή Spring Boot. Ευτυχώς το @SpringBootTest
Ο σχολιασμός περιλαμβάνει ήδη το @ExtendWith (SpringExtension.class)
σχολιασμός, επομένως πρέπει να συμπεριλάβουμε μόνο @SpringBootTest
.
Προσθήκη της εξάρτησης Mockito
Για να δοκιμάσουμε σωστά κάθε στοιχείο ξεχωριστά και να προσομοιώσουμε διαφορετικά σενάρια, θα θέλουμε να δημιουργήσουμε ψευδείς υλοποιήσεις των εξαρτήσεων κάθε τάξης. Εδώ μπαίνει το Mockito. Συμπεριλάβετε την ακόλουθη εξάρτηση στο αρχείο POM για να προσθέσετε υποστήριξη για το Mockito:
δοκιμή org.mockito mockito-junit-jupiter 3.2.4
Αφού ενσωματώσετε το JUnit 5 και το Mockito στην εφαρμογή Spring, μπορείτε να αξιοποιήσετε το Mockito ορίζοντας απλά ένα Spring bean (όπως μια υπηρεσία ή ένα αποθετήριο) στην τάξη δοκιμής σας χρησιμοποιώντας το @MockBean
σχόλιο. Εδώ είναι το παράδειγμά μας:
@SpringBootTest δημόσια τάξη WidgetServiceTest {/ ** * Autowire στην υπηρεσία που θέλουμε να δοκιμάσουμε * / @Autowired ιδιωτική υπηρεσία WidgetService; / ** * Δημιουργήστε μια πλαστή εφαρμογή του WidgetRepository * / @MockBean ιδιωτικό WidgetRepository repository; ...}
Σε αυτό το παράδειγμα, δημιουργούμε μια χλεύη WidgetRepository
μέσα μας Δοκιμή WidgetService
τάξη. Όταν το βλέπει η Άνοιξη, θα το συνδέσει αυτόματα στο δικό μας Υπηρεσία Widget
ώστε να μπορούμε να δημιουργήσουμε διαφορετικά σενάρια στις μεθόδους δοκιμών μας. Κάθε μέθοδος δοκιμής θα διαμορφώσει τη συμπεριφορά του WidgetRepository
, όπως επιστρέφοντας το ζητούμενο Widget
ή επιστροφή ενός Προαιρετικό.empty ()
για ένα ερώτημα για το οποίο δεν βρέθηκαν τα δεδομένα. Θα ξοδέψουμε το υπόλοιπο αυτού του σεμιναρίου εξετάζοντας παραδείγματα διαφόρων τρόπων διαμόρφωσης αυτών των πλαστών φασολιών.
Η εφαρμογή Spring MVC
Για να γράψουμε εαρινές δοκιμές μονάδας, χρειαζόμαστε μια εφαρμογή για να τις καταγράψουμε. Ευτυχώς, μπορούμε να χρησιμοποιήσουμε το παράδειγμα εφαρμογής από το δικό μου Σειρά άνοιξη φροντιστήριο "Mastering Spring framework 5, Μέρος 1: Spring MVC." Χρησιμοποίησα το παράδειγμα εφαρμογής από αυτό το σεμινάριο ως βασική εφαρμογή. Το τροποποίησα με ένα ισχυρότερο REST API, ώστε να έχουμε μερικά ακόμη πράγματα να δοκιμάσουμε.
Το παράδειγμα εφαρμογής είναι μια εφαρμογή ιστού Spring MVC με ελεγκτή REST, επίπεδο υπηρεσίας και αποθετήριο που χρησιμοποιεί το Spring Data JPA για να διατηρεί "widgets" από και προς μια βάση δεδομένων H2 στη μνήμη. Το σχήμα 1 είναι μια επισκόπηση.

Τι είναι το widget;
ΕΝΑ Widget
είναι απλώς ένα "πράγμα" με αναγνωριστικό, όνομα, περιγραφή και αριθμό έκδοσης. Σε αυτήν την περίπτωση, το widget μας επισημαίνεται με σχολιασμούς JPA για να το ορίσει ως οντότητα. ο WidgetRestController
είναι ένας ελεγκτής Spring MVC που μεταφράζει τις κλήσεις RESTful API σε ενέργειες για εκτέλεση Widgets
. ο Υπηρεσία Widget
είναι μια τυπική υπηρεσία Spring που καθορίζει την επιχειρησιακή λειτουργικότητα για Widgets
. Τέλος, το WidgetRepository
είναι μια διεπαφή Spring Data JPA, για την οποία το Spring θα δημιουργήσει μια εφαρμογή στο χρόνο εκτέλεσης. Θα εξετάσουμε τον κώδικα για κάθε τάξη καθώς γράφουμε δοκιμές στις επόμενες ενότητες.
Μονάδα δοκιμής υπηρεσίας Spring
Ας ξεκινήσουμε εξετάζοντας τον τρόπο δοκιμής μιας Άνοιξηςυπηρεσία, επειδή αυτό είναι το ευκολότερο συστατικό στην εφαρμογή MVC για δοκιμή. Παραδείγματα σε αυτήν την ενότητα θα μας επιτρέψουν να διερευνήσουμε την ενοποίηση του JUnit 5 με το Spring χωρίς να παρουσιάσουμε νέα στοιχεία δοκιμών ή βιβλιοθήκες, αν και θα το κάνουμε αργότερα στο σεμινάριο.
Θα ξεκινήσουμε εξετάζοντας το Υπηρεσία Widget
διεπαφή και το WidgetServiceImpl
τάξη, τα οποία εμφανίζονται στη Λίστα 1 και στη Λίστα 2, αντίστοιχα.
Λίστα 1. Η διεπαφή υπηρεσίας Spring (WidgetService.java)
πακέτο com.geekcap.javaworld.spring5mvcexample.service; εισαγωγή com.geekcap.javaworld.spring5mvcexample.model.Widget; εισαγωγή java.util.List; εισαγωγή java.util. Προαιρετικό; δημόσια διεπαφή WidgetService {Προαιρετικό findById (Long id); Λίστα εύρεσης Όλα (); Αποθήκευση Widget (Widget widget) void deleteById (Long id); }
Λίστα 2. Η εαρινή κλάση εφαρμογής υπηρεσίας (WidgetServiceImpl.java)
πακέτο com.geekcap.javaworld.spring5mvcexample.service; εισαγωγή com.geekcap.javaworld.spring5mvcexample.model.Widget; εισαγωγή com.geekcap.javaworld.spring5mvcexample.repository.WidgetRepository; εισαγωγή com.google.common.collect.Lists; εισαγωγή org.springframework.stereotype.Service; εισαγωγή java.util.ArrayList; εισαγωγή java.util.List; εισαγωγή java.util. Προαιρετικό; Το @Service δημόσια τάξη WidgetServiceImpl υλοποιεί το WidgetService {ιδιωτικό WidgetRepository repository; δημόσιο WidgetServiceImpl (αποθετήριο WidgetRepository) {this.repository = repository; } @Override public Προαιρετικό findById (Long id) {return repository.findById (id); } @Override δημόσια λίστα findAll () {return Lists.newArrayList (repository.findAll ()); } @Override public Widget save (Widget widget) {// Αυξήστε τον αριθμό έκδοσης widget.setVersion (widget.getVersion () + 1); // Αποθηκεύστε το widget στο repository return repository.save (widget). } @Override public void deleteById (Long id) {repository.deleteById (id); }}
WidgetServiceImpl
είναι μια υπηρεσία άνοιξη, σχολιασμένη με το @Υπηρεσία
σχολιασμός, που έχει WidgetRepository
ενσύρματο σε αυτό μέσω του κατασκευαστή του. ο εύρεσηById ()
, βρείτεΌλα ()
, και deleteById ()
Οι μέθοδοι είναι όλες οι μέθοδοι διέλευσης στο υποκείμενο WidgetRepository
. Η μόνη επιχειρηματική λογική που θα βρείτε βρίσκεται στο σώσει()
μέθοδος, η οποία αυξάνει τον αριθμό έκδοσης του Widget
όταν αποθηκεύεται.
Η τάξη δοκιμής
Για να δοκιμάσουμε αυτήν την τάξη, πρέπει να δημιουργήσουμε και να διαμορφώσουμε ένα πλαστό WidgetRepository
, συνδέστε το στο WidgetServiceImpl
παράδειγμα και, στη συνέχεια, συνδέστε το WidgetServiceImpl
στην τάξη μας. Ευτυχώς, αυτό είναι πολύ πιο εύκολο από ό, τι ακούγεται. Η λίστα 3 δείχνει τον πηγαίο κώδικα για το Δοκιμή WidgetService
τάξη.
Λίστα 3. Η τάξη δοκιμής υπηρεσίας Spring (WidgetServiceTest.java)
πακέτο com.geekcap.javaworld.spring5mvcexample.service; εισαγωγή com.geekcap.javaworld.spring5mvcexample.model.Widget; εισαγωγή com.geekcap.javaworld.spring5mvcexample.repository.WidgetRepository; εισαγωγή org.junit.jupiter.api.Asersions; εισαγωγή org.junit.jupiter.api.DisplayName; εισαγωγή org.junit.jupiter.api.Test; εισαγωγή org.junit.jupiter.api.extension.ExtendWith; εισαγωγή org.springframework.beans.factory.annotation.Autowired; εισαγωγή org.springframework.boot.test.context.SpringBootTest; εισαγωγή org.springframework.boot.test.mock.mockito.MockBean; εισαγωγή org.springframework.test.context.junit.jupiter.SpringExtension; εισαγωγή java.util.Arrays; εισαγωγή java.util.List; εισαγωγή java.util. Προαιρετικό; εισαγωγή στατικού org.mockito.Mockito.doReturn; εισαγωγή στατικού org.mockito.ArgumentMatchers.any; @SpringBootTest δημόσια τάξη WidgetServiceTest {/ ** * Autowire στην υπηρεσία που θέλουμε να δοκιμάσουμε * / @Autowired ιδιωτική υπηρεσία WidgetService; / ** * Δημιουργήστε μια πλαστή εφαρμογή του WidgetRepository * / @MockBean ιδιωτικό WidgetRepository repository; @Test @DisplayName ("Test findById Success") void testFindById () {// Ρυθμίστε το πλαστό αποθετήριο Widget widget = νέο Widget (1l, "Widget Name", "Description", 1); doReturn (Optional.of (widget)). όταν (αποθετήριο) .findById (1l); // Εκτελέστε την κλήση υπηρεσίας Προαιρετικό returnWidget = service.findById (1l); // Επιβεβαιώστε την απόκριση Assertions.assertTrue (returnWidget.isPresent (), "Το Widget δεν βρέθηκε"); Assertions.assertSame (returnWidget.get (), widget, "Το widget που επιστράφηκε δεν ήταν το ίδιο με το mock"); } @Test @DisplayName ("Test findById Not Found") void testFindByIdNotFound () {// Ρυθμίστε το πλαστό αποθετήριο doReturn (Optional.empty ()). When (repository) .findById (1l); // Εκτελέστε την κλήση υπηρεσίας Προαιρετικό returnWidget = service.findById (1l); // Επιβεβαιώστε την απόκριση Assertions.assertFalse (returnWidget.isPresent (), "Widget δεν πρέπει να βρεθεί"); } @Test @DisplayName ("Test findAll") void testFindAll () {// Ρυθμίστε το πλαστό αποθετήριο Widget widget1 = νέο Widget (1l, "Widget Name", "Description", 1); Widget widget2 = νέο Widget (2l, "Widget 2 Name", "Περιγραφή 2", 4); doReturn (Arrays.asList (widget1, widget2)). when (αποθετήριο) .findAll (); // Εκτελέστε τη λίστα κλήσεων υπηρεσίας widgets = service.findAll (); // Επιβεβαιώστε την απόκριση Assertions.assertEquals (2, widgets.size (), "findAll shall return 2 widgets"); } @Test @DisplayName ("widget save save") void testSave () {// Ρυθμίστε το πλαστό αποθετήριο Widget widget = νέο Widget (1l, "Widget Name", "Description", 1); doReturn (widget). όταν (αποθετήριο). αποθήκευση (οποιοδήποτε ()); // Εκτελέστε την κλήση υπηρεσίας Widget ReturnWidget = service.save (widget); // Επιβεβαιώστε την απόκριση Assertions.assertNotNull (returnWidget, "Το αποθηκευμένο widget δεν πρέπει να είναι μηδενικό"); Assertions.assertEquals (2, returnWidget.getVersion (), "Η έκδοση θα πρέπει να αυξηθεί"); }}
ο Δοκιμή WidgetService
τάξη σχολιάζεται με το @SpringBootTest
σχολιασμός, ο οποίος σαρώνει το CLASSPATH
για όλες τις κλάσεις διαμόρφωσης Spring και φασόλια και ρυθμίζει το πλαίσιο εφαρμογής Spring για την τάξη δοκιμής. Σημειώστε ότι Δοκιμή WidgetService
περιλαμβάνει επίσης σιωπηρά το @ExtendWith (SpringExtension.class)
σχολιασμός, μέσω του @SpringBootTest
σχολιασμός, ο οποίος ενσωματώνει την τάξη δοκιμής με το JUnit 5.
Η τάξη δοκιμής χρησιμοποιεί επίσης την άνοιξη @ Αυτόματο
σχολιασμός για autowire α Υπηρεσία Widget
για να δοκιμάσετε και χρησιμοποιεί το Mockito's @MockBean
σχολιασμός για να δημιουργήσετε μια χλεύη WidgetRepository
. Σε αυτό το σημείο, έχουμε μια χλεύη WidgetRepository
που μπορούμε να διαμορφώσουμε, και ένα πραγματικό Υπηρεσία Widget
με το χλεύη WidgetRepository
ενσύρματο σε αυτό.
Δοκιμή της υπηρεσίας Spring
Η πρώτη μέθοδος δοκιμής, testFindById ()
, εκτελεί Υπηρεσία Widget
'μικρό εύρεσηById ()
μέθοδο, η οποία θα πρέπει να επιστρέψει ένα Προαιρετικός
που περιέχει ένα Widget
. Αρχίζουμε δημιουργώντας ένα Widget
ότι θέλουμε το WidgetRepository
το να γυρίζεις. Στη συνέχεια, αξιοποιούμε το Mockito API για να διαμορφώσουμε το WidgetRepository :: findById
μέθοδος. Η δομή της πλαστής λογικής μας έχει ως εξής:
doReturn (VALUE_TO_RETURN). όταν (MOCK_CLASS_INSTANCE) .MOCK_METHOD
Σε αυτήν την περίπτωση, λέμε: Επιστροφή Προαιρετικός
των μας Widget
όταν το αποθετήριο είναι εύρεσηById ()
Η μέθοδος καλείται με ένα όρισμα 1 (ως μακρύς
).
Στη συνέχεια, επικαλούμεθα το Υπηρεσία Widget
'μικρό βρείτεById
μέθοδος με ένα όρισμα 1. Στη συνέχεια επικυρώνουμε ότι υπάρχει και ότι επιστρέφεται Widget
είναι αυτό που διαμορφώσαμε το πλαστό WidgetRepository
το να γυρίζεις.