Δοκιμαστικά δοχεία - Πώς να κάνετε τις δοκιμές πιο εύκολες;
Bartlomiej Kuczynski
Ψάχνετε έναν τρόπο να κάνετε δοκιμές με ευκολότερο τρόπο; Σας βρήκαμε! Ελέγξτε το παρακάτω άρθρο και μάθετε πώς να το κάνετε εφικτό.
Η σύγχρονη ανάπτυξη εφαρμογών βασίζεται σε έναν απλό κανόνα:
Χρήση σύνθεσης
Συνθέτουμε κλάσεις, συναρτήσεις και υπηρεσίες σε μεγαλύτερα κομμάτια λογισμικού. Αυτό το τελευταίο στοιχείο είναι το θεμέλιο των μικρουπηρεσιών και των εξαγωνική αρχιτεκτονική. Θα θέλαμε να χρησιμοποιήσουμε υπάρχουσες λύσεις, να τις ενσωματώσουμε στο λογισμικό μας και να προχωρήσουμε κατευθείαν στο αγορά.
Θέλετε να χειρίζεστε την εγγραφή λογαριασμού και να αποθηκεύετε δεδομένα χρηστών; Μπορείτε να επιλέξετε μία από τις υπηρεσίες OAuth. Ίσως η εφαρμογή σας προσφέρει κάποιο είδος συνδρομής ή πληρωμής; Υπάρχουν πολλές υπηρεσίες που μπορούν να σας βοηθήσουν να το χειριστείτε αυτό. Χρειάζεστε κάποια αναλυτικά στοιχεία στον ιστότοπό σας, αλλά δεν καταλαβαίνετε τον GDPR; Μη διστάσετε να επιλέξετε μία από τις έτοιμες λύσεις.
Κάτι που κάνει την ανάπτυξη τόσο εύκολη από επιχειρηματική άποψη μπορεί να σας προκαλέσει πονοκέφαλο - τη στιγμή που θα πρέπει να γράψετε μια απλή δοκιμή.
Το Fantastic Beasts: Βάσεις δεδομένων και πώς να τις ελέγξετε
Ο έλεγχος μονάδας είναι αρκετά απλός. Αν ακολουθήσετε μόνο τους κανόνες, τότε το περιβάλλον δοκιμών σας και το κωδικός είναι υγιείς. Ποιοι είναι αυτοί οι κανόνες;
Εύκολο στο γράψιμο - μια δοκιμή μονάδας θα πρέπει να είναι εύκολο να γραφτεί επειδή γράφετε πολλές από αυτές. Λιγότερη προσπάθεια σημαίνει ότι γράφονται περισσότερες δοκιμές.
Αναγνώσιμο - ο κώδικας δοκιμής πρέπει να είναι ευανάγνωστος. Η δοκιμή είναι μια ιστορία. Περιγράφει τη συμπεριφορά του λογισμικού και θα μπορούσε να χρησιμοποιηθεί ως συντόμευση της τεκμηρίωσης. Μια καλή δοκιμή μονάδας σας βοηθά να διορθώσετε σφάλματα χωρίς να κάνετε αποσφαλμάτωση του κώδικα.
Αξιόπιστη - η δοκιμή θα πρέπει να αποτύχει μόνο εάν υπάρχει σφάλμα στο σύστημα που δοκιμάζεται. Προφανές; Όχι πάντα. Μερικές φορές οι δοκιμές περνούν αν τις εκτελέσετε μία προς μία, αλλά αποτυγχάνουν όταν τις εκτελέσετε ως σύνολο. Περνούν στον υπολογιστή σας, αλλά αποτυγχάνουν στο CI (Λειτουργεί στο μηχάνημά μου). Μια καλή δοκιμή μονάδας έχει μόνο έναν λόγο αποτυχίας.
Γρήγορη - οι δοκιμές πρέπει να είναι γρήγορες. Η προετοιμασία για την εκτέλεση, η έναρξη και η ίδια η εκτέλεση των δοκιμών πρέπει να είναι πολύ γρήγορη. Διαφορετικά, θα τις γράφετε, αλλά δεν θα τις εκτελείτε. Αργές δοκιμές σημαίνουν χαμένη εστίαση. Περιμένετε και κοιτάτε τη γραμμή προόδου.
Ανεξάρτητο - τέλος, η δοκιμή πρέπει να είναι ανεξάρτητη. Αυτός ο κανόνας απορρέει από τους προηγούμενους. Μόνο οι πραγματικά ανεξάρτητες δοκιμές μπορούν να γίνουν μονάδα. Δεν παρεμβαίνουν μεταξύ τους, μπορούν να εκτελεστούν με οποιαδήποτε σειρά και οι πιθανές αποτυχίες δεν εξαρτώνται από τα αποτελέσματα άλλων δοκιμών. Ανεξάρτητη σημαίνει επίσης ότι δεν υπάρχει εξάρτηση από εξωτερικούς πόρους, όπως βάσεις δεδομένων, υπηρεσίες ανταλλαγής μηνυμάτων ή σύστημα αρχείων. Εάν πρέπει να επικοινωνήσετε με εξωτερικούς φορείς, μπορείτε να χρησιμοποιήσετε mocks, stubs ή dummies.
Τα πάντα περιπλέκονται όταν θέλουμε να γράψουμε κάποιες δοκιμές ολοκλήρωσης. Δεν είναι κακό αν θέλουμε να δοκιμάσουμε μερικές υπηρεσίες μαζί. Αλλά όταν πρέπει να δοκιμάσουμε υπηρεσίες που χρησιμοποιούν εξωτερικούς πόρους, όπως βάσεις δεδομένων ή υπηρεσίες ανταλλαγής μηνυμάτων, τότε θα έχουμε πρόβλημα.
Για να εκτελέσετε τη δοκιμή, πρέπει να εγκαταστήσετε...
Πριν από πολλά χρόνια, όταν θέλαμε να κάνουμε κάποιες δοκιμές ολοκλήρωσης και να χρησιμοποιήσουμε, π.χ., βάσεις δεδομένων, είχαμε δύο επιλογές:
Μπορούμε να εγκαταστήσουμε μια βάση δεδομένων τοπικά. Ρυθμίστε ένα σχήμα και συνδεθείτε από τη δοκιμή μας,
Μπορούμε να συνδεθούμε σε μια υπάρχουσα περίπτωση "κάπου στο διάστημα".
Και τα δύο είχαν πλεονεκτήματα, και τα δύο είχαν μειονεκτήματα. Αλλά και τα δύο εισάγουν πρόσθετα επίπεδα πολυπλοκότητας. Μερικές φορές επρόκειτο για τεχνική πολυπλοκότητα που προέκυπτε από τα χαρακτηριστικά ορισμένων εργαλείων, π.χ. εγκατάσταση και διαχείριση της Oracle DB στο localhost σας. Μερικές φορές ήταν μια ταλαιπωρία στη διαδικασία, π.χ. πρέπει να συμφωνήσετε με τη δοκιμή ομάδα σχετικά με τη χρήση του JMS... κάθε φορά που θέλετε να εκτελέσετε δοκιμές.
Τα εμπορευματοκιβώτια στη διάσωση
Τα τελευταία 10 χρόνια, η ιδέα της εμπορευματοκιβωτιοποίησης έχει κερδίσει αναγνώριση στον κλάδο. Έτσι, μια φυσική απόφαση είναι να επιλέξουμε τα εμπορευματοκιβώτια ως λύση για το ζήτημα των δοκιμών ολοκλήρωσης. Πρόκειται για μια απλή, καθαρή λύση. Απλώς εκτελείτε το build της διαδικασίας σας και όλα λειτουργούν! Δεν μπορείτε να το πιστέψετε; Ρίξτε μια ματιά σε αυτή την απλή διαμόρφωση ενός maven build:
com.dkanejs.maven.plugins
docker-compose-maven-plugin
4.0.0
up
test-compile
up
${έργο.basedir}/docker-compose.yml
true
down
post-integration-test
down
${project.basedir}/docker-compose.yml
true
Και το docker-compose.yml το αρχείο φαίνεται επίσης πολύ ωραίο!
Το παραπάνω παράδειγμα είναι πολύ απλό. Μόνο μια βάση δεδομένων postgres, pgAdmin και αυτό είναι όλο. Όταν εκτελείτε
bash
$ mvn clean verify
τότε το πρόσθετο maven εκκινεί τα κοντέινερ και μετά τις δοκιμές τα απενεργοποιεί. Τα προβλήματα αρχίζουν όταν το έργο μεγαλώνει και το αρχείο σύνθεσης μεγαλώνει επίσης. Κάθε φορά θα πρέπει να ξεκινάτε όλα τα containers και αυτά θα είναι ζωντανά σε όλη τη διάρκεια του build. Μπορείτε να κάνετε την κατάσταση λίγο καλύτερη αλλάζοντας τις ρυθμίσεις εκτέλεσης του plugin, αλλά αυτό δεν είναι αρκετό. Στη χειρότερη περίπτωση, τα containers σας εξαντλούν τους πόρους του συστήματος πριν ξεκινήσουν οι δοκιμές!
Και αυτό δεν είναι το μόνο ζήτημα. Δεν μπορείτε να εκτελέσετε ούτε μία δοκιμή ολοκλήρωσης από το IDE σας. Πριν από αυτό, πρέπει να εκκινήσετε τα δοχεία με το χέρι. Επιπλέον, η επόμενη εκτέλεση του maven θα διαλύσει αυτά τα κοντέινερ (ρίξτε μια ματιά στο κάτω εκτέλεση).
Έτσι, αυτή η λύση είναι σαν ένα μεγάλο φορτηγό πλοίο. Αν όλα λειτουργούν καλά, τότε είναι εντάξει. Οποιαδήποτε απροσδόκητη ή ασυνήθιστη συμπεριφορά μας οδηγεί σε κάποιου είδους καταστροφή.
Δοκιμαστικά κοντέινερ - εκτέλεση κοντέινερ από δοκιμές
Τι θα γινόταν όμως αν μπορούσαμε να τρέξουμε τα κοντέινερ μας από δοκιμές; Αυτή η ιδέα φαίνεται καλή και ήδη υλοποιείται. Δοχεία δοκιμής, επειδή μιλάμε για αυτό το έργο, εδώ είναι μια λύση για τα προβλήματά μας. Δεν είναι ιδανική, αλλά κανείς δεν είναι τέλειος.
Αυτό είναι ένα Java βιβλιοθήκη, η οποία υποστηρίζει τις δοκιμές JUnit και Spock, παρέχοντας ελαφρούς και εύκολους τρόπους εκτέλεσης του δοχείου Docker. Ας της ρίξουμε μια ματιά και ας γράψουμε λίγο κώδικα!
Προϋποθέσεις και διαμόρφωση
Πριν ξεκινήσουμε, πρέπει να ελέγξουμε τη διαμόρφωσή μας. Δοχεία δοκιμών ανάγκη:
Docker στην έκδοση v17.09,
Java ελάχιστη έκδοση 1.8,
Πρόσβαση στο δίκτυο, ειδικά στο docker.hub.
Περισσότερες πληροφορίες σχετικά με τις απαιτήσεις για συγκεκριμένα λειτουργικά συστήματα και CI μπορείτε να βρείτε στο τεκμηρίωση.
Τώρα ήρθε η ώρα να προσθέσουμε μερικές γραμμές στο pom.xml.
org.testcontainers
testcontainers-bom
${testcontaines.version}
pom
import
org.postgresql
postgresql
runtime
org.testcontainers
postgresql
test
org.testcontainers
junit-jupiter
test
Χρησιμοποιώ Δοχεία δοκιμών έκδοση 1.17.3, αλλά μπορείτε να χρησιμοποιήσετε το νεότερο.
Δοκιμές με δοχείο Postgres
Το πρώτο βήμα είναι να προετοιμάσουμε την περίπτωση του δοχείου μας. Μπορείτε να το κάνετε αυτό απευθείας στη δοκιμή, αλλά μια ανεξάρτητη κλάση φαίνεται καλύτερη.
public class Postgres13TC extends PostgreSQLContainer {
private static final Postgres13TC TC = new Postgres13TC(),
private Postgres13TC() {
super("postgres:13.2"),
}
public static Postgres13TC getInstance() {
return TC,
}
@Override
public void start() {
super.start(),
System.setProperty("DB_URL", TC.getJdbcUrl()),
System.setProperty("DB_USERNAME", TC.getUsername()),
System.setProperty("DB_PASSWORD", TC.getPassword()),
}
@Override
public void stop() {
// δεν κάνουμε τίποτα. Αυτή είναι μια κοινή περίπτωση. Αφήστε την JVM να χειριστεί αυτή τη λειτουργία.
}
}
Στην αρχή των δοκιμών, θα δημιουργήσουμε μια περίπτωση του Postgres13TC. Αυτή η κλάση μπορεί να διαχειρίζεται πληροφορίες σχετικά με το δοχείο μας. Τα πιο σημαντικά εδώ είναι οι συμβολοσειρές σύνδεσης με τη βάση δεδομένων και τα διαπιστευτήρια. Τώρα ήρθε η ώρα να γράψουμε μια πολύ απλή δοκιμή.
Χρησιμοποιώ το JUnit 5 εδώ. Σημειώσεις @Testcontainers αποτελεί μέρος των επεκτάσεων που ελέγχουν τα εμπορευματοκιβώτια στο περιβάλλον δοκιμών. Βρίσκουν όλα τα πεδία με @Container σχολιασμό και τα δοχεία έναρξης και παύσης αντίστοιχα.
Δοκιμές με Spring Boot
Όπως ανέφερα προηγουμένως, χρησιμοποιώ το Spring Boot στο έργο. Σε αυτή την περίπτωση, πρέπει να γράψουμε λίγο περισσότερο κώδικα. Το πρώτο βήμα είναι να δημιουργήσουμε μια πρόσθετη κλάση διαμόρφωσης.
Αυτή η κλάση αντικαθιστά τις υπάρχουσες ιδιότητες με τιμές από την κλάση δοχείο δοκιμής. Οι τρεις πρώτες ιδιότητες είναι τυπικές ιδιότητες της Spring. Οι επόμενες πέντε είναι πρόσθετες, προσαρμοσμένες ιδιότητες που μπορούν να χρησιμοποιηθούν για τη διαμόρφωση άλλων πόρων και επεκτάσεων όπως η liquibase, π.χ.:
Τώρα ήρθε η ώρα να ορίσετε μια απλή δοκιμή ολοκλήρωσης.
@SpringBootTest(webEnvironment = RANDOM_PORT)
@AutoConfigureTestDatabase(replace = NONE)
@ContextConfiguration(initializers = ContainerInit.class)
@Testcontainers
class DummyRepositoryTest {
@Autowired
private DummyRepository,
@Test
void shouldReturnDummy() {
var byId = dummyRepository.getById(10L),
var expected = new Dummy(),
expected.setId(10L),
assertThat(byId).completes().emitsCount(1).emits(expected),
}
}
Έχουμε μερικές επιπλέον παρατηρήσεις εδώ.
@SpringBootTest(webEnvironment = RANDOM_PORT) - χαρακτηρίζει τη δοκιμή ως δοκιμή Spring Boot και ξεκινά το πλαίσιο spring.
@AutoConfigureTestDatabase(replace = NONE) - αυτές οι επισημάνσεις λένε ότι η επέκταση spring test δεν πρέπει να αντικαταστήσει τη ρύθμιση της βάσης δεδομένων postgres με H2 στη ρύθμιση της μνήμης.
@ContextConfiguration(initializers = ContainerInit.class) - ένα πρόσθετο πλαίσιο άνοιξη διαμόρφωση όπου ορίζουμε ιδιότητες από Δοχεία δοκιμών.
@Testcontainers - όπως αναφέρθηκε προηγουμένως, αυτή η σημείωση ελέγχει τον κύκλο ζωής του δοχείου.
Σε αυτό το παράδειγμα, χρησιμοποιώ reactive repositories, αλλά λειτουργεί το ίδιο με τα κοινά αποθετήρια JDBC και JPA.
Τώρα μπορούμε να εκτελέσουμε αυτή τη δοκιμή. Αν είναι η πρώτη εκτέλεση, η μηχανή πρέπει να τραβήξει εικόνες από το docker.hub. Αυτό μπορεί να πάρει λίγο χρόνο. Μετά από αυτό, θα δούμε ότι έχουν εκτελεστεί δύο κοντέινερ. Το ένα είναι το postgres και το άλλο είναι ο ελεγκτής Testcontainers. Αυτό το δεύτερο κοντέινερ διαχειρίζεται τα κοντέινερ που τρέχουν και ακόμη και αν το JVM σταματήσει απροσδόκητα, τότε απενεργοποιεί τα κοντέινερ και καθαρίζει το περιβάλλον.
Ας συνοψίσουμε
Δοχεία δοκιμών είναι πολύ εύχρηστα εργαλεία που μας βοηθούν να δημιουργήσουμε δοκιμές ολοκλήρωσης που χρησιμοποιούν δοχεία Docker. Αυτό μας δίνει μεγαλύτερη ευελιξία και αυξάνει την ταχύτητα ανάπτυξης. Η σωστή ρύθμιση της διαμόρφωσης των δοκιμών μειώνει τον χρόνο που απαιτείται για την επιβίβαση νέων προγραμματιστών. Δεν χρειάζεται να ρυθμίσουν όλες τις εξαρτήσεις, απλώς εκτελούν τις γραπτές δοκιμές με επιλεγμένα αρχεία διαμόρφωσης.