παπι Δημοσ. 18 Νοεμβρίου 2017 Δημοσ. 18 Νοεμβρίου 2017 dafuq is this??????? ειπε ο τυπος που νομιζε οτι πηρε τον τιτλο του phpista
zynif Δημοσ. 18 Νοεμβρίου 2017 Μέλος Δημοσ. 18 Νοεμβρίου 2017 dafuq is this??????? ειπε ο τυπος που νομιζε οτι πηρε τον τιτλο του phpista Understanding Mock Objects by defacer Στο θέμα μας τωρα Τροποποιησα το test και την coupon reminder ως εξης <?php require_once './HostSystemClockTimeProvider.php'; require_once './MailerObject.php'; require_once './Database.php'; require_once './CouponReminder.php'; class CouponReminderTests extends PHPUnit\Framework\TestCase { public function testCoupon() { $coup1 = new stdClass(); $coup1->coupon_code = 'AXFRF5600'; $coup1->email = '[email protected]'; $coup1->diff = 30; $expiredCoupons = [ $coup1 ]; $expectedEmails = [ [ 'email' => $expiredCoupons[0]->email, 'body' => 'Coupon '.$expiredCoupons[0]->coupon_code.' is about to expire', ] ]; $mailProvider = $this->getMockForAbstractClass(MailProvider::class); $mailProvider ->expects($this->exactly(count($expectedEmails))) ->method('sendEmail') ->withConsecutive($expectedEmails); $clockProvider = new HostSystemClockTimeProvider(); $repo = $this->getMockBuilder(Database::class) ->disableOriginalConstructor() ->disableOriginalClone() ->disableArgumentCloning() ->disallowMockingUnknownTypes() ->getMock(); $repo->method('getExpiredCoupons') ->willReturn($expiredCoupons); $coup = new CouponReminder($clockProvider, $mailProvider, $repo); $coup->notify(); } } <?php class CouponReminder { private $clockProvider; private $mailProvilder; private $dbProvider; public function __construct($clockprovider, $mailer,$db) { $this->clockProvider = $clockprovider; $this->mailProvilder = $mailer; $this->dbProvider = $db; } public function notify() { $aboutToExpire = $this->getNotExpired(); foreach ($aboutToExpire as $exp) { $this->sendMail($exp); } } private function sendMail($args) { $this->mailProvilder->sendEmail(array('email' => $args->email, 'body' => 'Coupon '.$args->coupon_code.' is about to expire')); } public function getNotExpired() { return $this->dbProvider-> getExpiredCoupons($this->clockProvider->getCurrentDateTime()->format("Y-m-d H:i:s")); } } και οντως το test περνάει ! Άρα στην ουσια μέχρι στιγμής τεσταραμε οτι θα σταλει email σε ολα τα e-mails που περιέχει το αποτέλεσμα του query ; Αλλά , υπάρχει και το εξης θεμα. Στο production το cron job θα τρεχει καθε μια ωρα ,και αν υπολείπονται Χ μερες απο τη ληξη του θα αποστέλλεται ενα mail. Για να μην ξανασταλεί mail πρέπει η βαση να γίνεται update με το timestamp Που στειλαμε το mail. Πως θα γινει Mock αυτό; Και βασικα τι κακο υπάρχει στο να χρησιμοποιούμε μια test_db για τα tests και να αρχικοποιούμε τους πίνακες που θέλουμε μεσα στα setUp() και setupBeforeClass();
defacer Δημοσ. 19 Νοεμβρίου 2017 Δημοσ. 19 Νοεμβρίου 2017 και οντως το test περνάει ! Άρα στην ουσια μέχρι στιγμής τεσταραμε οτι θα σταλει email σε ολα τα e-mails που περιέχει το αποτέλεσμα του query ; Τέλεια! Το πρώτο πράγμα που πρέπει να κάνεις τώρα με το test που περνάει είναι να αρχίσεις να πειράζεις μερικά πράγματα, όπως "Σαμποτάζ" μέσα στην CouponReminder (κάνεις δηλαδή λάθος πράγματα επίτηδες, π.χ. comment out to sendEmail μέσα στο loop) Αλλάζεις τα assertions μέσα στο test έτσι που να μην επαληθευτούν -- π.χ. το κάνεις $this->exactly(count() + 666) Αλλάζεις μέρος από τα δεδομένα εισόδου χωρίς να αλλάξεις τα assertions, π.χ. επίτηδες λάθος κάτι μέσα στο $expectedEmails Σε πρώτη φάση αυτό θα σε βοηθήσει να αποκτήσεις μια καλύτερη αίσθηση/μεγαλύτερη εμπιστοσύνη του πώς το test θα σε προφυλάξει από μελλοντικά (ή και τωρινά καμιά φορά ) bugs. Θα δεις ότι μετά από λίγο καιρό, όταν θα έχεις χωνέψει τη λογική του unit testing, γράφοντας ένα test πέρα από την "τεχνικού" είδους προστασία που σου παρέχει, θα έχεις και τεράστια ψυχολογικά ωφέλη που μετράνε πολύ: βεβαιότητα ότι ο κώδικας που τεστάρεται δεν έχει bugs, και πολύ καλύτερη αίσθηση του τι θα μπορούσε να πηγαίνει στραβά και πού όταν εμφανιστεί κάποιο bug τελικά. Όταν έχεις σχεδιάσει τις χορογραφίες των τεστ και τις έχεις δει να περνάνε και να χαλάνε και να ξαναπερνάνε κλπ κλπ και κάποια στιγμή συμβαίνει κάτι απρόσμενο, η γνώση και σιγουριά που έχεις μαζέψει για τη συμπεριφορά του κώδικα σου επιτρέπουν πολλές φορές educated guesses που σε κάποιον τρίτο φαίνονται psychic debugging powers. Τώρα, όσον αφορά το τι τεστάραμε. Αυτό που τεστάραμε, και ταυτόχρονα το μόνο πράγμα ever που θα πρέπει να τεστάρουμε σε ένα unit test όπως αυτό, είναι: ότι η CouponReminder τηρεί το contract της με τον υπόλοιπο κόσμο. Ή ισοδύναμα, το public interface της λειτουργεί όπως υποτίθεται ότι θα έπρεπε. Εδώ τώρα φτάσαμε στο δεύτερο πολύ σημαντικό πράγμα που κάνουν τα interfaces (με την έννοια που έχει το keyword interface στην PHP, είναι άλλο πράγμα από το "public interface" που είπα παραπάνω): σε "αναγκάζουν" να σκεφτείς ποιό είναι το public interface μιας class που θα χρησιμοποιήσεις. Τι σημαίνει public interface μιας class, ή function? Είναι το σύνολο από τις απαιτήσεις που έχει η class/function από αυτόν που την καλεί, συν τις υποσχέσεις που δίνει στον ίδιο άπαξ και κληθεί. Για παράδειγμα, το public interface της strlen είναι: "Εσύ θα μου δώσεις ένα string ή κάτι που μπορεί να μετατραπεί σε string, και αν το κάνεις τότε υπόσχομαι πως θα σου επιστρέψω το μήκος του σε bytes". To public interface του ICouponRepository είναι: "Έχω μια method getExpiredCoupons. Όταν την καλέσεις και μου δώσεις μια ημερομηνία, εγώ θα φροντίσω να βρω όλα τα κουπόνια που διαχειρίζομαι και τα οποία έχουν λήξει εκείνη τη στιγμή ή νωρίτερα και να στείλω ένα mail στο χρήστη που ανήκει το καθένα προειδοποιώντας τον ότι αυτό θα λήξει". Εδώ μια μικρή παρένθεση: βλέποντας το ICouponRepository και μόνο, δε θα μπορούσε ποτέ κανένας να φανταστεί ότι αυτό που έγραψα παραπάνω είναι το public interface της. Αυτό δεν αλλάζει κάτι στη θεωρία, απλά σημαίνει ότι το PI της ICouponRepository είναι απλά πολύ άσχημα σχεδιασμένο, καθώς το όνομα getExpiredCoupons είναι τελείως λάθος και παραπλανεί τον αναγνώστη. Επίσης ακόμα και να ήταν πιο ακριβές περιγραφικά (π.χ. να λεγόταν η method "sendEmailsForCouponsExpiredOnOrBefore") πάλι θα ήταν κακοσχεδιασμένο επειδή τι δουλειά έχει ένα πράγμα που λέγεται SomethingRepository να στέλνει emails. Δε θα επειμείνω σ' αυτά, είναι θέματα OO design, πολύ σημαντικά μεν αλλά θα εκτροχιαστούμε. Απλά θα υποθέσουμε ότι τα έχεις σχεδιασμένα σωστά, βγάζουν νόημα, λουλούδια ανθισμένα και γύρω γύρω μέλισσες, και θα μιλήσουμε μόνο για το testing. Απλά κράτα το γεγονός ότι είναι πολύ πιο εύκολο να φτιάξεις μια class μπουρδέλο όταν τη "σχεδιάζεις" σαν νιούφης πετώντας μέσα ότι method σου κάνει κούκου, παρά απ' ότι αν αναγκάσεις τον εαυτό σου να πει "ποιό θα είναι το public interface του πράγματος που θέλω να κάνω; έτσι που το βλέπω στο χαρτί, χωρίς να έχει γραφτεί ούτε γραμμή κώδικα, βγάζει νόημα; Έχει μια λογική συνέπεια στο σύνολό του;" Εδώ λοιπόν τεστάραμε το public interface της CouponReminder: Αν εσύ κύριε μου δώσεις ένα σωστά υλοποιημένο IClockProvider και IMailerService και ICouponRepository Και καλέσεις τη notify() Τότε εγώ θα πάρω τα κουπόνια που λήγουν τώρα (ό,τι μας πει ο clock provider πως είναι "τώρα") ρωτώντας το repository, θα φτιάξω μερικά mail και τα πω στο mail service να τα στείλει. Αυτό και τίποτα παραπάνω. Αν ο clock provider λέει μαλακίες, ή αν το repository δε δουλεύει σωστά, ή αν το mail service είναι mock που δε στέλνει τίποτα απολύτως, αυτά δεν είναι δικό μου πρόβλημα λέει η CouponReminder. Εγώ αυτό που υποσχέθηκα το έκανα, βρες τα με τους άλλους. Αυτή είναι η πεμπτουσία του unit test. Αντίστοιχα, το κάθε ένα από τα dependencies έχει ένα δικό του public interface και στην κατασκευή σύνθετων services (με πολλά/σύνθετα dependencies) από απλά (π.χ. IClockProvider δεν έχει κανένα dependency και μόνο μια method που κάνει ένα πολύ απλό πράγμα) απλά πατάμε στις εγγυήσεις του public interface ενός μικρού συστήματος για να φτιάξουμε ένα πιο σύνθετο που παρέχει με τη σειρά του πιο σύνθετες υπηρεσίες/εγγυήσεις. Like Lego. Το θέμα είναι πως αν το κάνεις σωστά και έχεις καλά unit tests, μπορεί τελικά να καταλήξεις με ένα service που θα έχει έμμεσο dependency σε time provider μέσω μιας αλυσίδας 100 dependencies, αλλά δε θα χρειαστεί ποτέ να τεστάρεις το service σου όσον αφορά πράγματα που έχουν να κάνουν με το χρόνο. Αφού θεωρούμε ότι τα dependencies μου δουλεύουν καλά (έχουν δικά τους test), τότε το μόνο που χρειάζεται για να αποδείξουμε ότι και γω δουλεύω καλά είναι απλά να τεσταριστεί η δική μου συμπεριφορά (το 3ο bullet στην λίστα λίγο παραπάνω). Τώρα... καλά όλα αυτά, αλλά δεν τεστάραμε βασικά ότι η εφαρμογή actually στέλνει emails. Αυτό, συν σχόλια για το υπόλοιπο της απάντησής σου, λίγο αργότερα που θα έχω χρόνο. Έχουμε ακόμα πολλά να πούμε.
zynif Δημοσ. 19 Νοεμβρίου 2017 Μέλος Δημοσ. 19 Νοεμβρίου 2017 (επεξεργασμένο) Οκ υποκλίνομαι !!! Επίσης βλέπω οτι το phpunit εχει το @dataProvider Αν φτιαξουμε ενα dataprovider οπου θα περνάμε ενα clockprovider και repo provider δηλαδη πχ κατι σαν το παρακάτω υπερπλουστευμένο παράδειγμα public function addDataProvider() { return array( array('2017-11-18 00:00:00','[email protected]',true), array('2017-11-18 01:00:00','[email protected]',false) ); } Έστω οτι θα σεταρουμε το cron job να τρεχει ανα μια ωρα . Στην πρωτη γραμμη , επειδη ληγει το κουπονι στις 18/11 θα στελνεται mail, ενω στην δευτερη γραμμή δεν πρέπει να σταλει καποιο mail , αφου σταλθηκε ηδη. Υπάρχει κάποιος άλλος τρόπος ; Με τρώει η περιέργεια ! Επεξ/σία 19 Νοεμβρίου 2017 από zynif
Click4Money Δημοσ. 22 Νοεμβρίου 2017 Δημοσ. 22 Νοεμβρίου 2017 @defacer πολύ χρήσιμες πληροφοίες, σε ευχαριστούμε πολύ. Αναμένω την συνέχεια με ανυπομονησία
defacer Δημοσ. 22 Νοεμβρίου 2017 Δημοσ. 22 Νοεμβρίου 2017 Χαίρομαι που το βρίσκετε χρήσιμο, θα προσπαθήσω να το φέρω μέχρι τη λογική του κατάληξη πριν ξεχάσω τι ήθελα να αναφέρω ή θυμηθώ πόσα σεντόνια έχω χαλάσει. Αλλά , υπάρχει και το εξης θεμα. Στο production το cron job θα τρεχει καθε μια ωρα ,και αν υπολείπονται Χ μερες απο τη ληξη του θα αποστέλλεται ενα mail.Για να μην ξανασταλεί mail πρέπει η βαση να γίνεται update με το timestamp Που στειλαμε το mail. Πως θα γινει Mock αυτό; Και βασικα τι κακο υπάρχει στο να χρησιμοποιούμε μια test_db για τα tests και να αρχικοποιούμε τους πίνακες που θέλουμε μεσα στα setUp() και setupBeforeClass(); Το λοιπόν, εδώ είναι που λέμε για τα διάφορα είδη tests που υπάρχουν. Μιλάω πάντα για tests που γράφουμε σε μορφή κώδικα, όχι άλλα πράγματα που μπορεί να δοκιμάσει κανείς (π.χ. fuzzing). Υπάρχει πολλή λογοτεχνία πάνω στο θέμα και δεν συμφωνούν όλοι μεταξύ τους. Εδώ δε θα πιάσουμε τις λεπτομέρειες, μόνο τα βασικά. Υπάρχουν λοιπόν 3 είδη από παρόμοια test: Unit tests Integration/functional tests System tests Για το καλύτερο αποτέλεσμα είναι πολύ σημαντικό να ξέρει κανείς τι κάνει το καθένα και ποιά η χρήση του και πώς κολλάει με τα υπόλοιπα για να αλληλοσυμπληρώνονται. Unit tests είπαμε: γυμνάζουμε ένα unit (class, function) ελέγχοντας όλο το περιβάλλον. Αυτά είναι "white box tests": τα γράφει κανείς βλέποντας τον κώδικα του unit που θα τεστάρει για να δει τα public interface που τον ενδιαφέρουν. Ούτε τεστάρουμε κάτι που δεν είναι στο public interface, ούτε μπορούμε να τεστάρουμε αυτά που είναι αν δεν ξέρουμε τι προαπαιτήσεις υπάρχουν που πρέπει να δημιουργήσουμε στο τεστ. Επίσης, επειδή αυτά τα τεστ είναι (συγκριτικά) πολύ απλά στο setup και πολύ γρήγορα στην εκτέλεση, εδώ είναι που σκίζουμε τα πέταλα: τεστ όλα τα πιθανά σενάρια που μπορεί να δημιουργηθούν στο σύμπαν. Δεν είναι παράλογο μία και μόνη method που ζει μεγάλη ζωή και παίρνει 5-6 παραμέτρους να έχει πάνω της πάνω από 1000 test cases. Πώς θα γράψεις 1000 test cases? Προφανώς όχι ένα ένα, θα δούμε παρακάτω αν και το ανέφερες ήδη: data providers. Και μπαδεγουέι, αν στην πορεία συγγραφής του τεστ σκοντάψουμε σε πράγματα όπως "τώρα πρέπει να αλλάξω αυτό αλλά πως θα γίνει που είναι private" -- συγχαρητήρια, μόλις βρήκατε αποδείξεις ότι το συγκεκριμένο unit δεν είναι καλά γραμμένο και γι' αυτό δε μπορεί να τεσταριστεί σωστά (ή τέλος πάντων εύκολα). Integration tests είναι περίπου το ίδιο με τα unit, μόνο που εδώ τεστάρουμε τη χορογραφία μεταξύ δύο ή παραπάνω units που έτσι προσφέρουν πιο advanced functionality. Για παράδειγμα θα μπορούσαμε αντί να τεστάρουμε τη συμπεριφορά του CouponReminder (ότι καλεί κάτι στον mail provider) να βάλουμε και έναν πιο αληθινό mail provider που με τη σειρά του κάνει κάτι άλλο, και να τεστάρουμε σαν output το κάτι άλλο. Τα integration tests βοηθάνε στο να έχουμε μια βεβαιότητα ότι κάποια μεγάλα κομμάτια business logic όντως δουλεύουν και δεν έχουμε κάνει κάποιο ηλίθιο λάθος στη σύνδεση των επιμέρους classes. Για παράδειγμα, ένα integration test θα μπορούσε να είναι "δίνω στο OrderService μια παραγγελία και περιμένω να δω σαν αποτέλεσμα να φύγει ένα email στον πελάτη, να ξεκινήσει μια διαδικασία διεκπαιραίωσης στο σύστημα, και να καταγραφούν τα στοιχεία της παραγγελίας στη database". Σ' αυτά τα test δε θέλουμε και δε μπορούμε να ελέγξουμε όλα τα πιθανά σενάρια για όλες τις παραμέτρους εισόδου όλων των units που εμπλέκονται. Τα πιθανά σενάρια θα είναι πολύ λιγότερα στον αριθμό (διψήφια το πολύ θα έλεγα) και θα καλύπτουν μόνο τις πολύ σημαντικές περιπτώσεις που οδηγούν σε πολύ διαφορετικό αποτέλεσμα στο σύστημα. Αφού τα επιμέρους units συνεργάζονται σωστά στις βασικές περιπτώσεις (integration) και αφού το κάθε ένα από μόνο του δουλεύει σωστά σε όλες τις περιπτώσεις ever (unit) τότε έχουμε high confidence ότι δουλεύουν σωστά σε όλες τις περιπτώσεις και μαζί. Τέλος, τα system tests είναι black box tests τα οποία αμολάμε πάνω στην έτοιμη στημένη εφαρμογή. Black box ίσον δε βλέπουμε τον κώδικα καν, δε δουλεύουμε με classes, δουλεύουμε με το ίδιο interface που έχει ο τελικός χρήστης. Για παράδειγμα, κάνουμε manually HTTP request όπως θα έκανε ο browser του χρήστη και περιμένουμε να γίνουν αντίστοιχα πράγματα. Οπότε, TLDR, αν θες νας δεις ότι όντως στέλνονται email θα γράψεις κι ένα integration test που κάνει τα παρακάτω: Γράφει στην αληθινή (όχι mock) database ένα ή περισσότερα κουπόνια. Τεστάρει την CouponReminder με τα αληθινά dependencies, εκτός ίσως από αυτά που σηματοδοτούν το "τέλος" της business logic -- στην περίπτωσή μας, μπορείς να κάνεις mock το mail service και να πεις ΟΚ, είμαι σίγουρος πως εκτός κι αν η εφαρμογή μου σταματήσει να στέλνει mail τελείως τότε η απόδειξη πως καλείται η sendMail() σωστά είναι αρκετή σιγουριά για μένα. Τώρα μπορώ να σου απαντήσω και που είναι το κακό στο να κάνεις τα test "αληθινά" χωρίς mocks: Τζάμπα κόπος να τα σετάρεις όλα σωστά στο test. Δε μπορείς να τρέξεις το test αν δεν έχεις πρόχειρα όλα τα services που θα χρειαστούν. Τώρα εδώ μπορεί το μόνο service που χρειάζεσαι να είναι μια db, αλλά σε εφαρμογές ρεαλιστικού μεγέθους δεν είναι τόσο εύκολα. Το setup των dependencies μπορεί να είναι χρονικά ή υπολογιστικά ασύμφορο (π.χ. στην εφαρμογή που δουλεύω αυτό τον καιρό ένα integration test θέλει ~3 sec μόνο για το setup του -- καταλαβαίνεις πως δε μπορώ να τρέχω 1000 τέτοια με διαφορετικές παραμέτρους για κανένα λόγο, ούτε και μπορώ να τρέχω όλα τα integration test κάθε φορά που αλλάζει κάτι μόνο για να βεβαιωθώ ότι δε χάλασα τίποτα). Αν τρέχεις πολλά test cases παράλληλα (o build server μας τρέχει 20 παράλληλα γιατι αλλιώς το build θα έπαιρνε κανα μισάωρο) ίσως είναι δύσκολο να εξυπηρετηθούν όλα χωρίς αίμα, δάκρυα και ιδρώτα. Π.χ. τεστάρεις με πραγματική database και χρειάζεσαι εγγραφές στον πίνακα users, πώς θα καταφέρεις τα 100 test σου να λειτουργούν πάντα άψογα όποια κι αν είναι η σειρά ή το timing με το οποίο θα εκτελεστούν; Κανένα από αυτά δεν πρέπει να πειράξει τίποτα που θα κάνει κανένα άλλο, αλλά όλα δουλεύουν χρησιμοποιώντας τον ίδιο πίνακα. Δε γίνεται. Το ότι το cron τρέχει στον server σου κάθε μία ώρα δε μας απασχολεί καθόλου, δε θα εμφανίζεται πουθενά cron στα test εδώ. Αν θες κάνεις ένα system test αλλά δε νομίζω ότι θα χρειαστεί στο επίπεδο εφαρμογής που είσαι. Εμπιστεύεσαι τον πραγματικό data provider ότι θα δουλέψει σωστά και όταν του περάσεις μια ώρα θα γράψει το σωστό query κλπ; Τότε άμα περνάνε τα unit tests το σύστημά σου θα δουλεύει σωστά. Δεν τον εμπιστεύεσαι; Γράψε ένα integration τεστ που βάζεις μέσα 4-5 κουπόνια με χρόνους πολύ πριν, 1 δευτερόλεπτο πριν, ακριβώς, 1 δευτερόλεπτο μετά, και πολύ μετά, την ψεύτικη fixed ώρα που θα λέει στο test σου ο clock provider, και επαλήθευσε πόσα από αυτά σου γυρνάνε πίσω όταν καλέσεις getWhatever() στον CouponReminder. Απο κει και πέρα δε χρειάζεται να ασχοληθείς με το αν αυτό θα είναι αρκετό για να σταλεί email: έχεις ήδη unit test που καλύπτει αυτό το μέρος της διαδικασίας.
zynif Δημοσ. 23 Νοεμβρίου 2017 Μέλος Δημοσ. 23 Νοεμβρίου 2017 Μαλιστα. Ευχαριστω για την απάντηση. Τώρα ομως εχω μια αλλη απορία διαβάζοντας το post σου και εχει να κάνει με CI\CD. Ειναι ανάγκη κάθε φορα που γινεται push στο repository να τρέχουν ολα τα tests ;
defacer Δημοσ. 23 Νοεμβρίου 2017 Δημοσ. 23 Νοεμβρίου 2017 Μαλιστα. Ευχαριστω για την απάντηση. Τώρα ομως εχω μια αλλη απορία διαβάζοντας το post σου και εχει να κάνει με CI\CD. Ειναι ανάγκη κάθε φορα που γινεται push στο repository να τρέχουν ολα τα tests ; Ε, "εξαρτάται" Πέρα από το χαβαλέ θα έλεγα πως ναι, είναι, τελεία. Για μένα ο μόνος λόγος να μη τρέχουν αυτόματα σε κάθε push είναι ότι έχεις one man project του ΣΚ και βαριέσαι να το κάνεις. Επειδή έχουν περάσει πάρα πολλά από τη συζήτηση, υπάρχει κάτι που ξέχασα να απαντήσω; Αυτή τη στιγμή έχω στο μυαλό μου ότι προσπέρασα λίγο τη φάση crash course in using data providers και το πώς θα ήταν στην πράξη (κώδικας) ένα λογικό test για το θέμα που συζητάμε. Και ίσως και dependency injection container, γιατί από τη στιγμή που έχεις όλα τα πράγματα με modular dependencies το φτιάξιμο των high level services καταντά κουραστικό.
zynif Δημοσ. 23 Νοεμβρίου 2017 Μέλος Δημοσ. 23 Νοεμβρίου 2017 Ε ας κλείσουμε τότε το topic με τον Dependency Injection Container Και κατι τελευταιο ετσι που το φερε η κουβέντα. Εχω λοιπον ενα eshop , και ο πελάτης μπορει να πληρώσει μέσω του παρόχου XYZ. Η πληρωμή γινεται ως εξης : Οταν κανει κλικ ο πελάτης στην σελιδα πληρωμής του eshop , γινεται redirect στο site του XYZ, (βασικα γινεται ενα HTTP Request με τα στοιχεια της πληρωμής του) βαζει ο πελάτης τα στοιχεια του, και πατάει πληρωμή και εφοσον ολα πανε καλα στο XYZ, το XYZ κανει http request πίσω στο eshop , οπου εμφανίζεται ενα μήνυμα success. Εδω πως μπορω να γραψω "tests" ? Εστω οτι το eshop ειναι σκετο PHP app με μια templating engine (Twig, Blade) και δεν εμπλέκονται front-end frameworks Το ολο routing καθορίζεται δηλαδή απο το back-end app Αν θες να φτιαξω και sample κώδικα να το δουμε!
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα