{"title":"Γράφοντας το πρώτο σας Django app, μέρος 5","version":"2.1","locale":"el","docname":"intro/tutorial05","url":"/el/2.1/intro/tutorial05/","canonical":"https://djangodocs.dev/el/2.1/intro/tutorial05/","summary":"Ο οδηγός αυτός ξεκινά εκεί που τελειώνει ο οδηγός 4 . Έχουμε φτιάξει μια εφαρμογή ψηφοφορίας (Web-poll application) και τώρα θα δημιουργήσουμε μερικά…","html":"<h1>Γράφοντας το πρώτο σας Django app, μέρος 5<a class=\"heading-anchor\" href=\"#writing-your-first-django-app-part-5\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>Ο οδηγός αυτός ξεκινά εκεί που τελειώνει <a class=\"reference internal\" href=\"/el/2.1/intro/tutorial04/\"><span class=\"doc\">ο οδηγός 4</span></a>. Έχουμε φτιάξει μια εφαρμογή ψηφοφορίας (Web-poll application) και τώρα θα δημιουργήσουμε μερικά αυτοματοποιημένα τεστ για αυτήν.</p>\n<section id=\"introducing-automated-testing\">\n<h2>Εισαγωγή στα αυτοματοποιημένα τεστ<a class=\"heading-anchor\" href=\"#introducing-automated-testing\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<section id=\"what-are-automated-tests\">\n<h3>Τι είναι τα αυτοματοποιημένα τεστ;<a class=\"heading-anchor\" href=\"#what-are-automated-tests\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Τα τεστ (tests) δεν είναι τίποτε άλλο παρά απλές ρουτίνες οι οποίες ελέγχουν τη λειτουργία του κώδικα σας.</p>\n<p>Το τεστ δραστηριοποιείται σε διαφορετικά επίπεδα. Μερικά τεστ μπορεί να εφαρμοστούν σε μια μικρή λεπτομέρεια (<em>επιστρέφει μια συγκεκριμένη μέθοδος ενός μοντέλου τις αναμενόμενες τιμές;</em>) ενώ άλλα εξετάζουν την συνολική εικόνα λειτουργίας του λογισμικού (<em>παράγει το επιθυμητό αποτέλεσμα η σειρά των inputs ενός χρήστη στο site;</em>). Αυτό δεν είναι κάτι διαφορετικό από το τεστ που κάνατε νωρίτερα στον <a class=\"reference internal\" href=\"/el/2.1/intro/tutorial02/\"><span class=\"doc\">Οδηγό 2</span></a>, χρησιμοποιώντας την εντολή <a class=\"reference internal\" href=\"/el/2.1/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a> για να εξετάσετε τη συμπεριφορά των μεθόδων ή να τρέξετε την εφαρμογή και κατά την αλληλεπίδραση μαζί της, να δείτε πως συμπεριφέρεται.</p>\n<p>Αυτό που διαφέρει στα <em>αυτοματοποιημένα</em> τεστ είναι ότι η δουλειά των τεστ γίνεται για σας από το σύστημα. Δημιουργείτε μια φορά ένα σετ από τεστ και μετά καθώς η εφαρμογή σας αλλάζει, μπορείτε να ελέγξετε ότι ο κώδικας σας δουλεύει όπως αρχικά είχατε σχεδιάσει χωρίς να χρονοτριβείτε με χειροκίνητα τεστ.</p>\n</section>\n<section id=\"why-you-need-to-create-tests\">\n<h3>Γιατί χρειάζεται να δημιουργείτε τεστ<a class=\"heading-anchor\" href=\"#why-you-need-to-create-tests\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Γιατί, λοιπόν, δημιουργούμε τεστ και γιατί τώρα;</p>\n<p>Ίσως να αισθάνεστε ότι έχετε αρκετά στο κεφάλι σας ήδη (μαθαίνετε Python, μαθαίνετε Django) για να σας προστεθεί άλλη μια γνώση η οποία, σκέφτεστε, ίσως σας φανεί περιττή ή ακόμα και άχρηστη. Παρ’ όλ’ αυτά η εφαρμογή μας λειτουργεί περίφημα. Γιατί να μπούμε στον κόπο να φτιάξουμε αυτοματοποιημένα τεστ; Δεν θα κάνει την εφαρμογή μας καλύτερη, έτσι δεν είναι; Αν είναι να φτιάξετε μόνο αυτή την εφαρμογή με το Django και μετά δεν ασχοληθείτε άλλο, τότε ναι, δεν χρειάζεται να γνωρίζετε πως να φτιάξετε αυτοματοποιημένα τεστ. Αλλά αν δεν είναι έτσι, τώρα είναι μια καλή στιγμή να μάθετε πως.</p>\n<section id=\"tests-will-save-you-time\">\n<h4>Τα τεστ θα σας γλιτώσουν πολύτιμο χρόνο<a class=\"heading-anchor\" href=\"#tests-will-save-you-time\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>Μέχρι ένα σημείο η λογική του “τεστάρω για να δω αν λειτουργεί” θα δουλεύει μια χαρά. Σε πιο εκλεπτυσμένες-περίπλοκες εφαρμογές μπορεί να έχετε δεκάδες, αν όχι εκατοντάδες, αλληλεπιδράσεις μεταξύ των οντοτήτων σας (models, views κλπ) και η παραπάνω λογική να μην μπορεί πλέον να σας φανεί χρήσιμη.</p>\n<p>Μια αλλαγή σε μια από αυτές τις οντότητες μπορεί να έχει μη αναμενόμενα αποτελέσματα στη συμπεριφορά της εφαρμογής σας. Αν εφαρμόσετε την παραπάνω λογική του “φαίνεται να δουλεύει” ίσως χρειαστεί να ελέγξετε τον (και να ανατρέξετε στον) κώδικα σας με πάνω από είκοσι διαφορετικές εκδόσεις των τεστ δεδομένων σας, μόνο και μόνο για να σιγουρευτείτε ότι αυτή και μόνο η αλλαγή δεν επηρέασε αρνητικά κάποιο άλλο κομμάτι του κώδικα σας (κάποια άλλη οντότητα). Όλα αυτά χειροκίνητα - όχι και πολύ καλή αξιοποίηση του χρόνου σας.</p>\n<p>Αυτό είναι αλήθεια όταν τα αυτοματοποιημένα τεστ μπορούν να κάνουν την παραπάνω δουλειά για εσάς, σε δευτερόλεπτα. Αν κάτι πάει στραβά, τα τεστ θα σας βοηθήσουν στην αναγνώριση του κώδικα που δεν είχε την αναμενόμενη συμπεριφορά (δεν έτρεξε, δηλαδή, όπως του είχατε υπαγορεύσει).</p>\n<p>Καμιά φορά ίσως σας φανεί ως αγγαρεία να ξεφύγετε από το παραγωγικό και δημιουργικό κομμάτι της δουλειάς σας μόνο και μόνο για να μπείτε στη διαδικασία γραφής των ανιαρών τεστ, ειδικά όταν γνωρίζετε ότι η εφαρμογή σας λειτουργεί ορθά.</p>\n<p>Από την άλλη, το κομμάτι της συγγραφής των τεστ θα προσθέσει μεγαλύτερη αξιοπιστία στον κώδικα σας από το να τεστάρετε τον κώδικα χειροκίνητα ή να προσπαθείτε να αναγνωρίσετε τη πηγή ενός νεοεισερχόμενου προβλήματος.</p>\n</section>\n<section id=\"tests-don-t-just-identify-problems-they-prevent-them\">\n<h4>Τα τεστ όχι μόνο αναγνωρίζουν τα προβλήματα αλλά τα εμποδίζουν επίσης<a class=\"heading-anchor\" href=\"#tests-don-t-just-identify-problems-they-prevent-them\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>Είναι λάθος να νομίζετε ότι τα τεστ συμβάλλουν αρνητικά στην ανάπτυξη των εφαρμογών.</p>\n<p>Φανταστείτε τους προγραμματιστές να μην είχαν στα εργαλεία τους τα τεστ. Χωρίς αυτά, ο στόχος ή η αναμενόμενη συμπεριφορά μιας εφαρμογής θα ήταν θολή-σκοτεινή. Ακόμα και αν είναι δικός σας κώδικας, θα χρειαστεί, μερικές φορές, να ψάξετε μέσα στον κώδικα για να διαπιστώσετε τι κάνει.</p>\n<p>Τα τεστ τα αλλάζουν όλα αυτά. Φωτίζουν τον κώδικα σας από μέσα και όταν κάτι πάει στραβά, ξέρετε ακριβώς ποιο ήταν αυτό το κομμάτι, <em>ακόμη και αν δεν είχατε συνειδητοποιήσει ότι αυτό το κομμάτι πήγε στραβά</em>.</p>\n</section>\n<section id=\"tests-make-your-code-more-attractive\">\n<h4>Τα τεστ κάνουν τον κώδικα σας πιο προσιτό<a class=\"heading-anchor\" href=\"#tests-make-your-code-more-attractive\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>Μπορεί να έχετε δημιουργήσει ένα λογισμικό διαμάντι αλλά άλλοι προγραμματιστές αρνούνται να ρίξουν μια ματιά σε αυτό μόνο και μόνο επειδή στερείται τεστ. Χωρίς τεστ, δεν θα τον εμπιστευτούν. Ο Jacob Kaplan-Moss, ένας από τους αρχικούς προγραμματιστές του Django, λέει «Code without tests is broken by design.»</p>\n<p>Προκειμένου άλλοι προγραμματιστές (developers) να δουν σοβαρά το δικό σας software, θα πρέπει να έχετε υλοποιήσει (γράψει) τα απαραίτητα τεστ.</p>\n</section>\n<section id=\"tests-help-teams-work-together\">\n<h4>Τα τεστ βοηθούν τις ομάδες να συνεργαστούν<a class=\"heading-anchor\" href=\"#tests-help-teams-work-together\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>Οι προηγούμενες παράγραφοι γράφτηκαν από την σκοπιά ενός και μόνου developer ο οποίος διατηρεί ένα application. Οι περίπλοκες εφαρμογγές, όμως, θα διατηρούνται από ομάδες. Τα τεστ, εγγυώνται ότι οι συμμετέχοντες στην ομάδα δεν θα χαλάσουν (break) τον κώδικα σας ακούσια αλλά ούτε και εσείς θα χαλάσετε τον δικό του, ακούσια πάλι. Αν θέλετε να βγάλετε τα προς το ζην όντας ένας Django programmer, θα πρέπει να είστε καλός στη συγγραφή των τεστ!</p>\n</section>\n</section>\n</section>\n<section id=\"basic-testing-strategies\">\n<h2>Βασικές στρατηγικές τεστ<a class=\"heading-anchor\" href=\"#basic-testing-strategies\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Υπάρχουν πολλοί τρόποι για να προσεγγίσει κανείς τη συγγραφή των τεστ.</p>\n<p>Κάποιοι προγραμματιστές ακολουθούν μια διαδικασία με το όνομα «<a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Test-driven_development\">test-driven development</a>». Ξεκινούν να γράφουν τα τεστ προτού γράψουν τον κώδικα τους. Αυτό μπορεί να ακούγεται αντί-διαισθητικό αλλά στην πραγματικότητα οι περισσότεροι ακολουθούν αυτή τη μέθοδο: αρχικά περιγράφουν το πρόβλημα και κατόπιν γράφουν κώδικα για να το λύσουν. Η λογική της διαδικασίας test-driven development μπορεί να μεταφερθεί πανεύκολα στα test cases της Python.</p>\n<p>Πιο συχνά, όσοι είναι νέοι στο χώρο των τεστ θα γράψουν πρώτα τον κώδικα και μετά θα αποφασίσουν ότι θα έπρεπε να είχαν γράψει μερικά τεστ. Ίσως θα ήταν καλύτερα να είχαν γραφτεί τα τεστ νωρίτερα, αλλά ποτέ δεν είναι αργά για να ξεκινήσει κανείς.</p>\n<p>Μερικές φορές δεν ξέρεις πως να ξεκινήσεις να γράφεις τεστ. Αν έχετε γράψει μερικές χιλιάδες γραμμές Python κώδικα, είναι δύσκολο μετά να επιλέξεις κάτι για να γράψεις τεστ πάνω σε αυτό. Σε τέτοιες περιπτώσεις είναι πιο παραγωγικό να γράψετε το πρώτο σας τεστ κάθε φορά που κάνετε μια αλλαγή, είτε προσθέτετε κάποιο καινούργιο feature είτε διορθώνετε ένα bug.</p>\n<p>Ας ξεκινήσουμε λοιπόν, αμέσως τώρα.</p>\n</section>\n<section id=\"writing-our-first-test\">\n<h2>Γράφοντας το πρώτο σας τεστ<a class=\"heading-anchor\" href=\"#writing-our-first-test\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<section id=\"we-identify-a-bug\">\n<h3>Βρίσκουμε ένα bug<a class=\"heading-anchor\" href=\"#we-identify-a-bug\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Ευτυχώς, υπάρχει ένα μικρό bug στην εφαρμογή μας (<code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code>) και αξίζει να το διορθώσουμε ευθύς αμέσως: η μέθοδος <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> επιστρέφει <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> αν η ερώτηση (<code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>) εκδόθηκε εντός εικοσιτεσσάρων ωρών (το οποίο είναι σωστό). Η ίδια μέθοδος επιστρέφει πάλι <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> αν το <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> τοποθετείται στο μέλλον (κάτι το οποίο δεν μπορεί να ισχύει).</p>\n<p>Confirm the bug by using the <a class=\"reference internal\" href=\"/el/2.1/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a> to check the method on a question\nwhose date lies in the future:</p>\n<div class=\"console\" data-console><div class=\"console-panel\" data-platform=\"unix\"><p class=\"console-label\" id=\"console-0-unix-label\">Linux / macOS</p><div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>python<span class=\"w\"> </span>manage.py<span class=\"w\"> </span>shell\n</code></pre></div>\n</div><div class=\"console-panel\" data-platform=\"windows\"><p class=\"console-label\" id=\"console-0-windows-label\">Windows</p><div class=\"code-block\" data-language=\"doscon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Windows</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Windows shell\"><code><span class=\"gp\">...\\&gt;</span> py manage.py shell\n</code></pre></div></div></div>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python console code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">import</span><span class=\"w\"> </span><span class=\"nn\">datetime</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.utils</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">timezone</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">polls.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># create a Question instance with pub_date 30 days in the future</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">future_question</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"p\">(</span><span class=\"n\">pub_date</span><span class=\"o\">=</span><span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span> <span class=\"o\">+</span> <span class=\"n\">datetime</span><span class=\"o\">.</span><span class=\"n\">timedelta</span><span class=\"p\">(</span><span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">30</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># was it published recently?</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">future_question</span><span class=\"o\">.</span><span class=\"n\">was_published_recently</span><span class=\"p\">()</span>\n<span class=\"go\">True</span>\n</code></pre></div>\n<p>Εφόσον τα πράγματα στο μέλλον δε θεωρούνται “πρόσφατα”, τότε αυτό είναι ξεκάθαρα λάθος.</p>\n</section>\n<section id=\"create-a-test-to-expose-the-bug\">\n<h3>Δημιουργία ενός τεστ για την ανάδειξη του bug<a class=\"heading-anchor\" href=\"#create-a-test-to-expose-the-bug\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Ότι ακριβώς κάναμε στο <a class=\"reference internal\" href=\"/el/2.1/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a> για να τεστάρουμε το πρόβλημα, θα το κάνουμε στο αυτοματοποιημένο τεστ που θα γράψουμε αμέσως τώρα.</p>\n<p>Ένα βολικό μέρος για να κρατάτε όλα τα τεστ για την εφαρμογή σας είναι σε ένα ξεχωριστό αρχείο με το όνομα <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code>. Το σύστημα (testing system) θα βρει αυτόματα όλα τα τεστ, αρκεί καθένα από αυτά να ξεκινούν με τη λέξη <code class=\"docutils literal notranslate\"><span class=\"pre\">test</span></code>.</p>\n<p>Γράψτε τον ακόλουθο κώδικα στο αρχείο <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> μέσα στην εφαρμογή <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code>:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/tests.py</figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">import</span><span class=\"w\"> </span><span class=\"nn\">datetime</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.test</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">TestCase</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.utils</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">timezone</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Question</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">QuestionModelTests</span><span class=\"p\">(</span><span class=\"n\">TestCase</span><span class=\"p\">):</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_was_published_recently_with_future_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        was_published_recently() returns False for questions whose pub_date</span>\n<span class=\"sd\">        is in the future.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">time</span> <span class=\"o\">=</span> <span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span> <span class=\"o\">+</span> <span class=\"n\">datetime</span><span class=\"o\">.</span><span class=\"n\">timedelta</span><span class=\"p\">(</span><span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">future_question</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"p\">(</span><span class=\"n\">pub_date</span><span class=\"o\">=</span><span class=\"n\">time</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertIs</span><span class=\"p\">(</span><span class=\"n\">future_question</span><span class=\"o\">.</span><span class=\"n\">was_published_recently</span><span class=\"p\">(),</span> <span class=\"kc\">False</span><span class=\"p\">)</span>\n</code></pre></figure>\n<p>Here we have created a <a class=\"reference internal\" href=\"/el/2.1/topics/testing/tools/#django.test.TestCase\" title=\"django.test.TestCase\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">django.test.TestCase</span></code></a> subclass with a method that\ncreates a <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> instance with a <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> in the future. We then check\nthe output of <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code> - which <em>ought</em> to be False.</p>\n</section>\n<section id=\"running-tests\">\n<h3>Τρέχοντας τα τεστ<a class=\"heading-anchor\" href=\"#running-tests\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>In the terminal, we can run our test:</p>\n<div class=\"console\" data-console><div class=\"console-panel\" data-platform=\"unix\"><p class=\"console-label\" id=\"console-1-unix-label\">Linux / macOS</p><div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>python<span class=\"w\"> </span>manage.py<span class=\"w\"> </span><span class=\"nb\">test</span><span class=\"w\"> </span>polls\n</code></pre></div>\n</div><div class=\"console-panel\" data-platform=\"windows\"><p class=\"console-label\" id=\"console-1-windows-label\">Windows</p><div class=\"code-block\" data-language=\"doscon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Windows</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Windows shell\"><code><span class=\"gp\">...\\&gt;</span> py manage.py test polls\n</code></pre></div></div></div>\n<p>και θα δείτε κάτι σαν αυτό:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">Creating</span> <span class=\"n\">test</span> <span class=\"n\">database</span> <span class=\"k\">for</span> <span class=\"n\">alias</span> <span class=\"s1\">&#39;default&#39;</span><span class=\"o\">...</span>\n<span class=\"n\">System</span> <span class=\"n\">check</span> <span class=\"n\">identified</span> <span class=\"n\">no</span> <span class=\"n\">issues</span> <span class=\"p\">(</span><span class=\"mi\">0</span> <span class=\"n\">silenced</span><span class=\"p\">)</span><span class=\"o\">.</span>\n<span class=\"n\">F</span>\n<span class=\"o\">======================================================================</span>\n<span class=\"n\">FAIL</span><span class=\"p\">:</span> <span class=\"n\">test_was_published_recently_with_future_question</span> <span class=\"p\">(</span><span class=\"n\">polls</span><span class=\"o\">.</span><span class=\"n\">tests</span><span class=\"o\">.</span><span class=\"n\">QuestionModelTests</span><span class=\"p\">)</span>\n<span class=\"o\">----------------------------------------------------------------------</span>\n<span class=\"n\">Traceback</span> <span class=\"p\">(</span><span class=\"n\">most</span> <span class=\"n\">recent</span> <span class=\"n\">call</span> <span class=\"n\">last</span><span class=\"p\">):</span>\n  <span class=\"n\">File</span> <span class=\"s2\">&quot;/path/to/mysite/polls/tests.py&quot;</span><span class=\"p\">,</span> <span class=\"n\">line</span> <span class=\"mi\">16</span><span class=\"p\">,</span> <span class=\"ow\">in</span> <span class=\"n\">test_was_published_recently_with_future_question</span>\n    <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertIs</span><span class=\"p\">(</span><span class=\"n\">future_question</span><span class=\"o\">.</span><span class=\"n\">was_published_recently</span><span class=\"p\">(),</span> <span class=\"kc\">False</span><span class=\"p\">)</span>\n<span class=\"ne\">AssertionError</span><span class=\"p\">:</span> <span class=\"kc\">True</span> <span class=\"ow\">is</span> <span class=\"ow\">not</span> <span class=\"kc\">False</span>\n\n<span class=\"o\">----------------------------------------------------------------------</span>\n<span class=\"n\">Ran</span> <span class=\"mi\">1</span> <span class=\"n\">test</span> <span class=\"ow\">in</span> <span class=\"mf\">0.001</span><span class=\"n\">s</span>\n\n<span class=\"n\">FAILED</span> <span class=\"p\">(</span><span class=\"n\">failures</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">)</span>\n<span class=\"n\">Destroying</span> <span class=\"n\">test</span> <span class=\"n\">database</span> <span class=\"k\">for</span> <span class=\"n\">alias</span> <span class=\"s1\">&#39;default&#39;</span><span class=\"o\">...</span>\n</code></pre></div>\n<p>Αυτά που συνέβησαν είναι τα εξής:</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">manage.py</span> <span class=\"pre\">test</span> <span class=\"pre\">polls</span></code> looked for tests in the <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> application</p></li>\n<li><p>βρήκε μια subclass της κλάσης <a class=\"reference internal\" href=\"/el/2.1/topics/testing/tools/#django.test.TestCase\" title=\"django.test.TestCase\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">django.test.TestCase</span></code></a></p></li>\n<li><p>δημιούργησε μια ειδική database για τους σκοπούς του τεστ</p></li>\n<li><p>έψαξε για τεστ μεθόδους - εκείνες των οποίων το όνομα ξεκινά με τη λέξη <code class=\"docutils literal notranslate\"><span class=\"pre\">test</span></code></p></li>\n<li><p>μέσα στην μέθοδο <code class=\"docutils literal notranslate\"><span class=\"pre\">test_was_published_recently_with_future_question</span></code> δημιούργησε ένα <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code> instance του οποίου το <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> field είναι 30 μέρες στο μέλλον</p></li>\n<li><p>… και χρησιμοποιώντας τη μέθοδο <code class=\"docutils literal notranslate\"><span class=\"pre\">assertIs()</span></code>, διαπίστωσε ότι η μέθοδος <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code> επιστρέφει <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code>, αντί για <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code></p></li>\n</ul>\n<p>Το τεστ, μας ενημερώνει ποιο τεστ απέτυχε (τώρα έτυχε να έχουμε μονάχα ένα αλλά στο μέλλον δεν θα έχετε μόνο ένα) και ακόμη και την γραμμή του κώδικα στο τεστ όπου προέκυψε το failure.</p>\n</section>\n<section id=\"fixing-the-bug\">\n<h3>Διορθώνοντας το bug<a class=\"heading-anchor\" href=\"#fixing-the-bug\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Γνωρίζουμε ήδη ποιο και που είναι το πρόβλημα: η μέθοδος <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> θα πρέπει να επιστρέψει <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code> αν το <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> τοποθετηθεί στο μέλλον. Βελτιώστε τη μέθοδο μέσα στο αρχείο <code class=\"docutils literal notranslate\"><span class=\"pre\">models.py</span></code>, ούτως ώστε να επιστρέφει <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> μόνο για παρελθοντικές ημερομηνίες:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/models.py</figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">was_published_recently</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n    <span class=\"n\">now</span> <span class=\"o\">=</span> <span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span>\n    <span class=\"k\">return</span> <span class=\"n\">now</span> <span class=\"o\">-</span> <span class=\"n\">datetime</span><span class=\"o\">.</span><span class=\"n\">timedelta</span><span class=\"p\">(</span><span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">)</span> <span class=\"o\">&lt;=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">pub_date</span> <span class=\"o\">&lt;=</span> <span class=\"n\">now</span>\n</code></pre></figure>\n<p>και τρέξτε τα τεστ ξανά:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">Creating</span> <span class=\"n\">test</span> <span class=\"n\">database</span> <span class=\"k\">for</span> <span class=\"n\">alias</span> <span class=\"s1\">&#39;default&#39;</span><span class=\"o\">...</span>\n<span class=\"n\">System</span> <span class=\"n\">check</span> <span class=\"n\">identified</span> <span class=\"n\">no</span> <span class=\"n\">issues</span> <span class=\"p\">(</span><span class=\"mi\">0</span> <span class=\"n\">silenced</span><span class=\"p\">)</span><span class=\"o\">.</span>\n<span class=\"o\">.</span>\n<span class=\"o\">----------------------------------------------------------------------</span>\n<span class=\"n\">Ran</span> <span class=\"mi\">1</span> <span class=\"n\">test</span> <span class=\"ow\">in</span> <span class=\"mf\">0.001</span><span class=\"n\">s</span>\n\n<span class=\"n\">OK</span>\n<span class=\"n\">Destroying</span> <span class=\"n\">test</span> <span class=\"n\">database</span> <span class=\"k\">for</span> <span class=\"n\">alias</span> <span class=\"s1\">&#39;default&#39;</span><span class=\"o\">...</span>\n</code></pre></div>\n<p>Αφού βρήκαμε ένα bug, γράψαμε ένα τεστ το οποίο το αναδεικνύει και τέλος διορθώσαμε το bug στο κώδικα ούτως ώστε το τεστ να θεωρηθεί επιτυχές.</p>\n<p>Υπάρχει πιθανότητα πολλά άλλα πράγματα να πάνε στραβά με την εφαρμογή μας στο μέλλον αλλά μπορούμε να είμαστε σίγουροι ότι αυτό το συγκεκριμένο bug δεν θα ξαναεμφανιστεί στον κώδικα μας, αφού όταν τρέξουμε την σουίτα των τεστ του Django θα μας ειδοποιήσει αμέσως. Με άλλα λόγια έχουμε κάνει την εφαρμογή μας πιο ασφαλή-σταθερή κατά ένα βαθμό.</p>\n</section>\n<section id=\"more-comprehensive-tests\">\n<h3>Περισσότερα περιεκτικά τεστ<a class=\"heading-anchor\" href=\"#more-comprehensive-tests\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Όσο είμαστε εδώ μπορούμε να βελτιστοποιήσουμε την μέθοδο <code class=\"docutils literal notranslate\"><span class=\"pre\">was_published_recently()</span></code> λιγάκι περισσότερο. Θα ήταν ντροπιαστικό, αν προσπαθώντας να διορθώσουμε ένα bug να έχουμε δημιουργήσει κάπου αλλού ένα άλλο (άθελα μας).</p>\n<p>Προσθέστε δύο ακόμη μεθόδους τεστ στην ίδια κλάση, για να τεστάρουμε την συμπεριφορά της μεθόδου πιο περιεκτικά:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/tests.py</figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_was_published_recently_with_old_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">    </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">    was_published_recently() returns False for questions whose pub_date</span>\n<span class=\"sd\">    is older than 1 day.</span>\n<span class=\"sd\">    &quot;&quot;&quot;</span>\n    <span class=\"n\">time</span> <span class=\"o\">=</span> <span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">datetime</span><span class=\"o\">.</span><span class=\"n\">timedelta</span><span class=\"p\">(</span><span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"n\">seconds</span><span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">)</span>\n    <span class=\"n\">old_question</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"p\">(</span><span class=\"n\">pub_date</span><span class=\"o\">=</span><span class=\"n\">time</span><span class=\"p\">)</span>\n    <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertIs</span><span class=\"p\">(</span><span class=\"n\">old_question</span><span class=\"o\">.</span><span class=\"n\">was_published_recently</span><span class=\"p\">(),</span> <span class=\"kc\">False</span><span class=\"p\">)</span>\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_was_published_recently_with_recent_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">    </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">    was_published_recently() returns True for questions whose pub_date</span>\n<span class=\"sd\">    is within the last day.</span>\n<span class=\"sd\">    &quot;&quot;&quot;</span>\n    <span class=\"n\">time</span> <span class=\"o\">=</span> <span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span> <span class=\"o\">-</span> <span class=\"n\">datetime</span><span class=\"o\">.</span><span class=\"n\">timedelta</span><span class=\"p\">(</span><span class=\"n\">hours</span><span class=\"o\">=</span><span class=\"mi\">23</span><span class=\"p\">,</span> <span class=\"n\">minutes</span><span class=\"o\">=</span><span class=\"mi\">59</span><span class=\"p\">,</span> <span class=\"n\">seconds</span><span class=\"o\">=</span><span class=\"mi\">59</span><span class=\"p\">)</span>\n    <span class=\"n\">recent_question</span> <span class=\"o\">=</span> <span class=\"n\">Question</span><span class=\"p\">(</span><span class=\"n\">pub_date</span><span class=\"o\">=</span><span class=\"n\">time</span><span class=\"p\">)</span>\n    <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertIs</span><span class=\"p\">(</span><span class=\"n\">recent_question</span><span class=\"o\">.</span><span class=\"n\">was_published_recently</span><span class=\"p\">(),</span> <span class=\"kc\">True</span><span class=\"p\">)</span>\n</code></pre></figure>\n<p>Τώρα έχουμε τρία τεστ, τα οποία επιβεβαιώνουν ότι η μέθοδος <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.was_published_recently()</span></code> επιστρέφει λογικές τιμές για παρελθοντικές, παρούσες και μελλοντικές τιμές του <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code>.</p>\n<p>Υπενθυμίζουμε ότι η εφαρμογή μας, <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code> είναι μια απλή εφαρμογή. Ωστόσο, στο μέλλον, όσο περίπλοκη και να γίνει και ο οποιοσδήποτε άλλος κώδικας (ίσως από άλλη δική μας εφαρμογή) αλληλεπιδράσει με την εφαρμογή αυτή, μπορούμε να εγγυηθούμε ότι η μέθοδος στην οποία έχουμε γράψει τα τεστ θα συμπεριφέρεται σε αναμενόμενα πλαίσια.</p>\n</section>\n</section>\n<section id=\"test-a-view\">\n<h2>Τεστ ένα view<a class=\"heading-anchor\" href=\"#test-a-view\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Η εφαρμογή μας, <code class=\"docutils literal notranslate\"><span class=\"pre\">polls</span></code>, δεν κάνει διακρίσεις! Θα κάνει publish οποιαδήποτε ερώτηση, συμπεριλαμβανομένων και εκείνων που το <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> field βρίσκεται στο μέλλον. Θα πρέπει να το διορθώσουμε αυτό. Θέτοντας το <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> στο μέλλον θα σημαίνει ότι η ερώτηση θα πρέπει να γίνει publish εκείνη την ημερομηνία αλλά θα είναι αόρατη μέχρι τότε.</p>\n<section id=\"a-test-for-a-view\">\n<h3>Ένα τεστ για τη view<a class=\"heading-anchor\" href=\"#a-test-for-a-view\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Όταν διορθώσαμε το bug πριν, γράψαμε πρώτα το τεστ και μετά τον κώδικα για να διορθώσουμε το bug. Στην πραγματικότητα αυτό ήταν ένα απλό παράδειγμα του test-driven development, αλλά δεν έχει σημασία η σειρά που ακολουθείται για να γίνει η δουλειά.</p>\n<p>Στο πρώτο μας τεστ επικεντρωθήκαμε στην εσωτερική συμπεριφορά του κώδικα μας (στη μέθοδο του μοντέλου). Για αυτό το τεστ, θέλουμε να ελέγξουμε την συμπεριφορά του όπως θα τη βίωνε ο χρήστης μέσα από τον browser του.</p>\n<p>Προτού προσπαθήσουμε να φτιάξουμε το οτιδήποτε, ας ρίξουμε μια ματιά στα εργαλεία που έχουμε διαθέσιμα.</p>\n</section>\n<section id=\"the-django-test-client\">\n<h3>Τα τεστ του Django από την πλευρά του client<a class=\"heading-anchor\" href=\"#the-django-test-client\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Το Django παρέχει το τεστ <a class=\"reference internal\" href=\"/el/2.1/topics/testing/tools/#django.test.Client\" title=\"django.test.Client\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">Client</span></code></a> για να εξομοιώνει τον χρήστη που αλληλεπιδρά με τον κώδικα σε επίπεδο view. Μπορούμε να το χρησιμοποιήσουμε στο αρχείο <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> ή ακόμη και στο <a class=\"reference internal\" href=\"/el/2.1/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a>.</p>\n<p>We will start again with the <a class=\"reference internal\" href=\"/el/2.1/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a>, where we need to do a couple of\nthings that won’t be necessary in <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code>. The first is to set up the test\nenvironment in the <a class=\"reference internal\" href=\"/el/2.1/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a>:</p>\n<div class=\"console\" data-console><div class=\"console-panel\" data-platform=\"unix\"><p class=\"console-label\" id=\"console-2-unix-label\">Linux / macOS</p><div class=\"code-block\" data-language=\"console\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Shell</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Shell code\"><code><span class=\"gp\">$ </span>python<span class=\"w\"> </span>manage.py<span class=\"w\"> </span>shell\n</code></pre></div>\n</div><div class=\"console-panel\" data-platform=\"windows\"><p class=\"console-label\" id=\"console-2-windows-label\">Windows</p><div class=\"code-block\" data-language=\"doscon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Windows</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Windows shell\"><code><span class=\"gp\">...\\&gt;</span> py manage.py shell\n</code></pre></div></div></div>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python console code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.test.utils</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">setup_test_environment</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">setup_test_environment</span><span class=\"p\">()</span>\n</code></pre></div>\n<p>Η μέθοδος <a class=\"reference internal\" href=\"/el/2.1/topics/testing/advanced/#django.test.utils.setup_test_environment\" title=\"django.test.utils.setup_test_environment\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">setup_test_environment()</span></code></a> εγκαθιστά έναν template renderer ο οποίος μας επιτρέπει να ερευνήσουμε μερικά πρόσθετα attributes στα responses όπως το <code class=\"docutils literal notranslate\"><span class=\"pre\">response.context</span></code> το οποίο δεν θα ήταν διαθέσιμο αλλιώς. Σημειώστε ότι αυτή η μέθοδος <em>δεν</em> φτιάχνει καμία βάση δεδομένων για τεστ, κάτι το οποίο σημαίνει ότι θα τρέξει χρησιμοποιώντας την ήδη υπάρχουσα database. Επίσης σημειώστε ότι η έξοδος ίσως να είναι διαφορετική για εσάς ανάλογα τις ερωτήσεις που έχετε δημιουργήσει. Ίσως να λάβετε μη αναμενόμενα αποτελέσματα αν η ρύθμιση <code class=\"docutils literal notranslate\"><span class=\"pre\">TIME_ZONE</span></code>, μέσα στο γενικό αρχείο ρυθμίσεων <code class=\"docutils literal notranslate\"><span class=\"pre\">settings.py</span></code>, δεν είναι σωστή. Αν δεν την έχετε ρυθμίσει, κάντε το προτού προχωρήσετε.</p>\n<p>Επόμενο βήμα είναι να κάνουμε import την τεστ κλάση για τον client (αργότερα στο αρχείο <code class=\"docutils literal notranslate\"><span class=\"pre\">tests.py</span></code> θα χρησιμοποιήσουμε την κλάση <a class=\"reference internal\" href=\"/el/2.1/topics/testing/tools/#django.test.TestCase\" title=\"django.test.TestCase\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">django.test.TestCase</span></code></a>, η οποία έρχεται με δικό της client, οπότε αυτό δεν θα είναι απαραίτητο):</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.test</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Client</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># create an instance of the client for our use</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">client</span> <span class=\"o\">=</span> <span class=\"n\">Client</span><span class=\"p\">()</span>\n</code></pre></div>\n<p>Με αυτό έτοιμο, μπορούμε να βάλουμε τον client να εργαστεί για εμάς:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># get a response from &#39;/&#39;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">&#39;/&#39;</span><span class=\"p\">)</span>\n<span class=\"go\">Not Found: /</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># we should expect a 404 from that address; if you instead see an</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># &quot;Invalid HTTP_HOST header&quot; error and a 400 response, you probably</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># omitted the setup_test_environment() call described earlier.</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">status_code</span>\n<span class=\"go\">404</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># on the other hand we should expect to find something at &#39;/polls/&#39;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"c1\"># we&#39;ll use &#39;reverse()&#39; rather than a hardcoded URL</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.urls</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">reverse</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">status_code</span>\n<span class=\"go\">200</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">content</span>\n<span class=\"go\">b&#39;\\n    &lt;ul&gt;\\n    \\n        &lt;li&gt;&lt;a href=&quot;/polls/1/&quot;&gt;What&amp;#39;s up?&lt;/a&gt;&lt;/li&gt;\\n    \\n    &lt;/ul&gt;\\n\\n&#39;</span>\n<span class=\"gp\">&gt;&gt;&gt; </span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">]</span>\n<span class=\"go\">&lt;QuerySet [&lt;Question: What&#39;s up?&gt;]&gt;</span>\n</code></pre></div>\n</section>\n<section id=\"improving-our-view\">\n<h3>Βελτιώνοντας το view<a class=\"heading-anchor\" href=\"#improving-our-view\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Η λίστα με τις ψηφοφορίες εμφανίζει ψηφοφορίες οι οποίες δεν έχουν γίνει ακόμη published (δηλαδή αυτές που το <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> έχει μελλοντική ημερομηνία). Ας το διορθώσουμε.</p>\n<p>Στον <a class=\"reference internal\" href=\"/el/2.1/intro/tutorial04/\"><span class=\"doc\">Οδηγό 4</span></a> εισάγαμε την έννοια του class-based view, βασισμένο στη <a class=\"reference internal\" href=\"/el/2.1/ref/class-based-views/generic-display/#django.views.generic.list.ListView\" title=\"django.views.generic.list.ListView\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">ListView</span></code></a>:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/views.py</figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">IndexView</span><span class=\"p\">(</span><span class=\"n\">generic</span><span class=\"o\">.</span><span class=\"n\">ListView</span><span class=\"p\">):</span>\n    <span class=\"n\">template_name</span> <span class=\"o\">=</span> <span class=\"s1\">&#39;polls/index.html&#39;</span>\n    <span class=\"n\">context_object_name</span> <span class=\"o\">=</span> <span class=\"s1\">&#39;latest_question_list&#39;</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">get_queryset</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;Return the last five published questions.&quot;&quot;&quot;</span>\n        <span class=\"k\">return</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s1\">&#39;-pub_date&#39;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n</code></pre></figure>\n<p>Θα χρειαστεί να βελτιώσουμε τη μέθοδο <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset()</span></code> και να την αλλάξουμε ούτως ώστε να ελέγχει και την ημερομηνία συγκρίνοντας τη με το <code class=\"docutils literal notranslate\"><span class=\"pre\">timezone.now()</span></code>. Πρώτα θα χρειαστεί να προσθέσουμε ένα import:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/views.py</figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.utils</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">timezone</span>\n</code></pre></figure>\n<p>και μετά πρέπει να βελτιώσουμε την μέθοδο <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset</span></code> ως εξής:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/views.py</figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">get_queryset</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">    </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">    Return the last five published questions (not including those set to be</span>\n<span class=\"sd\">    published in the future).</span>\n<span class=\"sd\">    &quot;&quot;&quot;</span>\n    <span class=\"k\">return</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">filter</span><span class=\"p\">(</span>\n        <span class=\"n\">pub_date__lte</span><span class=\"o\">=</span><span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span>\n    <span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s1\">&#39;-pub_date&#39;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n</code></pre></figure>\n<p>Η μέθοδος <code class=\"docutils literal notranslate\"><span class=\"pre\">Question.objects.filter(pub_date__lte=timezone.now())</span></code> επιστρέφει ένα queryset (συμπεριφέρεται ως μια Python λίστα) το οποίο περιέχει <code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>s των οποίων το <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> είναι μικρότερο ή ίσο (επειδή στην προκειμένη επρόκειτο για ημερομηνίες, ίσως είναι καλύτερα να πούμε νωρίτερο ή ίσο) από το <code class=\"docutils literal notranslate\"><span class=\"pre\">timezone.now</span></code> (από το τώρα).</p>\n</section>\n<section id=\"testing-our-new-view\">\n<h3>Τεστάροντας το καινούργιο view<a class=\"heading-anchor\" href=\"#testing-our-new-view\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Now you can satisfy yourself that this behaves as expected by firing up\n<code class=\"docutils literal notranslate\"><span class=\"pre\">runserver</span></code>, loading the site in your browser, creating <code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code> with\ndates in the past and future, and checking that only those that have been\npublished are listed. You don’t want to have to do that <em>every single time you\nmake any change that might affect this</em> - so let’s also create a test, based on\nour <a class=\"reference internal\" href=\"/el/2.1/ref/django-admin/#django-admin-shell\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">shell</span></code></a> session above.</p>\n<p>Προσθέστε τα ακόλουθα στο αρχείο <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/tests.py</span></code>:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/tests.py</figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.urls</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">reverse</span>\n</code></pre></figure>\n<p>Θα δημιουργήσουμε μια συνάρτηση συντόμευσης για να φτιάχνουμε ερωτήσεις και θα δημιουργήσουμε μια νέα test class:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/tests.py</figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"p\">):</span>\n<span class=\"w\">    </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">    Create a question with the given `question_text` and published the</span>\n<span class=\"sd\">    given number of `days` offset to now (negative for questions published</span>\n<span class=\"sd\">    in the past, positive for questions that have yet to be published).</span>\n<span class=\"sd\">    &quot;&quot;&quot;</span>\n    <span class=\"n\">time</span> <span class=\"o\">=</span> <span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">()</span> <span class=\"o\">+</span> <span class=\"n\">datetime</span><span class=\"o\">.</span><span class=\"n\">timedelta</span><span class=\"p\">(</span><span class=\"n\">days</span><span class=\"o\">=</span><span class=\"n\">days</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">create</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"n\">question_text</span><span class=\"p\">,</span> <span class=\"n\">pub_date</span><span class=\"o\">=</span><span class=\"n\">time</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">QuestionIndexViewTests</span><span class=\"p\">(</span><span class=\"n\">TestCase</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_no_questions</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        If no questions exist, an appropriate message is displayed.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertEqual</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">status_code</span><span class=\"p\">,</span> <span class=\"mi\">200</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertContains</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"p\">,</span> <span class=\"s2\">&quot;No polls are available.&quot;</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span> <span class=\"p\">[])</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_past_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        Questions with a pub_date in the past are displayed on the</span>\n<span class=\"sd\">        index page.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Past question.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span>\n            <span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span>\n            <span class=\"p\">[</span><span class=\"s1\">&#39;&lt;Question: Past question.&gt;&#39;</span><span class=\"p\">]</span>\n        <span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_future_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        Questions with a pub_date in the future aren&#39;t displayed on</span>\n<span class=\"sd\">        the index page.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Future question.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertContains</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"p\">,</span> <span class=\"s2\">&quot;No polls are available.&quot;</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span> <span class=\"p\">[])</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_future_question_and_past_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        Even if both past and future questions exist, only past questions</span>\n<span class=\"sd\">        are displayed.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Past question.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Future question.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span>\n            <span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span>\n            <span class=\"p\">[</span><span class=\"s1\">&#39;&lt;Question: Past question.&gt;&#39;</span><span class=\"p\">]</span>\n        <span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_two_past_questions</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        The questions index page may display multiple questions.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Past question 1.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">30</span><span class=\"p\">)</span>\n        <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s2\">&quot;Past question 2.&quot;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">5</span><span class=\"p\">)</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:index&#39;</span><span class=\"p\">))</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertQuerysetEqual</span><span class=\"p\">(</span>\n            <span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">context</span><span class=\"p\">[</span><span class=\"s1\">&#39;latest_question_list&#39;</span><span class=\"p\">],</span>\n            <span class=\"p\">[</span><span class=\"s1\">&#39;&lt;Question: Past question 2.&gt;&#39;</span><span class=\"p\">,</span> <span class=\"s1\">&#39;&lt;Question: Past question 1.&gt;&#39;</span><span class=\"p\">]</span>\n        <span class=\"p\">)</span>\n</code></pre></figure>\n<p>Ας κοιτάξουμε μερικά από τα παραπάνω τεστ πιο προσεκτικά.</p>\n<p>Πρώτα, φτιάξαμε μια συνάρτηση (<code class=\"docutils literal notranslate\"><span class=\"pre\">create_question</span></code>) την οποία τη χρησιμοποιούμε ως συντόμευση για να δημιουργούμε ερωτήσεις.</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">test_no_questions</span></code> doesn’t create any questions, but checks the message:\n«No polls are available.» and verifies the <code class=\"docutils literal notranslate\"><span class=\"pre\">latest_question_list</span></code> is empty.\nNote that the <a class=\"reference internal\" href=\"/el/2.1/topics/testing/tools/#django.test.TestCase\" title=\"django.test.TestCase\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">django.test.TestCase</span></code></a> class provides some additional\nassertion methods. In these examples, we use\n<a class=\"reference internal\" href=\"/el/2.1/topics/testing/tools/#django.test.SimpleTestCase.assertContains\" title=\"django.test.SimpleTestCase.assertContains\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">assertContains()</span></code></a> and\n<a class=\"reference internal\" href=\"/el/2.1/topics/testing/tools/#django.test.TransactionTestCase.assertQuerysetEqual\" title=\"django.test.TransactionTestCase.assertQuerysetEqual\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">assertQuerysetEqual()</span></code></a>.</p>\n<p>In <code class=\"docutils literal notranslate\"><span class=\"pre\">test_past_question</span></code>, we create a question and verify that it appears in\nthe list.</p>\n<p>In <code class=\"docutils literal notranslate\"><span class=\"pre\">test_future_question</span></code>, we create a question with a <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> in the\nfuture. The database is reset for each test method, so the first question is no\nlonger there, and so again the index shouldn’t have any questions in it.</p>\n<p>Και τα τεστ συνεχίζονται. Στην πραγματικότητα χρησιμοποιούμε τα τεστ για να εξομοιώσουμε τη συμπεριφορά του admin interface καθώς και των υπόλοιπων χρηστών που αλληλεπιδρούν με το site μέσω του browser. Κάθε φορά που αλλάζει το state του συστήματος μας (της εφαρμογής μας αν θέλετε) εξετάζουμε την έξοδο του συστήματος και αν αυτή ανταποκρίνεται στα αποτελέσματα που αναμένουμε.</p>\n</section>\n<section id=\"testing-the-detailview\">\n<h3>Τεστάροντας το <code class=\"docutils literal notranslate\"><span class=\"pre\">DetailView</span></code><a class=\"heading-anchor\" href=\"#testing-the-detailview\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Ότι έχουμε μέχρι τώρα λειτουργεί όπως θα θέλαμε να λειτουργεί. Ωστόσο, παρόλο που οι μελλοντικές ερωτήσεις δεν φαίνονται στην αρχική σελίδα της εφαρμογής μας (<em>index</em>), αν οι χρήστες μπορέσουν να μαντέψουν το σωστό URL που οδηγεί σε μια τέτοια ερώτηση (και δεν είναι κάτι δύσκολο αφού χρησιμοποιούμε IDs), τότε θα μπορέσουν να τη δουν. Άρα θα πρέπει να προσθέσουμε παρόμοιους περιορισμούς στο <code class=\"docutils literal notranslate\"><span class=\"pre\">DetailView</span></code>:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/views.py</figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">DetailView</span><span class=\"p\">(</span><span class=\"n\">generic</span><span class=\"o\">.</span><span class=\"n\">DetailView</span><span class=\"p\">):</span>\n    <span class=\"o\">...</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">get_queryset</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        Excludes any questions that aren&#39;t published yet.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"k\">return</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">filter</span><span class=\"p\">(</span><span class=\"n\">pub_date__lte</span><span class=\"o\">=</span><span class=\"n\">timezone</span><span class=\"o\">.</span><span class=\"n\">now</span><span class=\"p\">())</span>\n</code></pre></figure>\n<p>Και φυσικά θα χρειαστεί να προσθέσουμε μερικά τεστ για να ελέγξουμε ότι η ερώτηση (<code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>) της οποίας το <code class=\"docutils literal notranslate\"><span class=\"pre\">pub_date</span></code> είναι στο παρελθόν εμφανίζεται ενώ αυτής που είναι στο μέλλον όχι:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\">polls/tests.py</figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">QuestionDetailViewTests</span><span class=\"p\">(</span><span class=\"n\">TestCase</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_future_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        The detail view of a question with a pub_date in the future</span>\n<span class=\"sd\">        returns a 404 not found.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">future_question</span> <span class=\"o\">=</span> <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s1\">&#39;Future question.&#39;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=</span><span class=\"mi\">5</span><span class=\"p\">)</span>\n        <span class=\"n\">url</span> <span class=\"o\">=</span> <span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:detail&#39;</span><span class=\"p\">,</span> <span class=\"n\">args</span><span class=\"o\">=</span><span class=\"p\">(</span><span class=\"n\">future_question</span><span class=\"o\">.</span><span class=\"n\">id</span><span class=\"p\">,))</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">url</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertEqual</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"o\">.</span><span class=\"n\">status_code</span><span class=\"p\">,</span> <span class=\"mi\">404</span><span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">test_past_question</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">        The detail view of a question with a pub_date in the past</span>\n<span class=\"sd\">        displays the question&#39;s text.</span>\n<span class=\"sd\">        &quot;&quot;&quot;</span>\n        <span class=\"n\">past_question</span> <span class=\"o\">=</span> <span class=\"n\">create_question</span><span class=\"p\">(</span><span class=\"n\">question_text</span><span class=\"o\">=</span><span class=\"s1\">&#39;Past Question.&#39;</span><span class=\"p\">,</span> <span class=\"n\">days</span><span class=\"o\">=-</span><span class=\"mi\">5</span><span class=\"p\">)</span>\n        <span class=\"n\">url</span> <span class=\"o\">=</span> <span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:detail&#39;</span><span class=\"p\">,</span> <span class=\"n\">args</span><span class=\"o\">=</span><span class=\"p\">(</span><span class=\"n\">past_question</span><span class=\"o\">.</span><span class=\"n\">id</span><span class=\"p\">,))</span>\n        <span class=\"n\">response</span> <span class=\"o\">=</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">client</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">url</span><span class=\"p\">)</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">assertContains</span><span class=\"p\">(</span><span class=\"n\">response</span><span class=\"p\">,</span> <span class=\"n\">past_question</span><span class=\"o\">.</span><span class=\"n\">question_text</span><span class=\"p\">)</span>\n</code></pre></figure>\n</section>\n<section id=\"ideas-for-more-tests\">\n<h3>Ιδέες για περισσότερα τεστ<a class=\"heading-anchor\" href=\"#ideas-for-more-tests\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Χρωστάμε να προσθέσουμε μια παρόμοια μέθοδο <code class=\"docutils literal notranslate\"><span class=\"pre\">get_queryset</span></code> στο <code class=\"docutils literal notranslate\"><span class=\"pre\">ResultsView</span></code> και να δημιουργήσουμε μια νέα test class για αυτό το view. Θα είναι παρόμοια με αυτή που μόλις δημιουργήσαμε (<code class=\"docutils literal notranslate\"><span class=\"pre\">DetailView</span></code>). Στη πραγματικότητα θα είναι μια επανάληψη.</p>\n<p>Θα μπορούσαμε επίσης να βελτιώσουμε την εφαρμογή μας και με άλλους τρόπους προσθέτοντας τεστ καθώς προχωράμε. Για παράδειγμα είναι λιγάκι ανόητο να μπορούν να γίνονται published οι ερωτήσεις (<code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code>) δίχως κάποιες απαντήσεις (<code class=\"docutils literal notranslate\"><span class=\"pre\">Choices</span></code>). Οπότε, τα views θα μπορούσα να ελέγξουν γι’ αυτό και να εξαιρέσουν (exclude) τέτοιες ερωτήσεις. Τα τεστ μας θα δημιουργούσαν μια ερώτηση χωρίς <code class=\"docutils literal notranslate\"><span class=\"pre\">Choices</span></code> και μετά θα έλεγχαν ότι αυτή η ερώτηση δεν θα γινόταν published. Ομοίως θα δημιουργούσαμε μια ερώτηση (σε άλλο τεστ) <em>με</em> <code class=\"docutils literal notranslate\"><span class=\"pre\">Choices</span></code> αυτή τη φορά και ελέγχαμε ότι αυτή η ερώτηση <em>θα γινόταν</em> published.</p>\n<p>Ίσως οι logged-in admin χρήστες να επιτρεπόταν να δουν τις unpublished ερωτήσεις, αλλά όχι οι συνηθισμένοι επισκέπτες. Για ακόμη μια φορά: οτιδήποτε χρειάζεται να προστεθεί στο software για να επιτευχθεί ο στόχος σας θα πρέπει να συνοδεύεται από ένα τεστ, είτε γράψετε το test πρώτο και μετά τον κώδικα για να κάνετε το τεστ να επιτύχει είτε γράψετε τον κώδικα πρώτα και μετά το τεστ για να αποδείξετε ότι ο κώδικας σας λειτουργεί.</p>\n<p>Θα φτάσετε σε ένα σημείο όπου θα κοιτάζετε τα τεστ σας και θα αναρωτιέστε αν ο κώδικας σας υποφέρει από υπερβολικό αριθμό τεστ, κάτι το οποίο μας φέρνει στο εξής:</p>\n</section>\n</section>\n<section id=\"when-testing-more-is-better\">\n<h2>Όταν τεστάρετε, το περισσότερο είναι καλύτερο<a class=\"heading-anchor\" href=\"#when-testing-more-is-better\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Ίσως να φαίνεται ότι τα τεστ αρχίζουν και γίνονται τεράστια καθώς και μη ελέγξιμα. Σύντομα θα διαπιστώσετε ότι υπάρχει περισσότερος κώδικας στα τεστ παρά στην εφαρμογή την ίδια και ότι η επανάληψη είναι αντιαισθητική εν συγκρίσει με την κομψή περιεκτικότητα του υπόλοιπου κώδικα σας.</p>\n<p><strong>Δεν πειράζει</strong>. Αφήστε το να μεγαλώσει. Τις πιο πολλές φορές θα γράψετε ένα τεστ και μετά θα το ξεχάσετε. Εκείνο όμως θα κάνει τη δουλειά του όσο εσείς συνεχίζετε με την ανάπτυξη της εφαρμογής σας (και γενικά όλου του project σας).</p>\n<p>Μερικές φορές τα τεστ θα χρειαστεί να αναβαθμιστούν. Υποθέστε ότι βελτιώνουμε τα views ούτως ώστε μόνο οι ερωτήσεις (<code class=\"docutils literal notranslate\"><span class=\"pre\">Questions</span></code>) που έχουν απαντήσεις (<code class=\"docutils literal notranslate\"><span class=\"pre\">Choices</span></code>) θα γίνουν published. Σε αυτή την περίπτωση πολλά από τα τεστ μας θα αποτύχουν - <em>λέγοντας μας ακριβώς ποια τεστ θα χρειαστεί να βελτιώσουμε για να είναι συμβατά με την εφαρμογή μας</em>. Οπότε όσον αφορά την ανανέωση των τεστ, μην ανησυχείτε, τα τεστ φροντίζουν μόνα τους για τους εαυτούς τους.</p>\n<p>Στη χειρότερη, καθώς αναπτύσσετε την εφαρμογή σας, ίσως βρεθείτε σε μια θέση όπου μερικά τεστ είναι πλέον περιττά. Και πάλι μην ανησυχείτε. Στον κόσμο του τεστ ο πλεονασμός (redundancy) είναι <em>καλό</em> στοιχείο.</p>\n<p>Όσο τα τεστ σας είναι λογικά οργανωμένα δεν θα γίνουν αδιαχείριστα. Μερικοί κανόνες όσον αφορά τα τεστ είναι:</p>\n<ul class=\"simple\">\n<li><p>μια ξεχωριστή <code class=\"docutils literal notranslate\"><span class=\"pre\">TestClass</span></code> για κάθε μοντέλο ή view</p></li>\n<li><p>μια ξεχωριστή μέθοδο τεστ για κάθε κατάσταση που θέλετε να τεστάρετε</p></li>\n<li><p>να ονομάζετε τις μεθόδους τεστ όσο πιο περιεκτικά γίνεται σχετικά με αυτό που προσπαθούν να τεστάρουν</p></li>\n</ul>\n</section>\n<section id=\"further-testing\">\n<h2>Επιεπλέον τεστ<a class=\"heading-anchor\" href=\"#further-testing\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Αυτό ο οδηγός έκανε μια εισαγωγή στον κόσμο των τεστ. Υπάρχουν πολλά περισσότερα που μπορείτε να κάνετε όπως επίσης και περισσότερα εργαλεία που μπορείτε να χρησιμοποιήσετε για να φτιάξετε πολύ έξυπνα τεστ.</p>\n<p>Για παράδειγμα, παρόλο που τα τεστ σε αυτό τον οδηγό κάλυψαν ένα κομμάτι από την εσωτερική λειτουργία του κώδικα μας (μέθοδος μοντέλου) και άλλο ένα από τον τρόπο που τα views κάνουν publish τις ερωτήσεις μας, μπορείτε να χρησιμοποιήσετε ένα «in-browser» framework όπως το <a class=\"reference external\" href=\"http://seleniumhq.org/\">Selenium</a> για να τεστάρετε τον τρόπο με τον οποίο η HTML γίνεται render στον browser. Αυτά τα εργαλεία δεν σας επιτρέπουν να ελέγξετε μόνο τη συμπεριφορά του Django κώδικα σας αλλά και του κώδικα π.χ της Javascript (που πολύ πιθανόν να έχετε). Είναι αρκετά συναρπαστικό να μπορείτε να βλέπετε τα τεστ σας να ανοίγουν τον browser σας και να αλληλεπιδρούν με το site ακριβώς όπως ένας άνθρωπος θα το έκανε! Το Django περιλαμβάνει την κλάση <a class=\"reference internal\" href=\"/el/2.1/topics/testing/tools/#django.test.LiveServerTestCase\" title=\"django.test.LiveServerTestCase\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">LiveServerTestCase</span></code></a> για να διευκολύνει την ενσωμάτωση (συνεργασία) με εργαλεία όπως το Selenium.</p>\n<p>Αν έχετε μια περίπλοκη εφαρμογή ίσως να θέλετε να τρέχουν τα τεστ, αυτόματα, κάθε φορά που κάνετε commit για λόγους του <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Continuous_integration\">continuous integration</a>, ούτως ώστε το ίδιο το quality control (έλεγχος ποιότητας) - να είναι τουλάχιστον μερικώς - αυτοματοποιημένο.</p>\n<p>Ένας καλός τρόπος για να εντοπίσετε τα μέρη της εφαρμογής σας που δεν έχουν γραφτεί τεστ για αυτά, είναι να ελέγξετε το κατά πόσο είναι καλυμμένος ο κώδικας σας με τεστ. Αυτό επίσης σας βοηθά στο να αναγνωρίσετε αδύναμο ή ακόμη και αχρησιμοποίητο-νεκρό κώδικα. Αν δεν μπορείτε να τεστάρετε ένα κομμάτι κώδικα, τότε αυτό ίσως σημαίνει ότι ο κώδικας θα πρέπει να ξαναγραφτεί ή να αφαιρεθεί τελείως. Το Coverage θα σας βοηθήσει (και είναι πολύ σημαντικό αυτό) να αναγνωρίσετε τον νεκρό κώδικα. Δείτε στην αναφορά <a class=\"reference internal\" href=\"/el/2.1/topics/testing/advanced/#topics-testing-code-coverage\"><span class=\"std std-ref\">Δουλεύοντας με το coverage.py</span></a> για περισσότερες λεπτομέρειες.</p>\n<p>Επίσης το άρθρο <a class=\"reference internal\" href=\"/el/2.1/topics/testing/\"><span class=\"doc\">Testing με το Django</span></a> περιέχει περισσότερες πληροφορίες σχετικά με το testing.</p>\n</section>\n<section id=\"what-s-next\">\n<h2>Επόμενα βήματα<a class=\"heading-anchor\" href=\"#what-s-next\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Για περισσότερες πληροφορίες σχετικά με τα τεστ δείτε στο άρθρο <a class=\"reference internal\" href=\"/el/2.1/topics/testing/\"><span class=\"doc\">Testing με το Django</span></a>.</p>\n<p>Όταν είστε εξοικειωμένοι με το concept και τη λειτουργία των τεστ στο Django, διαβάστε το <a class=\"reference internal\" href=\"/el/2.1/intro/tutorial06/\"><span class=\"doc\">έκτο μέρος αυτού του οδηγού</span></a> για να μάθετε περισσότερα σχετικά με τη διαχείριση των στατικών αρχείων (static files).</p>\n</section>","rootId":"writing-your-first-django-app-part-5","toc":[{"title":"Εισαγωγή στα αυτοματοποιημένα τεστ","anchor":"introducing-automated-testing","children":[{"title":"Τι είναι τα αυτοματοποιημένα τεστ;","anchor":"what-are-automated-tests","children":[]},{"title":"Γιατί χρειάζεται να δημιουργείτε τεστ","anchor":"why-you-need-to-create-tests","children":[{"title":"Τα τεστ θα σας γλιτώσουν πολύτιμο χρόνο","anchor":"tests-will-save-you-time","children":[]},{"title":"Τα τεστ όχι μόνο αναγνωρίζουν τα προβλήματα αλλά τα εμποδίζουν επίσης","anchor":"tests-don-t-just-identify-problems-they-prevent-them","children":[]},{"title":"Τα τεστ κάνουν τον κώδικα σας πιο προσιτό","anchor":"tests-make-your-code-more-attractive","children":[]},{"title":"Τα τεστ βοηθούν τις ομάδες να συνεργαστούν","anchor":"tests-help-teams-work-together","children":[]}]}]},{"title":"Βασικές στρατηγικές τεστ","anchor":"basic-testing-strategies","children":[]},{"title":"Γράφοντας το πρώτο σας τεστ","anchor":"writing-our-first-test","children":[{"title":"Βρίσκουμε ένα bug","anchor":"we-identify-a-bug","children":[]},{"title":"Δημιουργία ενός τεστ για την ανάδειξη του bug","anchor":"create-a-test-to-expose-the-bug","children":[]},{"title":"Τρέχοντας τα τεστ","anchor":"running-tests","children":[]},{"title":"Διορθώνοντας το bug","anchor":"fixing-the-bug","children":[]},{"title":"Περισσότερα περιεκτικά τεστ","anchor":"more-comprehensive-tests","children":[]}]},{"title":"Τεστ ένα view","anchor":"test-a-view","children":[{"title":"Ένα τεστ για τη view","anchor":"a-test-for-a-view","children":[]},{"title":"Τα τεστ του Django από την πλευρά του client","anchor":"the-django-test-client","children":[]},{"title":"Βελτιώνοντας το view","anchor":"improving-our-view","children":[]},{"title":"Τεστάροντας το καινούργιο view","anchor":"testing-our-new-view","children":[]},{"title":"Τεστάροντας το DetailView","anchor":"testing-the-detailview","children":[]},{"title":"Ιδέες για περισσότερα τεστ","anchor":"ideas-for-more-tests","children":[]}]},{"title":"Όταν τεστάρετε, το περισσότερο είναι καλύτερο","anchor":"when-testing-more-is-better","children":[]},{"title":"Επιεπλέον τεστ","anchor":"further-testing","children":[]},{"title":"Επόμενα βήματα","anchor":"what-s-next","children":[]}],"breadcrumbs":[{"docname":"intro/index","title":"Ξεκινώντας","url":"/el/2.1/intro/"}],"prev":{"docname":"intro/tutorial04","title":"Γράφοντας το πρώτο σας Django app, μέρος 4","url":"/el/2.1/intro/tutorial04/"},"next":{"docname":"intro/tutorial06","title":"Γράφοντας το πρώτο σας Django app, μέρος 6","url":"/el/2.1/intro/tutorial06/"},"formats":{"html":"/el/2.1/intro/tutorial05/","markdown":"/el/2.1/intro/tutorial05.md","json":"/el/2.1/intro/tutorial05.json"},"source":"https://github.com/django/django/blob/stable/2.1.x/docs/intro/tutorial05.txt","official":"https://docs.djangoproject.com/el/2.1/intro/tutorial05/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0","1.11","1.10"],"inLocales":["en","zh-hans","fr","ja","id","pt-br","ko","es","el","pl"]}