Μηχανική Λογισμικού ΙΙ Έλεγχος Λογισμικού JUNIT – TDD TFD + Refactoring Παναγιώτης Σφέτσος, PhD
Σκοπός 2Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Έλεγχοι παντού και πάντα (Testing…) Test Driven Development (TDD) Test-First Design (TFD) + Refactoring
Εισαγωγή 3Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Έλεγχοι μονάδας (Unit testing): Από τους προγραμματιστές Αυτοματοποιημένοι, ώστε να μπορούν να εκτελούνται επαναληπτικά Εργαλείο JUNIT Έλεγχοι ενσωμάτωσης (Integration testing): Από τους προγραμματιστές, καθώς ενσωματώνουν νέο κώδικα στη βάση κώδικα. Αυτοματοποιημένοι, ώστε να μπορούν να εκτελούνται επαναληπτικά Λειτουργικοί έλεγχοι συστήματος (Functional Testing): Εκτελούνται συνήθως από κάποια εξωτερική ομάδα ελέγχου Συνήθως είναι αυτοματοποιημένοι και έχουν την μορφή μαύρου κουτιού Έλεγχοι αποδοχής (Acceptance testing): Διενεργούνται από τον πελάτη ή τους εκπροσώπους του, στο δικό τους χώρο. Συνήθως είναι αυτοματοποιημένοι και έχουν την μορφή μαύρου κουτιού
Unit Testing – JUNIT 4Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Έλεγχοι μονάδας (Unit Testing) Το πλαίσιο ελέγχου (testing framework) - JUNIT Java και JUNIT (Beck και Gamma) – Ανοικτός κώδικας Η επιτυχία ή αποτυχία μιας δοκιμής στον κώδικα απεικονίζεται με την βοήθεια πράσινηςκόκκινης μπάρας μιας πράσινης ή κόκκινης μπάρας αντίστοιχα (στο γραφικό περιβάλλον). μια κλάση ελέγχου | δοκιμής Δημιουργούμε μια κλάση ελέγχου | δοκιμής για κάθε κλάση του προγράμματος της εφαρμογής μεθόδους δοκιμής Κάθε κλάση ελέγχου περιέχει ένα σύνολο από μεθόδους δοκιμής, που ελέγχουν τη σωστή λειτουργία των μεθόδων των κλάσεων της εφαρμογής Πλεονεκτήματα: Σωστός κώδικας, Τεκμηρίωση
Integration Testing – JUNIT 5Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Έλεγχοι ενσωμάτωσης (Integration testing) Εργαλείο συνήθως το JUNIT ακολουθιών δοκιμών Δημιουργία ακολουθιών δοκιμών (test suites) Οι ακολουθίες δοκιμών ενσωματώνουν όλες τις κλάσεις δοκιμών σε μία κλάση και με τον τρόπο αυτό εκτελούνται ταυτόχρονα όλα τα τεστ των κλάσεων της βάσης κώδικα Εύκολα και γρήγορα γνωρίζουμε ότι όλες οι κλάσεις μας λειτουργούν σωστά.
Εγκατάσταση του JUNIT (DOS) - 1 6Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Κατεβάστε το εργαλείο junit από τη διεύθυνση x.x Κάντε unzip των αρχείων στον επιθυμητό κατάλογο (συνήθως c:\junitx.x ) Ανοίξτε το παράθυρο γραμμής εντολών (Command Prompt) Ορίστε ως κατάλογο εργασίας τον φάκελο bin της έκδοσης java που έχετε: cd C:\Program Files\Java\jdk1.6.0_01\bin Με την CLASSPATH ορίστε την διαδρομή όπου βρίσκεται junit.jar αποθηκευμένο το junit.jar καθώς και την διαδρομή όπου βρίσκονται αποθηκευμένες οι κλάσεις δοκιμών που θέλετε να τρέξετε. (π.χ C:\Program Files\Java\jdk1.6.0_01\bin> set classpath=%classpath%;C:\junit3.8.1\junit.jar; C:\junit3.8.1;
Εγκατάσταση του JUNIT (DOS) - 2 Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Για να εμφανιστεί το παράθυρο του JUNIT και να δείτε το αποτέλεσμα της κλάσης δοκιμής σας εκτελέστε την ακόλουθη εντολή: C:\Program Files\Java\jdk1.6.0_01\bin> και μετά: για text: 1) java junit.textui.TestRunner junit.samples.AllTests για γραφική: 2) java junit.awtui.TestRunner junit.samples.AllTests για swing: 3) java junit.swingui.TestRunnerjunit.samples.AllTests Όπου: AllTests στην συγκεκριμένη περίπτωση είναι μία κλάση δοκιμής που υπάρχει στον φάκελο junit\samples του junitx.x. Αντί για AllTests εσείς θα γράφετε το όνομα της δικής σας κλάσης δοκιμής, έχοντας βέβαια ορίσει την κατάλληλη διαδρομή στην μεταβλητή CLASSPATH. 7
8Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Εγκατάσταση του JUNIT (DOS) - 3
Το πλαίσιο δοκιμών - JUNIT 9Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Το εργαλείο JUNIT αποτελείται από: πακέτα κώδικα της Java (packages) και περιπτώσεις ελέγχου (test cases) του κώδικα. επίπεδο μονάδας • Εκτελεί τους ελέγχους κώδικα σε επίπεδο μονάδας (unit testing) μαζικής επεξεργασίας ή μαζικής επεξεργασίας (batch mode-integration testing) – για ελέγχους ενσωμάτωσης. • Εκτελεί τους ελέγχους σαν τις μεθόδους μιας κλάσης. • Πολλαπλές περιπτώσεις ελέγχου εκτελούνται με την μορφή ακολουθιών δοκιμών ακολουθιών δοκιμών (test suites) περίπτωση ελέγχου • Η περίπτωση ελέγχου (test case) περιέχει μια συλλογή ελέγχων ακολουθία δοκιμών μονάδων ενώ η ακολουθία δοκιμών (test suite) περιέχει ένα σύνολο από περιπτώσεις ελέγχου.
Το μοντέλο JUNIT 10Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ
Κανόνες και ορισμοί Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Ο κύκλος ζωής της περίπτωσης ελέγχου (TestCase lifecycle) 1.setUp() // αρχικοποίηση 2.testXXX() // η μέθοδος ελέγχου ΧΧΧ 3.tearDown() // η διαγραφή •Επανάληψη βημάτων 1 έως 3 για κάθε μέθοδο testXXX … •Οι setUp() και tearDown() μπορούν να υπερφορτωθούν (overriding), αλλά και να παραληφθούν ανάλογα τις περιπτώσεις.
Κανόνες και ορισμοί Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Απλό Test: Εισάγουμε το πλαίσιο των junit-tests Ορίζουμε μια υποκλάση της TestCase. Υπερφορτώνουμε ή παραλείπουμε τις μεθόδους setUp() & tearDown(). Ορίζουμε μια ή περισσότερες δημόσιες μεθόδους testXXX(). Ελέγχουμε το αντικείμενο/α που λαμβάνουν μέρος στην δοκιμή. Βεβαιώνουμε (assert) τα αποτελέσματα. import junit.framework.TestCase; class Calculator { public double add(double x, double y) { return (x+y);} } public class TestCalculator extends TestCase { public void testAdd() public void testAdd() { Calculator calc = new Calculator(); double result = calc.add(50,10); assertEquals(60, result,0); } }
Κανόνες και ορισμοί Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Test με την χρήση της Test suit() Εισάγουμε το πλαίσιο των junit-tests Ορίζουμε μια υποκλάση της TestCase. Υπερφορτώνουμε ή παραλείπουμε τις μεθόδους setUp() & tearDown(). Ορίζουμε μια ή περισσότερες δημόσιες μεθόδους testXXX(). Ελέγχουμε το αντικείμενο/α που λαμβάνουν μέρος στην δοκιμή. Βεβαιώνουμε (assert) τα αποτελέσματα. Ορίζουμε μια στατική μέθοδο την suite() Ορίζουμε μια ακολουθία δοκιμών (TestSuite) Προαιρετικά ορίζουμε μια μέθοδο main() για να τρέξουμε την TestCase package TestExamples; import java.util.*; import junit.framework.*; public class FirstTest extends TestCase { public void testEmptyCollection() { Collection collection = new ArrayList(); assertTrue(collection.isEmpty()); } public static Test suite() { return new TestSuite(FirstTest.class); } public static void main(String args[]) { junit.textui.TestRunner.run(suite()); } }
Ο έλεγχος με την Test suit και main() 14Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ // 1 – εισάγουμε τις κλάσεις και μεθόδους της junit package TestExamples; import java.util.*; import junit.framework.*; // 2 – δημιουργούμε την υποκλάση της TestCase: public class FirstTest extends TestCase { // 3 – γράφουμε μια μέθοδο ελέγχου για να βεβαιωθούμε (to assert) : public void testEmptyCollection() { Collection collection = new ArrayList(); assertTrue(collection.isEmpty()); //τις εντολές assert θα δούμε αναλυτικά παρακάτω } // 4 – γράφουμε μια ακολουθία δοκιμών που περιέχει όλες τις μεθόδους TestXXX: public static Test suite() { return new TestSuite(FirstTest.class); } // 5 – γράφουμε την main() για να τρέξουμε το τεστ με αποτελέσματα σε μορφή κειμένου: public static void main(String args[]) { junit.textui.TestRunner.run(suite()); } }
Εκτελεστές Ελέγχων (TestRunners) 15Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ 6 – τρέχουμε το τεστ στο παράθυρο του dos (αποτελέσματα σε μορφή κειμένου): java TestExamples.FirstTest Το αποτέλεσμα θα είναι:. Time: 0 OK (1 tests) 7 – τρέχουμε το τεστ για αποτελέσματα σε γραφική μορφή – πράσινη μπάρα: java junit.swingui.TestRunner TestExamples.FirstTest
Οι Εντολές - assert 16Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ assertTrue(boolean condition) assertFalse(boolean condition) assertEquals(Object expected, Object actual) Χρησιμοποιεί την equals() σύγκριση (ίδια αντικείμενα) assertSame(Object expected, Object actual) assertNotSame(Object expected, Object actual) Χρησιμοποιεί την == σύγκριση (δύο αντικείμενα αναφέρονται στο ίδιο) assertEquals(float expected, float actual, float tolerance) assertNull(Object o) assertNotNull(Object o) fail(String message), κλπ……….. - Απλό μήνυμα λάθους
Ενοποιημένο περιβάλλον ανάπτυξης κώδικα java και τεστ ελέγχων (JUNIT). To περιβάλλον διατίθεται δωρεάν και μπορείτε να το κατεβάσετε από την διεύθυνση JCreator - 1 Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ 17
JCreator - 2 Για να τρέχουμε τις κλάσεις δοκιμών με το JUnit μέσα από τον JCreator κάνουμε τις παρακάτω ρυθμίσεις : Προσθέτουμε την διαδρομή του Junit στο παρακάτω πεδίο του μενού Project: Project Project Settings JDK version j2sdk1.4.2_04 edit add add archive c:\junit x.x \junit.jar Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ18
JCreator - 3 Προσθέτουμε την διαδρομή CLASSPATH του junit.jar στο παρακάτω πεδίο του μενού Build ή Run: Build Runtime Configuration default edit Run Application default edit Parameters Γράφουμε: -classpath ".;c:\junit3.8.1\junit.jar" junit.swingui.TestRunner Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ 19
Παράδειγμα – 2ο 20Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Θέλουμε να κατασκευάσουμε την κλάση - box που θα περιλαμβάνει μέθοδο υπολογισμού του όγκου του. Για να βεβαιωθούμε ότι η μέθοδος δουλεύει σωστά θα γράψουμε ένα τεστ ελέγχου (μέθοδο), ορίζοντας δύο διαφορετικά αντικείμενα. Γράφουμε πρώτα τον κώδικα της κλάσης : import junit.framework.*; public class BoxTest extends TestCase { class Box { double width; double height; double depth; / / O κατασκευαστής της Box. Box(double x, double y, double z) { width = x; height = y; depth = z; } //υπολογισμός και επιστροφή του όγκου double volume() {return width * height * depth; }}
Παράδειγμα – 2ο (συνέχεια..) 21Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Το τεστ ελέγχου public void testAdd() { // δήλωση, και αρχικοποίηση των αντικειμένων box Box mybox1 = new Box(10, 20, 15); Box mybox2 = new Box(3, 6, 9); double vol; // λήψη όγκου του πρώτου box vol = mybox1.volume(); assertTrue(vol == ); // λήψη όγκου του δευτέρου box vol = mybox2.volume(); assertTrue(vol == ); }}
Παράδειγμα – 2ο (συνέχεια..) 22Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ
Παράδειγμα – 3ο import junit.framework.*; public class SimpleTest extends TestCase { protected int fValue1; protected int fValue2; protected void setUp() { fValue1= 2; fValue2= 3; } public void testAdd() { double result= fValue1 + fValue2; assertTrue(result == 6); } public void testDivideByZero() { int zero= 0; int result= 8/(zero); } public void testEquals() { assertEquals(12, 12); assertEquals(12L, 12L); assertEquals(new Long(12), new Long(12)); assertEquals("Size", 12, 13); assertEquals("Capacity", 12.0, 11.00, 0.0); } Θα ελέγξουμε τους τύπους και τιμές μερικών μεταβλητών – Κάνουμε λάθη σε διαίρεση με το μηδέν, αλλά και σε τιμές μεταβλητών 23Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ
Παράδειγμα – 3ο 24Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ
Παράδειγμα – 3ο (Με τις διορθώσεις) import junit.framework.*; public class SimpleTest extends TestCase { protected int fValue1; protected int fValue2; protected void setUp() { fValue1= 2; fValue2= 3; } public void testAdd() { double result= fValue1 + fValue2; assertTrue(result == 5); } public void testDivideByZero() { int zero= 0; try{ int result= 8/(zero); } catch (Exception e) {} } public void testEquals() { assertEquals(12, 12); assertEquals(12L, 12L); assertEquals(new Long(12), new Long(12)); assertEquals("Size", 12, 12); assertEquals("Capacity", 12.0, 12.00, 0.0); } Διορθώνουμε τα λάθη, προσθέτουμε try – catch για να πιάσουμε το λάθος της διαίρεσης με το μηδέν. 25Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ
Παράδειγμα – 3ο (Με τις διορθώσεις) 26Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ
Δημιουργία ακολουθίας δοκιμών για όλες τις κλάσεις 27Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ Αν θέλουμε την main() για εκτέλεση όλων των κλάσεων δοκιμών, τότε δημιουργούμε την παρακάτω ξεχωριστή κλάση: import junit.framework.Test; import junit.framework.TestSuite; public class AllMyTestSuite { // Δημιουργούμε το σετ των τεστ (πλήθος 3) public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(A_Test.class); suite.addTestSuite(BoxTest.class); suite.addTestSuite(SimpleTest.class); return suite; } //Τρέχουμε το σετ των τεστ (suite) public static void main(String args[]) { junit.textui.TestRunner.run(suite()); } }
Δημιουργία ακολουθίας δοκιμών για όλες τις κλάσεις 28Παναγιώτης Σφέτσος, Μηχανική Λογισμικού ΙΙ