Πώς να στεγνώνετε τις δοκιμές RSpec χρησιμοποιώντας κοινόχρηστα παραδείγματα

" Δώσε μου έξι ώρες για να κόψω ένα δέντρο και θα περάσω τα πρώτα τέσσερα ακονίζοντας το τσεκούρι." - Αβραάμ Λίνκολν

Όταν αναπαράγω ένα έργο πριν από λίγες εβδομάδες, πέρασα τον περισσότερο χρόνο μου γράφοντας προδιαγραφές. Αφού έγραψα πολλές παρόμοιες δοκιμαστικές περιπτώσεις για ορισμένα API, άρχισα να αναρωτιέμαι αν θα μπορούσα να απαλλαγώ από πολλά από αυτά τα διπλότυπα.

Γι 'αυτό έβαλα τον εαυτό μου στην ανάγνωση των βέλτιστων πρακτικών για το DRYing up test (Μην επαναλάβετε τον εαυτό σας). Και έτσι γνώρισα shared examplesκαι shared contexts.

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

Όταν έχετε πολλές προδιαγραφές που περιγράφουν παρόμοια συμπεριφορά, ίσως είναι καλύτερο να εξαγάγετε περιττά παραδείγματα shared examplesκαι να τα χρησιμοποιήσετε σε πολλές προδιαγραφές.

Ας υποθέσουμε ότι έχετε δύο μοντέλα Χρήστη και Δημοσίευση και ένας χρήστης μπορεί να έχει πολλές αναρτήσεις. Οι χρήστες θα πρέπει να μπορούν να βλέπουν τη λίστα χρηστών και δημοσιεύσεων. Η δημιουργία μιας ενέργειας ευρετηρίου στους χρήστες και στους ελεγκτές αναρτήσεων θα εξυπηρετήσει αυτόν τον σκοπό.

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

# users_controller_spec.rbdescribe "GET #index" do before do 5.times do FactoryGirl.create(:user) end get :index end it { expect(subject).to respond_with(:ok) } it { expect(subject).to render_template(:index) } it { expect(assigns(:users)).to match(User.all) }end
# users_controller.rbclass UsersController < ApplicationController .... def index @users = User.all end ....end

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

Τέλος, όλα αυτά τα δεδομένα παρουσιάζονται σε προβολές μέσω HTML, JSON ή XML χρησιμοποιώντας API. Για να απλοποιήσω το παράδειγμά μου, οι ενέργειες ευρετηρίου των ελεγκτών θα πάρουν απλώς δεδομένα και, στη συνέχεια, θα τους δείξουν μέσω προβολών.

Το ίδιο ισχύει και για την ενέργεια ευρετηρίου στον ελεγκτή δημοσιεύσεων:

describe "GET #index" do before do 5.times do FactoryGirl.create(:post) end get :index end it { expect(subject).to respond_with(:ok) } it { expect(subject).to render_template(:index) } it { expect(assigns(:posts)).to match(Post.all) }end
# posts_controller.rbclass PostsController < ApplicationController .... def index @posts = Post.all end ....end

Οι δοκιμές RSpec που έχουν γραφτεί τόσο για τους χρήστες όσο και για τον ελεγκτή δημοσιεύσεων είναι πολύ παρόμοιες. Και στους δύο ελεγκτές έχουμε:

  • Ο κωδικός απόκρισης - πρέπει να είναι "ΟΚ"
  • Και οι δύο ενέργειες ευρετηρίου πρέπει να αποδίδουν σωστή μερική ή προβολή - στην περίπτωσή μας index
  • Τα δεδομένα που θέλουμε να αποδώσουμε, όπως δημοσιεύσεις ή χρήστες

Ας στεγνώσουμε τις προδιαγραφές για τη δράση ευρετηρίου χρησιμοποιώντας shared examples.

Πού να βάλετε τα κοινά σας παραδείγματα

Μου αρέσει να τοποθετώ κοινόχρηστα παραδείγματα στον κατάλογο προδιαγραφών / support / shared_examples έτσι ώστε όλα τα shared exampleσχετικά αρχεία να φορτώνονται αυτόματα.

Μπορείτε να διαβάσετε σχετικά με άλλες συνήθεις συμβάσεις για τον εντοπισμό σας shared examplesεδώ: τεκμηρίωση κοινών παραδειγμάτων

Πώς να ορίσετε ένα κοινό παράδειγμα

Η ενέργεια του ευρετηρίου σας πρέπει να ανταποκρίνεται με 200 κωδικό επιτυχίας (ΟΚ) και να αποδίδει το πρότυπο ευρετηρίου σας.

RSpec.shared_examples "index examples" do it { expect(subject).to respond_with(:ok) } it { expect(subject).to render_template(:index) }end

Εκτός από τα itμπλοκ σας - και πριν και μετά τα άγκιστρα σας - μπορείτε να προσθέσετε letμπλοκ, περιβάλλον και να περιγράψετε μπλοκ, τα οποία μπορούν επίσης να οριστούν μέσα shared examples.

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

Πώς να χρησιμοποιήσετε κοινά παραδείγματα

Η προσθήκη include_examples "index examples"στις προδιαγραφές του χειριστή των χρηστών και των αναρτήσεων σας περιλαμβάνει "παραδείγματα ευρετηρίου" στις δοκιμές σας.

# users_controller_spec.rbdescribe "GET #index" do before do 5.times do FactoryGirl.create(:user) end get :index end include_examples "index examples" it { expect(assigns(:users)).to match(User.all) }end
# similarly, in posts_controller_spec.rbdescribe "GET #index" do before do 5.times do FactoryGirl.create(:post) end get :index end include_examples "index examples" it { expect(assigns(:posts)).to match(Post.all) }end

Μπορείτε επίσης να χρησιμοποιήσετε it_behaves_likeή it_should_behaves_likeαντί για include_examplesαυτήν την περίπτωση. it_behaves_likeκαι it_should_behaves_likeστην πραγματικότητα είναι ψευδώνυμα και λειτουργούν με τον ίδιο τρόπο, ώστε να μπορούν να χρησιμοποιηθούν εναλλακτικά. Αλλά include_examplesκαι it_behaves_likeείναι διαφορετικά.

Όπως αναφέρεται στην επίσημη τεκμηρίωση:

  • include_examples - περιλαμβάνει παραδείγματα στο τρέχον πλαίσιο
  • it_behaves_likeκαι it_should_behave_likeσυμπεριλάβετε τα παραδείγματα σε ένθετο περιβάλλον

Γιατί έχει σημασία αυτή η διάκριση;

Η τεκμηρίωση του RSpec δίνει μια σωστή απάντηση:

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

Έτσι, όταν αντιμετωπίζετε κατάσταση όπου τα παραμετροποιημένα παραδείγματα περιέχουν μεθόδους που έρχονται σε διένεξη με άλλες μεθόδους στο ίδιο πλαίσιο, μπορείτε να αντικαταστήσετε include_examplesμε τη it_behaves_likeμέθοδο. Αυτό θα δημιουργήσει ένα ένθετο περιβάλλον και θα αποφύγει τέτοιου είδους καταστάσεις.

Ρίξτε μια ματιά στην ακόλουθη γραμμή στις προδιαγραφές του ελεγκτή χρηστών και στις προδιαγραφές του ελεγκτή δημοσιεύσεων:

it { expect(assigns(:users)).to match(User.all) }it { expect(assigns(:posts)).to match(Post.all) }

Τώρα οι προδιαγραφές του ελεγκτή σας μπορούν να επαναπροσδιοριστούν περαιτέρω μεταβιβάζοντας παραμέτρους στο κοινόχρηστο παράδειγμα όπως παρακάτω:

# specs/support/shared_examples/index_examples.rb
# here assigned_resource and resource class are parameters passed to index examples block RSpec.shared_examples "index examples" do |assigned_resource, resource_class| it { expect(subject).to respond_with(:ok) } it { expect(subject).to render_template(:index) } it { expect(assigns(assigned_resource)).to match(resource_class.all) }end

Τώρα, κάντε τις ακόλουθες αλλαγές στις προδιαγραφές του χειριστή σας και των αναρτήσεων:

# users_controller_spec.rbdescribe "GET #index" do before do ... end include_examples "index examples", :users, User.allend
# posts_controller_spec.rbdescribe "GET #index" do before do ... end include_examples "index examples", :posts, Post.allend

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

συμπέρασμα

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

Κυρίως, όταν δουλεύω με API, χρησιμοποιώ shared examplesγια να μου παρέχει μια κοινή δομή για το σχεδιασμό παρόμοιων API.

Μη διστάσετε να μοιραστείτε πώς στεγνώνετε τις προδιαγραφές σας χρησιμοποιώντας shared examples.