14. Ανάπτυξη οδηγούμενη από ελέγχους
Σύνοψη Έλεγχος μονάδας, βιβλιοθήκες ελέγχου μονάδας, η βιβλιοθήκη Unity, συγγραφή και εκτέλεση ελέγχων μονάδας με τη Unity, ανάπτυξη οδηγούμενη από ελέγχους.
Προαπαιτούμενη γνώση Τύποι δεδομένων, είσοδος/έξοδος, δομές επιλογής και επανάληψης, συναρτήσεις, πίνακες, δείκτες, αλφαριθμητικά, διαμέριση κώδικα.
14.1 Έλεγχος μονάδας
Ο έλεγχος λογισμικού (software testing) είναι συνυφασμένος με την ανάπτυξη λογισμικού. Πραγματοποιώντας ελέγχους λογισμικού παρέχονται «διαβεβαιώσεις» ότι το λογισμικό λειτουργεί ορθά με βάση καθορισμένες προδιαγραφές, καθώς για δεδομένες εισόδους επιβεβαιώνεται η ορθότητα των παραγόμενων αποτελεσμάτων. Ωστόσο, αν και ο έλεγχος λογισμικού μπορεί να εντοπίσει σφάλματα, δεν μπορεί να εγγυηθεί την απουσία τους.
Ο έλεγχος μονάδας (unit testing) είναι ένας αυτόματος μηχανισμός που καλεί μια μονάδα του λογισμικού (π.χ. μια συνάρτηση) και επιβεβαιώνει ότι για συγκεκριμένες εισόδους επιστρέφονται τα θεωρούμενα ως ορθά αποτελέσματα. Κάθε μοναδιαίος έλεγχος (unit test) είναι ένα σύντομο τμήμα κώδικα που εξετάζει, σχετικά ανεξάρτητα από τον υπόλοιπο κώδικα, μια μονάδα λογισμικού και επιστρέφει ως αποτέλεσμα την επιτυχία ή την αποτυχία του ελέγχου. Είναι γενικά αποδεκτό ότι οι έλεγχοι μονάδων που γίνονται κατά τη διαδικασία ανάπτυξης του λογισμικού λειτουργούν θετικά ως προς την ποιότητα και τον χρόνο ανάπτυξης του λογισμικού. Αυτό συμβαίνει διότι ορισμένα λάθη εντοπίζονται νωρίς, πριν δημιουργήσουν μεγαλύτερα προβλήματα. Ίσως περισσότερο σημαντικό είναι ότι οι προγραμματιστές συνηθίζουν να σκέφτονται ότι ο κώδικάς τους πρέπει να είναι σε θέση να ικανοποιεί τις προδιαγραφές των ελέγχων, οπότε οδηγούνται σχετικά αυτόματα σε λύσεις με καλύτερα ποιοτικά χαρακτηριστικά. Επιπλέον, πιθανές αλλαγές κώδικα μπορούν αυτόματα να ελέγχονται σχετικά με το εάν «σπάνε» κάπου τον υπάρχοντα κώδικα.
14.1.1 Βιβλιοθήκες ελέγχου μονάδας για τη C
Η C μπορεί να υποστηρίξει μοναδιαίους ελέγχους μέσω εξωτερικών βιβλιοθηκών, ενώ ένας απλοϊκός τρόπος υλοποίησης μοναδιαίων ελέγχων μπορεί να εφαρμοστεί με την κλήση της συνάρτησης assert() που παρουσιάζεται στο Κεφάλαιο 15. Σε ό,τι αφορά τις εξωτερικές βιβλιοθήκες, υπάρχουν πολλές διαθέσιμες επιλογές και μερικές από τις πλέον διαδεδομένες περιπτώσεις παρουσιάζονται στην ακόλουθη λίστα:
-
Cgreen (1)
-
Cpputest (1)
-
Criterion (1)
-
LCUT (1)
-
MinUnit (1)
-
Unity (1)
Στη συνέχεια του κεφαλαίου θα χρησιμοποιηθεί η βιβλιοθήκη Unity λόγω του ότι είναι απλή για βασική χρήση, αλλά διαθέτει και προχωρημένες δυνατότητες που μπορεί κανείς να χρησιμοποιήσει εφόσον επιθυμεί. Επιπλέον, η Unity μπορεί να εγκατασταθεί εύκολα, υποστηρίζει και τα τρία πλέον διαδεδομένα λειτουργικά συστήματα (Windows, Linux, MacOS) και ο κώδικάς της έχει γραφεί εξολοκλήρου σε C σύμφωνα με τα ANSI πρότυπα. Αξίζει να σημειωθεί ότι η Unity χρησιμοποιείται από τη δημοφιλή ιστοσελίδα εκμάθησης και εξάσκησης στον προγραμματισμό https://exercism.org/ σε ό,τι αφορά το C track που προσφέρει, γεγονός που υποδηλώνει ότι πρόκειται για ώριμο λογισμικό. Τέλος, μπορεί να χρησιμοποιηθεί και για έλεγχο κώδικα σε ενσωματωμένα συστήματα (embedded systems). Το βιβλίο 1 εστιάζει ακριβώς σε αυτό το θέμα χρησιμοποιώντας τη Unity.
14.2 Unity
Όπως αναφέρθηκε στην προηγούμενη παράγραφο, η Unity είναι μια βιβλιοθήκη ελέγχων μονάδας για τη C. Στο απλούστερο σενάριο, για να χρησιμοποιηθεί, αρκεί να βρίσκονται τα αρχεία unity.c, unity.h και unity_internals.h σε θέσεις που να είναι γνωστές στον μεταγλωττιστή. Τα αρχεία αυτά μπορούν να μεταφορτωθούν από τη σελίδα της Unity. Αν και η Unity μπορεί να χρησιμοποιηθεί για περαιτέρω αυτοματοποίηση των ελέγχων, σε συνδυασμό με τις βιβλιοθήκες Ceedling και CMock που έχουν αναπτυχθεί επίσης από την ίδια ομάδα (ThrowTheSwitch.org), η περιγραφή και τα παραδείγματα που θα ακολουθήσουν χρησιμοποιούν μόνο τη Unity.
Η βιβλιοθήκη διαθέτει διαφόρων ειδών assertions, δηλαδή εντολών που επαληθεύουν ότι οι τιμές που υπολογίζονται από τον κώδικα της εφαρμογής είναι οι αναμενόμενες. Υπάρχουν πολλές συναρτήσεις επαλήθευσης τιμών TEST_ASSERT. Στη συνέχεια θα γίνει αναφορά στις σημαντικότερες από αυτές ανά ομάδα.
Επαλήθευση λογικών τιμών. Οι συναρτήσεις που ακολουθούν επαληθεύουν την τιμή μιας λογικής συνθήκης (condition).
- TEST_ASSERT_TRUE(condition) - Αν η συνθήκη είναι αληθής τότε ο έλεγχος επιτυγχάνει, αλλιώς αποτυγχάνει.
- TEST_ASSERT_FALSE(condition) - Αν η συνθήκη είναι ψευδής τότε ο έλεγχος επιτυγχάνει, αλλιώς αποτυγχάνει.
- TEST_ASSERT_NULL(pointer) - Αν ο pointer είναι NULL τότε ο έλεγχος επιτυγχάνει, αλλιώς αποτυγχάνει.
- TEST_ASSERT_ΝΟΤ_NULL(pointer) - Αν ο pointer δεν είναι NULL τότε ο έλεγχος επιτυγχάνει, αλλιώς αποτυγχάνει.
Επαλήθευση ακέραιων τιμών. Υπάρχουν ξεχωριστές ομάδες συναρτήσεων για επαλήθευση ακεραίων με πρόσημο, ακεραίων χωρίς πρόσημο καθώς και υποστήριξη για διάφορα μεγέθη ακεραίων (8bits, 16bits, 32bits, 64bits).
- TEST_ASSERT_EQUAL_INT(expected, actual) - Αν η αναμενόμενη (expected) τιμή είναι ίση με την τιμή που υπολογίζεται (actual), τότε ο έλεγχος επιτυγχάνει, αλλιώς αποτυγχάνει.
- TEST_ASSERT_EQUAL_INT_WITHIN(delta, expected, actual) - Ο έλεγχος επιτυγχάνει αν η int τιμή actual απέχει το πολύ delta από την int τιμή expected, αλλιώς αποτυγχάνει.
Επαλήθευση πραγματικών τιμών. Υπάρχουν συναρτήσεις για επαλήθευση float και double αποτελεσμάτων.
- TEST_ASSERT_FLOAT(expected, actual) - Αν η αναμενόμενη (expected) τιμή είναι ίση με την τιμή που υπολογίζεται (actual), τότε ο έλεγχος επιτυγχάνει, αλλιώς αποτυγχάνει.
- TEST_ASSERT_DOUBLE(expected, actual) - Αντίστοιχα με την παραπάνω για double τιμές όμως.
- TEST_ASSERT_FLOAT_WITHIN(delta, expected, actual) - Ο έλεγχος επιτυγχάνει αν η τιμή actual απέχει το πολύ delta από την τιμή expected, αλλιώς αποτυγχάνει.
- TEST_ASSERT_DOUBLE_WITHIN(delta, expected, actual) - Αντίστοιχα με την παραπάνω για double τιμές όμως.
Επαλήθευση τιμών δεικτών (και λεκτικών).
- TEST_ASSERT_EQUAL_PTR(expected, actual) - Αν η expected τιμή δείκτη είναι ίση με την τιμή δείκτη actual, τότε ο έλεγχος επιτυγχάνει, αλλιώς αποτυγχάνει.
- TEST_ASSERT_EQUAL_STRING(expected, actual) - Αν το λεκτικό expected είναι ίσο χαρακτήρα προς χαρακτήρα με το λεκτικό actual και τα δύο λεκτικά τερματίζονται ορθά με τον NULL χαρακτήρα τότε ο έλεγχος επιτυγχάνει, αλλιώς αποτυγχάνει.
- TEST_ASSERT_EQUAL_STRING_LEN(expected, actual, len) - Αν το λεκτικό expected είναι ίσο χαρακτήρα προς χαρακτήρα μέχρι το μήκος len, τότε ο έλεγχος επιτυγχάνει, αλλιώς αποτυγχάνει.
Εκτός από τις ανωτέρω ομάδες συναρτήσεων ελέγχου, η Unity υποστηρίζει επαλήθευση δεκαεξαδικών τιμών, επαλήθευση bits, επαλήθευση πινάκων και επαλήθευση δομών και μνήμης. Για κάθε συνάρτηση υπάρχει και μια παραλλαγή της όπου το αναγνωριστικό της τελειώνει με _MESSAGE και δέχεται μια επιπλέον, τελευταία στη σειρά παράμετρο, που αντιστοιχεί στο μήνυμα που επιθυμούμε να εμφανίζεται, όταν ο έλεγχος θα αποτυγχάνει. Για παράδειγμα, για την TEST_ASSERT_EQUAL_INT64(expected, actual) η αντίστοιχη συνάρτηση με προσαρμοσμένο μήνυμα είναι η TEST_ASSERT_EQUAL_INT64_MESSAGE(expected, actual, message).
Μια άλλη χρήσιμη συνάρτηση είναι η TEST_FAIL(), που προκαλεί την άμεση αποτυχία ενός ελέγχου. Κάτι τέτοιο βρίσκει εφαρμογή για παράδειγμα όταν ένας έλεγχος αφορά κώδικα που δεν έχει ακόμα υλοποιηθεί. Καλώντας τη συνάρτηση TEST_FAIL() ο έλεγχος αποτυγχάνει και συνεπώς εμφανίζεται στη λίστα ελέγχων που πρέπει να επιδιορθωθούν στη συνέχεια. Με αυτόν τον τρόπο ο προγραμματιστής πληροφορείται για τον κώδικα που πρέπει να υλοποιηθεί κατά προτεραιότητα.
Επίσης, η Unity δίνει τη δυνατότητα να αγνοηθούν ένας ή περισσότεροι έλεγχοι με χρήση της συνάρτησης TEST_IGNORE(). Η προσθήκη μιας κλήσης της TEST_IGNORE() πάνω από κλήσεις TEST_ASSERT οδηγεί στο να αγνοηθούν κατά την εκτέλεση των ελέγχων οι κλήσεις των TEST_ASSERT. Αυτό μπορεί να είναι χρήσιμο όταν επιθυμούμε να ασχοληθούμε σταδιακά με την υλοποίηση κώδικα που «περνά» ένα προς ένα τα TEST_ASSERT. Τοποθετούμε το TEST_IGNORE() αμέσως μετά από το πρώτο TEST_ASSERT, το οποίο αρχικά αποτυγχάνει, υλοποιούμε τον κώδικα που το κάνει να επιτυγχάνει και μόλις συμβεί αυτό μεταφέρουμε το TEST_IGNORE() κάτω από το επόμενο TEST_ASSERT και επαναλαμβάνουμε τη διαδικασία μέχρι
η TEST_IGNORE() να βρεθεί κάτω από το τελευταίο TEST_ASSERT. Αυτή μάλιστα είναι η διαδικασία που χρησιμοποιεί η σελίδα https://exercism.org/ για τον έλεγχο της ορθότητας των λύσεων που υποβάλλουν οι χρήστες του.
14.2.1 Πρακτική χρήση της Unity
Συνήθως, δημιουργείται ένα αρχείο test για κάθε C module που επιθυμούμε να ελέγξουμε στο οποίο γίνεται include το utility.h και το αρχείο επικεφαλίδας του module. Το αρχείο test μπορεί να περιέχει τις συναρτήσεις setUp() και tearDown(). Στην πρώτη συνάρτηση συμπεριλαμβάνουμε οτιδήποτε επιθυμούμε να εκτελεστεί πριν κάθε test και στη δεύτερη οτιδήποτε επιθυμούμε να εκτελεστεί μετά. Το αρχείο test συμπληρώνεται με συναρτήσεις που ξεκινούν είτε με τη λέξη test_ είτε με τη λέξη spec_, αν και αυτό είναι απλά μια σύμβαση. Όλες οι συναρτήσεις που αναφέρθηκαν δεν δέχονται ορίσματα και δεν επιστρέφουν κάποια τιμή. Στο τέλος του αρχείου test συμπληρώνεται η συνάρτηση main() που καλεί αρχικά τη συνάρτηση UNITY_BEGIN(), μετά την RUN_TEST για κάθε test συνάρτηση, και τελικά τη συνάρτηση UNITY_END().
14.2.2 Παράδειγμα χρήσης της Unity
Η λειτουργία της Unity θα επιδειχθεί στη συνέχεια μέσω ενός υποθετικού σεναρίου στο οποίο σταδιακά θα συμπληρωθεί ο κώδικας συναρτήσεων που θα ελέγχουν ότι ένα συνθηματικό ικανοποιεί τους ακόλουθους κανόνες:
- Nα έχει μήκος από 5 μέχρι και 20 χαρακτήρες.
- Να περιέχει τουλάχιστον 1 ψηφίο.
- Να περιέχει τουλάχιστον 1 πεζό γράμμα της αγγλικής αλφαβήτου.
- Να περιέχει τουλάχιστον 1 κεφαλαίο γράμμα της αγγλικής αλφαβήτου.
Ειδικότερα, ζητείται για τις συναρτήσεις που δηλώνονται στο ch14_p1.h (κώδικας 14.1) να συμπληρωθεί ο κώδικάς τους στο ch14_p1.c (κώδικας 14.2) έτσι ώστε να επαληθεύονται όλοι οι έλεγχοι που βρίσκονται στο ch14_p1_test.c (κώδικας 14.3). Παρατηρήστε ότι στην παρούσα φάση το ch14_p1_test.c περιλαμβάνει μόνο stubs συναρτήσεων. Ένα stub συνάρτησης είναι μια συνάρτηση η οποία μπορεί να κληθεί χωρίς να προκαλείται σφάλμα, αλλά η ίδια δεν επιτελεί κάποιο έργο. Ο σκοπός της είναι να λειτουργήσει ως «κράτηση θέσης» (placeholder) στην οποία, σε κάποια μετέπειτα φάση, θα συμπληρωθεί κώδικας που θα παρέχει την επιθυμητή λειτουργικότητα.
Κώδικας 14.1: ch14_p1.h - αρχείο επικεφαλίδας με συναρτήσεις που πρέπει να υλοποιηθούν. | |
---|---|
Η μεταγλώττιση και εκτέλεση των ελέγχων γίνεται ως εξής:
$ gcc unity.c ch14_p1.c ch14_p1_test.c ‐o ch14_p1
$ ./ch14_p1
ch14_p1_test.c:9:test_has_size_ok:FAIL: Expected TRUE Was FALSE
ch14_p1_test.c:50:test_has_size_ok:IGNORE: Not implemented yet
Ο έλεγχος αποτυγχάνει στη γραμμή 9 του ch14_p1_test.c καθώς η αναμενόμενη τιμή ήταν true, αλλά επιστράφηκε η τιμή false. Αυτό συνέβη διότι ο κώδικας της συνάρτησης has_size_ok() στο ch14_p1.c δεν έχει συμπληρωθεί επί της ουσίας, αλλά απλά επιστρέφει πάντα την τιμή false. Το δεύτερο μήνυμα προκαλείται από την κλήση της TEST_IGNORE_MESSAGE η οποία εμφανίζει το μήνυμα που δέχεται ως όρισμα και αποτρέπει την εκτέλεση των ελέγχων που ακολουθούν. Συνεπώς, το επόμενο βήμα μας θα πρέπει να είναι η ορθή υλοποίηση της συνάρτησης has_size_ok(). Αυτό συμβαίνει με τον ακόλουθο κώδικα (κώδικας 14.4) να αντικαθιστά τον κώδικα της συνάρτησης has_ok_size() στο ch14_p1.c.
bool has_ok_size(const char *pwd) {
int size = strlen(pwd);
if (size >= 5 && size <= 20)
return true;
else
return false;
}
Τώρα, η μεταγλώττιση και εκτέλεση των ελέγχων θα επιστρέψει τα ακόλουθα:
$ gcc unity.c ch14_p1.c ch14_p1_test.c ‐o ch14_p1
$ ./ch14_p1
ch14_p1_test.c:49:test_has_size_ok:PASS
ch14_p1_test.c:50:test_has_size_ok:IGNORE: Not implemented yet
Μεταφέροντας την κλήση της συνάρτησης TEST_IGNORE_MESSAGE() κάτω από την επόμενη κλήση συνάρτησης ελέγχου, την RUN_TEST(test_has_digit), λαμβάνουμε το ακόλουθο μήνυμα κατά τη μεταγλώττιση και εκτέλεση.
$ gcc unity.c ch14_p1.c ch14_p1_test.c ‐o ch14_p1
$ ./ch14_p1
ch14_p1_test.c:49:test_has_size_ok:PASS
ch14_p1_test.c:18:test_has_digit:FAIL: Expected TRUE Was FALSE
ch14_p1_test.c:51:test_has_digit:IGNORE: Not implemented yet
Τώρα θα πρέπει να συμπληρωθεί ορθή υλοποίηση για τη συνάρτηση has_digit(). Η διαδικασία επαναλαμβάνεται μέχρι που ο κώδικας 14.5 στη ch14_p1.c να είναι ο ακόλουθος:
Τώρα, η μεταγλώττιση και εκτέλεση των ελέγχων θα επιστρέψει τα ακόλουθα:
$ gcc unity.c ch14_p1.c ch14_p1_test.c ‐o ch14_p1
$ ./ch14_p1
ch14_p1_test.c:49:test_has_size_ok:PASS
ch14_p1_test.c:50:test_has_digit:PASS
ch14_p1_test.c:51:test_has_lowercase_letter:PASS
ch14_p1_test.c:52:test_has_uppercase_letter:PASS
ch14_p1_test.c:53:test_is_valid_password:PASS
14.3 Ανάπτυξη οδηγούμενη από ελέγχους
Η οδηγούμενη από ελέγχους ανάπτυξη (TDD = Test Driven Development) είναι ένας τρόπος ανάπτυξης λογισμικού όπου γράφονται πρώτα οι έλεγχοι μονάδας, που αρχικά αποτυγχάνουν, και στη συνέχεια γράφονται οι μονάδες κώδικα που κάνουν τους ελέγχους να επιτύχουν. Έμφαση δίνεται στην αυτοματοποίηση της διαδικασίας με μεγάλο αριθμό μικρών ελέγχων, που μπορούν να εκτελεστούν μαζικά και συνήθως γρήγορα. Αυτό ίσως να δημιουργεί ενστάσεις καθώς μπορεί να έχει εδραιωθεί σε κάποιους προγραμματιστές η αντίληψη ότι «οι έλεγχοι γράφονται για κώδικα που δεν έχει την ορθή συμπεριφορά και εφόσον βέβαια έχει γραφεί ο κώδικας». Ωστόσο, στην πράξη, σφάλματα μπορούν να υπεισέρχονται στον κώδικα σε κάθε φάση ανάπτυξής του και η πολιτική της συγγραφής ελέγχων πρώτα και πριν την ανάπτυξη του κάθε νέου τμήματος κώδικα ή την τροποποίηση παλαιότερων τμημάτων κώδικα φαίνεται να οδηγεί σε κώδικα με λιγότερα σφάλματα συνολικά.
Με δεδομένο ότι η ανάπτυξη λογισμικού είναι από τις συνθετότερες κατασκευές που μπορεί να δημιουργήσει ο άνθρωπος, είναι λογικό να αναμένουμε ότι σφάλματα θα υπάρχουν λόγω ακριβώς της ανθρώπινης φύσης. Η TDD αποτελεί έναν συστηματικό τρόπο με τον οποίο μεμονωμένοι προγραμματιστές ή και ομάδες προγραμματιστών μπορούν να ελέγξουν την πολυπλοκότητα και να ολοκληρώσουν σύνθετα έργα λογισμικού.
Το παράδειγμα της παραγράφου 14.2.2 μπορεί να θεωρηθεί ως ένα απλοϊκό παράδειγμα TDD, καθώς αρχικά γράφηκαν οι έλεγχοι που θα έπρεπε να επαληθεύονται στο αρχείο ch14_p1_test.c, ενώ ο κώδικας που υλοποιεί τη λειτουργικότητα, π.χ. η συνάρτηση has_size_ok() που δηλώνεται στο ch14_p1.h και ορίζεται στο ch14_p1.c έχει κενό σώμα. Άρα, αρχικά ο έλεγχος για τη has_size_ok() αποτυγχάνει, αλλά στη συνέχεια συμπληρώνοντας ο προγραμματιστής ορθά τον κώδικα της συνάρτησης ο έλεγχος θα επιτύχει. Η διαδικασία συνεχίζεται με αυτόν τον τρόπο επαυξητικά έτσι ώστε να υλοποιηθεί τελικά η πλήρης ζητούμενη λειτουργικότητα. Παράλληλα, με την ολοκλήρωση της διαδικασίας μένει διαθέσιμο και ένα σύνολο ελέγχων που αυτόματα μπορούν να ξανά-εκτελεστούν αν ο κώδικας χρειαστεί να τροποποιηθεί.
14.4 Ασκήσεις
Άσκηση 1
Επεκτείνετε το παράδειγμα της παραγράφου 14.2.2 έτσι ώστε να προστεθούν οι δύο ακόλουθοι επιπλέον έλεγχοι για κάθε συνθηματικό:
-
Να μην είναι ένα από 20 εύκολα συνθηματικά που εντοπίζονται σε μια λίστα επισφαλών συνθη ματικών όπως στη https://www.tomsguide.com/news/worst‐passwords‐2022
-
Να περιέχει τουλάχιστον 1 ειδικό χαρακτήρα από τους χαρακτήρες που ορίζονται ως ειδικοί στο https://owasp.org/www-community/password-special-characters
Χρησιμοποιήστε τα αρχεία ch14_p1.h, ch14_p1.c και ch14_p1_test.c πραγματοποιώντας αλλαγές μόνο στο ch14_p1.c.
Λύση άσκησης 1
Άσκηση 2
Υλοποιήστε μια συνάρτηση long factorial(int n) που υπολογίζει το παραγοντικό ενός μη αρνητικού ακεραίου n. Το παραγοντικό του n (συμβολίζεται με n!) είναι το γινόμενο όλων των θετικών ακεραίων μικρότερων ή ίσων του n, ενώ ισχύει επιπλέον ότι το 0! είναι 1. Γράψτε τους ακόλουθους μοναδιαίους ελέγχους:
- Μοναδιαίος έλεγχος για το παραγοντικό του 0 που πρέπει να είναι 1.
- Μοναδιαίος έλεγχος για το παραγοντικό του 1 που πρέπει να είναι 1.
- Μοναδιαίος έλεγχος για το παραγοντικό του 5 που πρέπει να είναι 120.
- Μοναδιαίος έλεγχος για το παραγοντικό του 10 που πρέπει να είναι 3628800.
- Μοναδιαίος έλεγχος για το παραγοντικό μιας αρνητικής τιμής. Η συνάρτηση θα πρέπει να επιστρέφει μια τιμή που έχει συμφωνηθεί ότι αντιστοιχεί σε αδυναμία υπολογισμού (π.χ. -1).
Λύση άσκησης 2
Άσκηση 3
Υλοποιήστε μια συνάρτηση int find_index(int* array, int size, int element) που επιστρέφει τη θέση (index) ενός στοιχείου element εντός ενός πίνακα array με μέγεθος size. Αν το στοιχείο δεν υπάρχει, η συνάρτηση θα πρέπει να επιστρέφει -1. Γράψτε τους ακόλουθους μοναδιαίους ελέγχους:
- Μοναδιαίος έλεγχος για έλεγχο ότι η συνάρτηση επιστρέφει τη σωστή θέση όταν το στοιχείο που αναζητείται υπάρχει στον πίνακα.
- Μοναδιαίος έλεγχος για έλεγχο ότι η συνάρτηση επιστρέφει την τιμή -1 όταν το στοιχείο που αναζητείται δεν υπάρχει στον πίνακα.
- Μοναδιαίος έλεγχος για έλεγχο στην περίπτωση που ο πίνακας έχει μηδενικό μέγεθος.
- Μοναδιαίος έλεγχος για έλεγχο στην περίπτωση που το στοιχείο που αναζητείται υπάρχει πολλές φορές στον πίνακα (θα πρέπει να επιστρέφει τη θέση της πρώτης εμφάνισης από αριστερά προς
τα δεξιά).
Τοποθετήστε τη συνάρτηση int find_index(int* array, int size, int element) σε ξεχωριστό αρχείο από το αρχείο κώδικα το οποίο θα περιέχει τους ελέγχους.
Άσκηση 4
Υλοποιήστε λύσεις στα ακόλουθα προβλήματα από το C track του exercism:
- Απόσταση hamming https://exercism.org/tracks/c/exercises/hamming
- Πυθαγόρειες τριάδες https://exercism.org/tracks/c/exercises/pythagorean‐triplet
- Υλοποίηση excel-like λειτουργικότητας https://exercism.org/tracks/c/exercises/react
-
James Grenning. “Test Driven Development for Embedded C”. Στο: Test Driven Development for Embedded C (2011), σσ. 1–356. ↩