Προγραμματισμός

Δοκιμάστε εφαρμογές Web με το HttpUnit

Σε μια τυπική εταιρική εφαρμογή, πολλοί τομείς απαιτούν δοκιμές. Ξεκινώντας από τα απλούστερα στοιχεία, τάξεις, οι προγραμματιστές ή οι εξειδικευμένοι προγραμματιστές δοκιμών πρέπει να προγραμματίσουν δοκιμές μονάδας για να διασφαλίσουν ότι οι μικρότερες μονάδες της εφαρμογής συμπεριφέρονται σωστά. Κάθε συστατικό μπορεί ενδεχομένως να περάσει τις δοκιμές μονάδας μόνο του. Ωστόσο, οι προγραμματιστές πρέπει να διασφαλίσουν ότι συνεργάζονται όπως αναμενόταν - ως μέρος ενός υποσυστήματος και ως μέρος ολόκληρης της εφαρμογής - ως εκ τούτου, δοκιμές ολοκλήρωσης πρέπει να εκτελεστεί. Σε ορισμένα έργα, οι απαιτήσεις απόδοσης πρέπει να πληρούνται, έτσι ώστε οι μηχανικοί διασφάλισης ποιότητας να αποδίδουν δοκιμές φορτίου για επαλήθευση και τεκμηρίωση της απόδοσης της εφαρμογής υπό διάφορες συνθήκες. Κατά την ανάπτυξη εφαρμογών, οι μηχανικοί διασφάλισης ποιότητας εκτελούν αυτοματοποιημένα και χειροκίνητα λειτουργικές δοκιμές για να ελέγξετε τη συμπεριφορά της εφαρμογής από την άποψη του χρήστη. Όταν ένα αναπτυξιακό έργο ολοκληρώνει σχεδόν ένα συγκεκριμένο ορόσημο, δοκιμές αποδοχής μπορεί να εκτελεστεί για να επαληθευτεί ότι η εφαρμογή πληρούσε τις απαιτήσεις.

Το HttpUnit είναι ένα πλαίσιο που βασίζεται στο JUnit, το οποίο επιτρέπει την εφαρμογή αυτοματοποιημένων δοκιμαστικών σεναρίων για εφαρμογές Web. Είναι πιο κατάλληλο για την εφαρμογή αυτοματοποιημένων λειτουργικών δοκιμών ή δοκιμών αποδοχής. Όπως υποδηλώνει το όνομα, μπορεί να χρησιμοποιηθεί για δοκιμές μονάδας. Ωστόσο, τυπικά στοιχεία επιπέδου Web όπως σελίδες JSP (JavaServer Pages), servlets και άλλα στοιχεία προτύπου δεν προσφέρονται για δοκιμή μονάδας. Όσον αφορά τα διάφορα στοιχεία που βασίζονται στο πλαίσιο MVC (Model-View Controller), αυτά ταιριάζουν καλύτερα για δοκιμές με άλλα πλαίσια δοκιμών. Οι ενέργειες Struts μπορούν να ελεγχθούν μονάδες με StrutsUnit και οι ενέργειες WebWork 2 μπορούν να δοκιμαστούν μονάδες χωρίς κοντέινερ Web, για παράδειγμα.

Στόχοι δοκιμής

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

Μια τυπική εταιρική εφαρμογή Web (ή ένας σύνθετος ιστότοπος) έχει πολλά έγγραφα που περιγράφουν τις απαιτήσεις των διαφόρων χρηστών ή των συντηρητών εφαρμογών. Αυτά μπορεί να περιλαμβάνουν προδιαγραφές περίπτωσης χρήσης, προδιαγραφές μη λειτουργικών απαιτήσεων, προδιαγραφές περίπτωσης δοκιμής που προέρχονται από άλλα αντικείμενα, έγγραφα σχεδίασης διεπαφής χρήστη, κοροϊδεύω, προφίλ ηθοποιών και διάφορα πρόσθετα αντικείμενα. Για μια απλή εφαρμογή, ολόκληρη η προδιαγραφή θα μπορούσε ενδεχομένως να αποτελείται από ένα απλό αρχείο κειμένου με μια λίστα απαιτήσεων.

Από αυτά τα έγγραφα, πρέπει να δημιουργήσουμε μια οργανωμένη λίστα δοκιμαστικών περιπτώσεων. Κάθε περίπτωση δοκιμής περιγράφει ένα σενάριο που μπορεί να επιτευχθεί από έναν επισκέπτη Web μέσω ενός προγράμματος περιήγησης στο Web. Μια καλή πρακτική είναι να στοχεύσετε σε σενάρια παρόμοιου μεγέθους - τα μεγαλύτερα σενάρια μπορούν να αναλυθούν σε μικρότερα κομμάτια. Πολλά εξαιρετικά βιβλία και άρθρα συζητούν τη δημιουργία προδιαγραφών δοκιμής. Για αυτό το άρθρο, ας υποθέσουμε ότι έχετε ένα σύνολο στοιχείων που θέλετε να δοκιμάσετε για την εφαρμογή Ιστού σας, οργανωμένα σε σύνολα σεναρίων δοκιμαστικών περιπτώσεων.

Ώρα να κατεβάσετε υλικό!

Εντάξει, τώρα γνωρίζουμε τα βαρετά πράγματα, ας κατεβάσουμε μερικά ωραία παιχνίδια! Πρώτα απ 'όλα, χρειαζόμαστε ένα εγκατεστημένο Java 2 SDK για τη μεταγλώττιση και την εκτέλεση των δοκιμών μας. Στη συνέχεια, πρέπει να κατεβάσετε το πλαίσιο HttpUnit - επί του παρόντος στην έκδοση 1.5.5. Το δυαδικό πακέτο περιέχει όλες τις απαιτούμενες βιβλιοθήκες τρίτων. Θα χρειαστούμε επίσης το εργαλείο κατασκευής Ant για να εκτελέσουμε τις δοκιμές και να δημιουργήσουμε αυτόματα αναφορές. Οποιαδήποτε αρκετά πρόσφατη έκδοση αυτών των εργαλείων θα λειτουργούσε πιθανώς. Προτιμώ απλώς να χρησιμοποιήσω την πιο πρόσφατη και μεγαλύτερη έκδοση όλων.

Για να γράψετε και να εκτελέσετε δοκιμές, προτείνω να χρησιμοποιήσετε ένα IDE που διαθέτει ενσωματωμένο πρόγραμμα δοκιμής JUnit. Χρησιμοποιώ το Eclipse 3.0M7 για να αναπτύξω τα δοκιμαστικά μου σενάρια, αλλά το IntelliJ έχει επίσης υποστήριξη JUnit, όπως και τα IDE που κυκλοφόρησαν πρόσφατα.

HttpUnit: Ο προσομοιωτής πελάτη HTTP

Καθώς θέλουμε να δοκιμάσουμε εφαρμογές Web, ιδανικά, το εργαλείο δοκιμής πρέπει να συμπεριφέρεται ακριβώς όπως τα προγράμματα περιήγησης Ιστού των χρηστών. Η εφαρμογή μας (ο στόχος δοκιμής) δεν πρέπει να γνωρίζει καμία διαφορά κατά την προβολή σελίδων σε πρόγραμμα περιήγησης στο Web ή στο εργαλείο δοκιμής. Αυτό ακριβώς παρέχει το HttpUnit: προσομοιώνει τα αιτήματα GET και POST ενός κανονικού προγράμματος περιήγησης, και παρέχει ένα ωραίο μοντέλο αντικειμένου με το οποίο θα κωδικοποιήσουμε τις δοκιμές μας.

Δείτε τον αναλυτικό οδηγό API για τις υπόλοιπες τάξεις και μεθόδους. Το σχήμα 1 δίνει μια σύντομη επισκόπηση των τάξεων που χρησιμοποιώ πιο συχνά. Μια συνεδρία χρήστη (μια ακολουθία αλληλεπιδράσεων με την εφαρμογή Ιστού) ενσωματώνεται με ένα Διαδικτυακή συνομιλία. Κατασκευάζουμε WebRequests, συνήθως διαμορφώνοντας τη διεύθυνση URL και τις παραμέτρους, και στη συνέχεια το στέλνουμε μέσω του Διαδικτυακή συνομιλία. Στη συνέχεια, το πλαίσιο επιστρέφει a WebResponse, που περιέχει την επιστρεφόμενη σελίδα και τα χαρακτηριστικά από το διακομιστή.

Ακολουθεί ένα δείγμα θήκης HttpUnit από τα έγγραφα HttpUnit:

 / ** * Επαληθεύει ότι η υποβολή της φόρμας σύνδεσης με το όνομα "κύρια" αποτελέσματα * σε μια σελίδα που περιέχει το κείμενο "Κορυφαίο μυστικό" ** / public void testGoodLogin () ρίχνει την Εξαίρεση {WebConversation chat = new WebConversation (); WebRequest request = νέο GetMethodWebRequest ("//www.meterware.com/servlet/TopSecret"); WebResponse απόκριση = chat.getResponse (αίτημα); WebForm loginForm = response.getForms () [0]; request = loginForm.getRequest (); request.setParameter ("όνομα", "κύριο"); απόκριση = συνομιλία.getResponse (αίτημα); assertTrue ("Η είσοδος δεν έγινε αποδεκτή", response.getText (). indexOf ("Το καταφέρατε!")! = -1); assertEquals ("Τίτλος σελίδας", "Κορυφαίο μυστικό", response.getTitle ()); } 

Αρχιτεκτονικά θέματα

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

Κατά τη διάρκεια της κωδικοποίησης, θα συνειδητοποιήσετε ότι πολλές ενότητες κώδικα εμφανίζονται σε περισσότερες από μία υλοποιήσεις δοκιμαστικών περιπτώσεων (πιθανώς σε όλες τις δοκιμαστικές περιπτώσεις). Εάν είστε έμπειρος προγραμματιστής αντικειμενοστραφής, θα μπείτε στον πειρασμό να δημιουργήσετε ιεραρχίες τάξης και κοινές τάξεις. Σε ορισμένες περιπτώσεις, αυτό έχει πολύ νόημα - για παράδειγμα, η διαδικασία σύνδεσης πρέπει να είναι μια κοινή μέθοδος διαθέσιμη για όλες τις δοκιμαστικές περιπτώσεις. Ωστόσο, πρέπει να κάνετε ένα βήμα πίσω και να συνειδητοποιήσετε ότι δεν δημιουργείτε ένα νέο σύστημα παραγωγής πάνω από την εφαρμογή στόχου-δοκιμής - αυτές οι τάξεις Java δεν είναι παρά δοκιμαστικά σενάρια για την επικύρωση της εξόδου του ιστότοπου. Ασκήστε κοινή λογική και στοχεύστε σε απλά, διαδοχικά και αυτοτελή δοκιμαστικά σενάρια.

Οι δοκιμαστικές περιπτώσεις είναι συνήθως εύθραυστες. Εάν ένας προγραμματιστής αλλάξει μια διεύθυνση URL, αναδιοργανώνει τη διάταξη

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

Η ανιχνευσιμότητα είναι ζωτικής σημασίας για τις δοκιμαστικές μας περιπτώσεις. Εάν κάτι πάει KA-BOOM ή, για παράδειγμα, ένα αποτέλεσμα υπολογισμού είναι λάθος, είναι σημαντικό να επισημάνετε τον προγραμματιστή στην αντίστοιχη προδιαγραφή περίπτωσης δοκιμής και στην προδιαγραφή περίπτωσης χρήσης για γρήγορη επίλυση σφαλμάτων. Επομένως, σημειώστε την εφαρμογή σας με αναφορές στα πρωτότυπα έγγραφα προδιαγραφών. Η συμπερίληψη του αριθμού έκδοσης αυτών των εγγράφων είναι επίσης χρήσιμη. Αυτό θα μπορούσε να είναι ένα απλό σχόλιο κώδικα ή ένας πολύπλοκος μηχανισμός όπου οι αναφορές δοκιμών συνδέονται με τα έγγραφα. το σημαντικό είναι να υπάρχει η αναφορά στον κώδικα και στο κρατήστε την ιχνηλασιμότητα.

Πότε μπορώ να γράψω κώδικα;

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

Για την ανάπτυξη των εφαρμογών δοκιμαστικής θήκης, προτιμώ να δουλεύω στο Eclipse. Πρώτα απ 'όλα, έχει έναν ωραίο δρομέα δοκιμών JUnit. Μπορείτε να επιλέξετε μια κλάση Java και από το μενού Εκτέλεση μπορείτε να την εκτελέσετε ως δοκιμή μονάδας JUnit. Ο δρομέας εμφανίζει τη λίστα των αναγνωρισμένων μεθόδων δοκιμής και το αποτέλεσμα εκτέλεσης. Όταν όλα πάνε εντάξει κατά τη διάρκεια της δοκιμής, δίνει μια ωραία πράσινη γραμμή. Εάν εμφανιστεί μια εξαίρεση ή μια αποτυχία ισχυρισμού, εμφανίζεται μια ενοχλητική κόκκινη γραμμή. Νομίζω ότι τα οπτικά σχόλια είναι πραγματικά σημαντικά - προσφέρει μια αίσθηση ολοκλήρωσης, ειδικά όταν γράφετε τεστ μονάδας για τον δικό σας κωδικό. Μου αρέσει επίσης να χρησιμοποιώ το Eclipse για τις δυνατότητες αναδιαμόρφωσης. Αν συνειδητοποιήσω ότι σε μια τάξη δοκιμαστικής υπόθεσης πρέπει να αντιγράψω και να επικολλήσω ενότητες κώδικα, μπορώ απλώς να χρησιμοποιήσω το μενού Refactoring για να δημιουργήσω μια μέθοδο από την ενότητα κώδικα. Αν συνειδητοποιήσω ότι πολλές δοκιμαστικές περιπτώσεις θα χρησιμοποιούν την ίδια μέθοδο, μπορώ να χρησιμοποιήσω το μενού για να ανεβάσω τη μέθοδο μου στην βασική μου τάξη.

Με βάση τις παραπάνω αρχιτεκτονικές απαιτήσεις, για κάθε έργο, συνήθως δημιουργώ μια βασική κατηγορία δοκιμαστικής περίπτωσης, η οποία επεκτείνει το JUnit TestCase τάξη. Το λέω ConfigurableTestCase. Κάθε υλοποίηση δοκιμαστικής επέκτασης επεκτείνει αυτήν την τάξη, βλ. Εικόνα 2.

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

Οι συγκεκριμένες εφαρμογές δοκιμαστικών περιπτώσεων περιέχουν μία μέθοδο δοκιμής ανά σενάριο δοκιμαστικής περίπτωσης (από το έγγραφο προδιαγραφής δοκιμαστικής περίπτωσης). Κάθε μέθοδος συνδέεται συνήθως με έναν συγκεκριμένο ρόλο και στη συνέχεια εκτελεί την αλληλεπίδραση με την εφαρμογή Web. Οι περισσότερες περιπτώσεις δοκιμής δεν χρειάζονται συγκεκριμένο χρήστη για την ολοκλήρωση των δραστηριοτήτων. Συνήθως απαιτούν χρήστη σε συγκεκριμένο ρόλο, όπως Διαχειριστής ή Επισκέπτης ή Εγγεγραμμένος Χρήστης. Δημιουργώ πάντα ένα Μέθοδος σύνδεσης enum, το οποίο περιέχει τους διαθέσιμους ρόλους. Χρησιμοποιώ το πακέτο Jakarta Commons ValuedEnum για να δημιουργήσω αριθμούς για τους ρόλους. Όταν μια συγκεκριμένη μέθοδος δοκιμής σε μια εφαρμογή υπόθεσης συνδέεται, πρέπει να καθορίσει ποιος ρόλος σύνδεσης απαιτείται για το συγκεκριμένο σενάριο δοκιμής. Φυσικά, η δυνατότητα σύνδεσης με έναν συγκεκριμένο χρήστη θα πρέπει επίσης να είναι δυνατή, για παράδειγμα, για την επαλήθευση της υπόθεσης χρήσης του εγγεγραμμένου χρήστη.

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

Οι ισχυρισμοί JUnit προσφέρουν μια κακή προσέγγιση για να ελέγξετε εάν η εμφάνιση και η αίσθηση, η διάταξη και ο σχεδιασμός της σελίδας συμμορφώνονται με τις απαιτήσεις. Είναι πιθανό, δεδομένου ενός άπειρου χρόνου για την ανάπτυξη του τεστ, αλλά ένας καλός άνθρωπος δοκιμαστής μπορεί να αξιολογήσει αυτά τα πράγματα πιο αποτελεσματικά. Εστιάστε λοιπόν στην επαλήθευση της λειτουργικότητας της εφαρμογής Web, αντί να ελέγχετε ό, τι είναι δυνατό στη σελίδα.

Ακολουθεί ένα ενημερωμένο σενάριο δοκιμής που βασίζεται στην αρχιτεκτονική δοκιμαστικών περιπτώσεων. Η τάξη επεκτείνεται ConfigurableTestCaseκαι τα στοιχεία σύνδεσης αντιμετωπίζονται στην βασική κλάση:

 / ** * Επαληθεύει ότι η υποβολή της φόρμας σύνδεσης με το όνομα "κύρια" αποτελέσματα * σε μια σελίδα που περιέχει το κείμενο "Κορυφαίο μυστικό" ** / public void testGoodLogin () ρίχνει την Εξαίρεση {WebConversation chat = new WebConversation (); Απόκριση WebResponse = σύνδεση (συνομιλία, LoginMode.ADMIN_MODE); assertTrue ("Η είσοδος δεν έγινε αποδεκτή", response.getText (). indexOf ("Το καταφέρατε!")! = -1); assertEquals ("Τίτλος σελίδας", "Κορυφαίο μυστικό", response.getTitle ()); } 

Συμβουλές και κόλπα

Τα περισσότερα σενάρια μπορούν να αντιμετωπιστούν πολύ εύκολα ρυθμίζοντας WebForm παραμέτρους και στη συνέχεια αναζητώντας συγκεκριμένα στοιχεία με αποτελέσματα στο WebResponse σελίδες, αλλά υπάρχουν πάντα ορισμένες προκλητικές δοκιμαστικές περιπτώσεις.