Περίληψη
Ένα πρόγραμμα που μεταγλωττίζεται και γράφει κάτι εύλογο δεν έχει γι' αυτό δοκιμαστεί. Αυτό το μάθημα διδάσκει να δοκιμάζουμε με μέθοδο: να εξάγουμε τις περιπτώσεις από την προδιαγραφή, κατά περιοχές και όρια· να τις συγκεντρώνουμε σε ένα πρόγραμμα αυτοματοποιημένων δοκιμών που μετρά τις αποτυχίες και τις αναφέρει· να επιβεβαιώνουμε με το assert ό,τι δεν μπορεί να συμβεί αν το πρόγραμμα είναι σωστό, και με το static_assert, κατά τη μεταγλώττιση, ό,τι πρέπει να υπολογίζει μια συνάρτηση constexpr· και να εκτελούμε τις δοκιμές με τους ανιχνευτές, που αποκαλύπτουν την απροσδιόριστη συμπεριφορά την οποία κρύβει ένα σωστό αποτέλεσμα. Όλα στηρίζονται σε μια προειδοποίηση: μια δοκιμή μπορεί να δείξει την παρουσία ενός σφάλματος, ποτέ την απουσία του.
Μαθησιακοί στόχοι
- Να εξάγει τις περιπτώσεις δοκιμής από την προδιαγραφή μιας συνάρτησης: τις διαμερίσεις της εισόδου της, τα όρια ανάμεσά τους και τις ειδικές περιπτώσεις, με το αποτέλεσμα που απαιτεί η μετασυνθήκη σε καθεμία.
- Να γράφει ένα πρόγραμμα αυτοματοποιημένων δοκιμών που συγκρίνει τα αποτελέσματα που λήφθηκαν με τα αναμενόμενα, αναφέρει κάθε αποτυχία χωρίς να σταματά και τελειώνει με μια σύνοψη και έναν κωδικό εξόδου που την αντικατοπτρίζει.
- Να χρησιμοποιεί το
assertγια να ελέγχει προσυνθήκες και αναλλοίωτες, και να εξηγεί γιατί εξαφανίζεται όταν μεταγλωττίζουμε με-DNDEBUGκαι γιατί η συνθήκη του δεν πρέπει να έχει παρενέργειες. - Να χρησιμοποιεί το
static_assertμε συναρτήσειςconstexprγια να δοκιμάζει αποτελέσματα κατά τη μεταγλώττιση, και να εκτελεί τις δοκιμές με τους ανιχνευτές για να αποκαλύπτει την απροσδιόριστη συμπεριφορά που τα αποτελέσματα δεν φανερώνουν.
Τι σημαίνει δοκιμή
Η δοκιμή ενός προγράμματος είναι η εκτέλεσή του με επιλεγμένες εισόδους και η σύγκριση αυτού που κάνει με αυτό που όφειλε να κάνει. Κάθε περίπτωση δοκιμής αποτελείται από δύο μέρη: μια είσοδο που ικανοποιεί την προσυνθήκη, και το αποτέλεσμα που απαιτεί γι' αυτήν η μετασυνθήκη, υπολογισμένο εκ των προτέρων και από άλλο δρόμο (με το χέρι, με άλλον τύπο, με έναν γνωστό πίνακα τιμών). Χωρίς το αναμενόμενο αποτέλεσμα δεν υπάρχει δοκιμή, αλλά μια εκτέλεση: να κοιτάξουμε την έξοδο και να τη βρούμε «εύλογη» είναι μια εντύπωση, όχι μια σύγκριση.
Ωστόσο, μια συνάρτηση με ένα όρισμα int δέχεται περισσότερες από τέσσερα δισεκατομμύρια εισόδους, και μια με δύο ορίσματα, περίπου ζεύγη· καμία συστοιχία δοκιμών δεν τις διατρέχει όλες. Αν αυτό ισχύει, οι δοκιμές εξετάζουν ένα ελάχιστο μέρος των δυνατών περιπτώσεων, και από το ότι περνούν όλες δεν έπεται ότι η συνάρτηση είναι σωστή. Ο Έντσχερ Ντάικστρα το διατύπωσε το 1970: οι δοκιμές μπορούν να δείξουν την παρουσία σφαλμάτων, ποτέ την απουσία τους. Η διάκριση είναι εκείνη που ο κύκλος μαθημάτων «Μαθηματική λογική I» θέτει ανάμεσα στον έλεγχο μεμονωμένων περιπτώσεων και την απόδειξη: να διαπιστώσουμε ότι ένας τύπος είναι αληθής για ορισμένες τιμές δεν αποδεικνύει ότι είναι αληθής για όλες, ενώ μια απόδειξη (σε αυτόν τον κύκλο μαθημάτων, η αναλλοίωτη του μαθήματος Σχεδίαση σωστών βρόχων) καλύπτει όλες τις περιπτώσεις ταυτόχρονα. Οι δοκιμές δεν αντικαθιστούν αυτόν τον συλλογισμό: βρίσκουν τα σφάλματα που του ξέφυγαν.
Από αυτό έπεται το κριτήριο για την επιλογή των περιπτώσεων. Αν οι δοκιμές δεν μπορούν να επιβεβαιώσουν την ορθότητα, η αξία τους βρίσκεται στο να βρίσκουν σφάλματα, και μια καλή περίπτωση δοκιμής είναι εκείνη που έχει τη μεγαλύτερη πιθανότητα να αποτύχει. Μια περίπτωση που ο συντάκτης διάλεξε επειδή ήξερε ότι θα λειτουργούσε δεν πληροφορεί τίποτα. Το πρόγραμμα που ακολουθεί το δείχνει με μια συνάρτηση που πρέπει να επιστρέφει το μεγαλύτερο στοιχείο ενός μη κενού διανύσματος.
#include <print>#include <vector>// Μεγαλύτερο στοιχείο μιας λίστας δεδομένων.// Προσυνθήκη: το values δεν είναι κενό.// Μετασυνθήκη: επιστρέφει το μεγαλύτερο στοιχείο του values.int largest(const std::vector<int>& values){ int best{0}; for (const int v : values) { if (v > best) { best = v; } } return best;}int main(){ std::println("{}", largest({3, 9, 4})); std::println("{}", largest({7})); std::println("{}", largest({1, 2, 3, 4})); std::println("{}", largest({-5, -2, -8}));}9740Τις τρεις πρώτες περιπτώσεις τις γράφει όποιος και να είναι πρώτες, και η συνάρτηση τις περνά: τα 9, 7 και 4 είναι σωστά. Η τέταρτη δίνει 0, μια τιμή που δεν βρίσκεται καν στο διάνυσμα, ενώ έπρεπε να δώσει . Το σφάλμα βρίσκεται στη γραμμή 9: το best ξεκινά από το 0 και όχι από το πρώτο στοιχείο, έτσι ώστε η συνάρτηση να είναι σωστή μόνο αν κάποιο στοιχείο είναι θετικό ή μηδέν. Οι τυπικές περιπτώσεις μοιράζονταν αυτή την ιδιότητα χωρίς να την έχει επιλέξει κανείς· η τέταρτη την παραβιάζει σκόπιμα. Η διόρθωση είναι int best{values[0]};, την οποία η προσυνθήκη καθιστά θεμιτή, επειδή εγγυάται ότι το values[0] υπάρχει.
Φόρτωση του περιεχομένου…
Ολοκληρώθηκε
Συνδεθείτε για να αποθηκεύσετε την πρόοδό σας