Πώς να δοκιμάσετε υπηρεσίες, τελικά σημεία και αποθετήρια στο Spring Boot

Σε αυτήν την ανάρτηση θα σας δείξω πώς να γράφετε δοκιμές μονάδας σε εφαρμογές εκκίνησης την άνοιξη.

Γιατί είναι απαραίτητο να γράψετε ένα τεστ μονάδας, απαιτείται ένα άλλο άρθρο για να το εξηγήσετε. Αλλά για μια σύντομη εξήγηση, θα σας πω πολλά πράγματα.

Υποστηρίζω συνήθως το επιχείρημα ότι ο κώδικας χωρίς δοκιμές μονάδας είναι νεκρός κώδικας. Διότι, όταν ένας προγραμματιστής προσθέτει μια νέα δυνατότητα σε κάποιον κώδικα που δεν καλύπτεται από δοκιμή μονάδας, είναι επιρρεπές να παρακάμψει τους υπάρχοντες επιχειρηματικούς κανόνες (που σκοτώνουν τον κώδικα που γράφτηκε πριν). Ίσως δεν είναι ακριβώς επιρρεπές σε αυτό, αλλά μπορείτε να φανταστείτε ποια σφάλματα μπορεί να προκύψουν όταν ένα σύνθετο έργο πρέπει να αλλάξει. Ο έλεγχος μονάδας είναι ο μόνος τρόπος για να προστατέψετε τον κώδικά σας από αλλαγές που σπάνε.

Γιατί τα τελικά σημεία δοκιμής μονάδας;

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

Γιατί υπηρεσίες δοκιμής μονάδας;

Πρέπει να είναι ήδη ξεκάθαρο, αλλά για κάθε περίπτωση: πρέπει να είμαστε σίγουροι ότι η επιχειρηματική μας λογική λειτουργεί σωστά.

Γιατί μονάδες δοκιμής μονάδας;

Υπάρχουν αρκετές περιπτώσεις για τη δοκιμή αποθετηρίων. Φυσικά δεν δοκιμάζουμε το ίδιο το πλαίσιο. Αλλά γράφουμε τεστ μονάδας για να είμαστε σίγουροι ότι οι προδιαγραφές ή οι σχέσεις μας έχουν εφαρμοστεί σωστά.

Πώς δοκιμάζουμε λοιπόν τους ελεγκτές;

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

Σημείωση: Τα παραδείγματα που εμφανίζονται σε αυτήν την ανάρτηση δεν είναι για αρχιτεκτονική πραγματικής χρήσης παραγωγής

@[email protected] class User { @Id @GeneratedValue(generator = "uuid2") @GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator") @Column(name = "id", columnDefinition = "BINARY(16)") private UUID id; private String name; private String email; private int age;}
@Datapublic class CreateUserRequest { private String name; private String email; private int age;}
@[email protected]("/users")public class UserController { UserService userService; @Autowired public UserController(UserService userService) { this.userService = userService; } @PostMapping public ResponseEntity createUser(@RequestBody CreateUserRequest request) { User created = userService.save(request); return ResponseEntity.ok(created); }}

Ο ελεγκτής μας εξαρτάται από το UserService, αλλά δεν ενδιαφερόμαστε για το τι κάνει η υπηρεσία αυτή τη στιγμή.

Ας γράψουμε λοιπόν μια δοκιμαστική μονάδα για τον ελεγκτή μας για να βεβαιωθούμε ότι λειτουργεί σωστά.

Χλευαστήκαμε την υπηρεσία μας επειδή δεν χρειαζόμαστε τις λεπτομέρειες εφαρμογής της. Απλά δοκιμάζουμε τον ελεγκτή μας εδώ. Χρησιμοποιούμε MockMvcεδώ για να ελέγξουμε τον ελεγκτή μας και τον χαρτογράφο αντικειμένων για σειριοποίηση.

Ρυθμίζουμε τη userService.Save() μέθοδο μας για να επιστρέψουμε το επιθυμητό αντικείμενο χρήστη. Περάσαμε ένα αίτημα προς τον ελεγκτή μας και μετά ελέγξαμε τα επιστρεφόμενα στοιχεία με την ακόλουθη σειρά: andExpect(jsonPath("$.name").value(request.getName())).

Έχουμε επίσης άλλες μεθόδους για χρήση. Εδώ είναι η λίστα των μεθόδων:

Όταν εκτελούμε το τεστ βλέπουμε ότι περνάει.

Πώς δοκιμάζουμε τις υπηρεσίες;

Τώρα πάμε να δοκιμάσουμε το UserService. Είναι πολύ απλό να δοκιμάσετε.

Κοροϊδεύουμε το αποθετήριο και εισάγουμε τις χλευές μας στο UserService. Τώρα, όταν εκτελέσουμε το τεστ, θα δούμε ότι περνά.

Τώρα ας προσθέσουμε έναν επιχειρηματικό κανόνα στο UserService: ας πούμε ότι ο χρήστης πρέπει να έχει διεύθυνση email.

Αλλάζουμε τη μέθοδο αποθήκευσης στο UserService όπως παρακάτω:

public User save(CreateUserRequest request) { requireNonNull(request.getEmail()); User user = new User(); user.setName(request.getName()); user.setEmail(request.getEmail()); user.setAge(request.getAge()); userRepository.save(user); return user;}

Όταν εκτελέσουμε ξανά τη δοκιμή, θα δούμε μια αποτυχημένη δοκιμή.

Προτού το επιδιορθώσουμε, ας γράψουμε μια δοκιμή που ικανοποιεί αυτήν την επιχείρηση

Έχουμε γράψει ένα νέο τεστ που διευκρινίζει ότι αν στείλουμε ένα μηδενικό email, θα ρίξει NullPointerException.

Ας διορθώσουμε την αποτυχημένη δοκιμή προσθέτοντας ένα μήνυμα ηλεκτρονικού ταχυδρομείου στο αίτημά μας:

createUserRequest.setEmail("testemail");

Εκτελέστε και τις δύο δοκιμές:

Πώς δοκιμάζουμε τα αποθετήρια;

Τώρα έχουμε φτάσει στη δοκιμή αποθετηρίων. Χρησιμοποιούμε μια βάση δεδομένων στη μνήμη h2 μεTestEntityManager.

Το αποθετήριο μας ορίζεται ως εξής:

@Repositorypublic interface UserRepository extends JpaRepository, JpaSpecificationExecutor { Optional findById(UUID id);}

Πρώτα ρυθμίστε το h2db. Δημιουργήστε το όνομα αρχείου application.yaml σε δοκιμή -> διαδρομή πόρων:

spring: application: name: Spring Boot Rest API datasource: type: com.zaxxer.hikari.HikariDataSource url: "jdbc:h2:mem:test-api;INIT=CREATE SCHEMA IF NOT EXISTS dbo\\;CREATE SCHEMA IF NOT EXISTS definitions;DATABASE_TO_UPPER=false;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false;MODE=MSSQLServer" name: password: username: initialization-mode: never hikari: schema: dbo jpa: database: H2 database-platform: org.hibernate.dialect.H2Dialect show-sql: true hibernate: ddl-auto: create-drop test: database: replace: none

Και ας γράψουμε πρώτα μια βασική δοκιμή για το αποθετήριο μας: αποθηκεύστε έναν χρήστη και ανακτήστε το:

@RunWith(SpringRunner.class)@DataJpaTestpublic class UserRepositoryTest { @Autowired TestEntityManager entityManager; @Autowired UserRepository sut; @Test public void it_should_save_user() { User user = new User(); user.setName("test user"); user = entityManager.persistAndFlush(user); assertThat(sut.findById(user.getId()).get()).isEqualTo(user); }}

Όταν το τρέξουμε, θα δούμε πολλά αποτελέσματα εξόδου από την κονσόλα, καθώς και τις δοκιμές μας:

Τώρα ας προσθέσουμε μια άλλη μέθοδο στο αποθετήριο μας για αναζήτηση ενός χρήστη μέσω email:

Optional findByEmail(String email);

Και γράψτε ένα άλλο τεστ:

@Testpublic void it_should_find_user_byEmail() { User user = new User(); user.setEmail("[email protected]"); user = entityManager.persistAndFlush(user); assertThat(sut.findByEmail(user.getEmail()).get()).isEqualTo(user);}

Όταν ρίξουμε μια ματιά στην κονσόλα μετά την εκτέλεση της δοκιμής, θα δούμε το SQL που δημιουργείται από την αδρανοποίηση:

SELECT user0_.id AS id1_1_,user0_.age AS age2_1_,user0_.email AS email3_1_,user0_.name AS name4_1_FROM user user0_WHERE user0_.email=?

Μέχρι εδώ καλά. Έχουμε καλύψει τα βασικά της δοκιμής μονάδας με ελατήριο.

Τώρα δεν έχετε δικαιολογίες να μην γράψετε τεστ μονάδας! Ελπίζω ότι είναι ξεκάθαρο για εσάς πώς να γράφετε τεστ μονάδας για διαφορετικούς σκοπούς.