Seperti definisi ujian unit, sistem yang diuji harus diuji secara berasingan. Oleh itu, mana -mana objek luaran yang diperlukan oleh sistem perlu digantikan dengan objek mock. Seperti yang dikatakan oleh nama itu sendiri, objek mengejek tidak melaksanakan tugas sebenar; Sebaliknya mereka digunakan untuk memenuhi jangkaan sistem yang diuji. Sekiranya anda memerlukan penyegaran pada mengejek, sila rujuk salah satu artikel terdahulu saya: Mengejutkan dependensi dalam ujian AngularJS.
Dalam artikel ini, saya akan berkongsi satu set petua mengenai perkhidmatan ujian, pengawal dan penyedia di AngularJS. Coretan kod telah ditulis menggunakan melati dan boleh dijalankan dengan pelari ujian karma. Anda boleh memuat turun kod yang digunakan dalam artikel ini dari repo GitHub kami, di mana anda juga akan mencari arahan untuk menjalankan ujian.
Perkhidmatan adalah salah satu komponen yang paling biasa dalam aplikasi AngularJS. Mereka menyediakan cara untuk menentukan logik yang boleh digunakan semula di tempat utama supaya seseorang tidak perlu mengulangi logik yang sama berulang kali. Sifat perkhidmatan singleton memungkinkan untuk berkongsi sekeping data yang sama merentasi pelbagai pengawal, arahan dan juga perkhidmatan lain.
Perkhidmatan boleh bergantung kepada satu set perkhidmatan lain untuk melaksanakan tugasnya. Katakanlah, perkhidmatan yang dinamakan A bergantung kepada perkhidmatan B, C dan D untuk melaksanakan tugasnya. Semasa menguji perkhidmatan A, kebergantungan B, C dan D perlu digantikan dengan mengejek.
Kami secara umumnya mengejek semua kebergantungan, kecuali perkhidmatan utiliti tertentu seperti $ ROOTSCOPE dan $ Parse. Kami mencipta mata -mata mengenai kaedah yang perlu diperiksa dalam ujian (dalam melati, mengejek dirujuk sebagai mata -mata) menggunakan Jasmine.Createspy () yang akan mengembalikan fungsi baru.
mari kita pertimbangkan perkhidmatan berikut:
angular<span>.module('services', []) </span> <span>.service('sampleSvc', ['$window', 'modalSvc', function($<span>window, modalSvc</span>){ </span> <span>this.showDialog = function(message<span>, title</span>){ </span> <span>if(title){ </span> modalSvc<span>.showModalDialog({ </span> <span>title: title, </span> <span>message: message </span> <span>}); </span> <span>} else { </span> $<span>window.alert(message); </span> <span>} </span> <span>}; </span> <span>}]);</span>
Perkhidmatan ini hanya mempunyai satu kaedah (showdialog). Bergantung pada nilai input yang diterima oleh kaedah ini, ia memanggil salah satu daripada dua perkhidmatan yang disuntik ke dalamnya sebagai kebergantungan ($ tetingkap atau modalsvc).
Untuk menguji samplesvc kita perlu mengejek kedua -dua perkhidmatan yang bergantung, memuatkan modul sudut yang mengandungi perkhidmatan kami dan mendapatkan rujukan kepada semua objek:
<span>var mockWindow, mockModalSvc, sampleSvcObj; </span><span>beforeEach(function(){ </span> <span>module(function($provide){ </span> $provide<span>.service('$window', function(){ </span> <span>this.alert= jasmine.createSpy('alert'); </span> <span>}); </span> $provide<span>.service('modalSvc', function(){ </span> <span>this.showModalDialog = jasmine.createSpy('showModalDialog'); </span> <span>}); </span> <span>}); </span> <span>module('services'); </span><span>}); </span> <span>beforeEach(inject(function($<span>window, modalSvc, sampleSvc</span>){ </span> mockWindow<span>=$window; </span> mockModalSvc<span>=modalSvc; </span> sampleSvcObj<span>=sampleSvc; </span><span>}));</span>
Sekarang kita dapat menguji tingkah laku kaedah showdialog. Kedua -dua kes ujian yang boleh kita tulis untuk kaedah adalah seperti berikut:
coretan berikut menunjukkan ujian ini:
<span>it('should show alert when title is not passed into showDialog', function(){ </span> <span>var message="Some message"; </span> sampleSvcObj<span>.showDialog(message); </span> <span>expect(mockWindow.alert).toHaveBeenCalledWith(message); </span> <span>expect(mockModalSvc.showModalDialog).not.toHaveBeenCalled(); </span><span>}); </span> <span>it('should show modal when title is passed into showDialog', function(){ </span> <span>var message="Some message"; </span> <span>var title="Some title"; </span> sampleSvcObj<span>.showDialog(message, title); </span> <span>expect(mockModalSvc.showModalDialog).toHaveBeenCalledWith({ </span> <span>message: message, </span> <span>title: title </span> <span>}); </span> <span>expect(mockWindow.alert).not.toHaveBeenCalled(); </span><span>});</span>
Kaedah ini tidak mempunyai banyak logik untuk menguji, sedangkan perkhidmatan dalam aplikasi web biasa biasanya mengandungi banyak fungsi. Anda boleh menggunakan teknik yang ditunjukkan dalam hujung ini untuk mengejek dan mendapatkan rujukan kepada perkhidmatan. Ujian perkhidmatan harus meliputi setiap senario yang mungkin diandaikan semasa menulis perkhidmatan.
kilang dan nilai juga boleh diuji menggunakan teknik yang sama.
Proses penubuhan untuk menguji pengawal agak berbeza daripada perkhidmatan. Ini kerana pengawal tidak suntikan, sebaliknya mereka ditegaskan secara automatik apabila laluan dimuatkan atau, arahan NG-CONTROLLER disusun. Memandangkan kita tidak mempunyai pandangan yang dimuatkan dalam ujian, kita perlu secara manual meniru pengawal di bawah ujian.
Oleh kerana pengawal umumnya terikat dengan pandangan, tingkah laku kaedah dalam pengawal bergantung kepada pandangan. Juga, beberapa objek tambahan mungkin ditambah ke skop selepas pandangan telah disusun. Salah satu contoh yang paling biasa ialah objek bentuk. Untuk membuat ujian berfungsi seperti yang diharapkan, objek ini perlu dibuat secara manual dan ditambah kepada pengawal.
Pengawal boleh menjadi salah satu jenis berikut:
Jika anda tidak pasti mengenai perbezaannya, anda boleh membaca lebih lanjut mengenainya di sini. Sama ada, kita akan membincangkan kedua -dua kes ini.
Pertimbangkan pengawal berikut:
angular<span>.module('services', []) </span> <span>.service('sampleSvc', ['$window', 'modalSvc', function($<span>window, modalSvc</span>){ </span> <span>this.showDialog = function(message<span>, title</span>){ </span> <span>if(title){ </span> modalSvc<span>.showModalDialog({ </span> <span>title: title, </span> <span>message: message </span> <span>}); </span> <span>} else { </span> $<span>window.alert(message); </span> <span>} </span> <span>}; </span> <span>}]);</span>
Untuk menguji pengawal ini, kita perlu membuat contoh pengawal dengan lulus dalam objek skop $ dan objek yang dihina dari Perkhidmatan (DATASVC). Oleh kerana perkhidmatan ini mengandungi kaedah yang tidak segerak, kita perlu mengejek bahawa menggunakan teknik janji mengejek yang saya digariskan dalam artikel sebelumnya.
coretan berikut mengolok -olok perkhidmatan DataSvc:
<span>var mockWindow, mockModalSvc, sampleSvcObj; </span><span>beforeEach(function(){ </span> <span>module(function($provide){ </span> $provide<span>.service('$window', function(){ </span> <span>this.alert= jasmine.createSpy('alert'); </span> <span>}); </span> $provide<span>.service('modalSvc', function(){ </span> <span>this.showModalDialog = jasmine.createSpy('showModalDialog'); </span> <span>}); </span> <span>}); </span> <span>module('services'); </span><span>}); </span> <span>beforeEach(inject(function($<span>window, modalSvc, sampleSvc</span>){ </span> mockWindow<span>=$window; </span> mockModalSvc<span>=modalSvc; </span> sampleSvcObj<span>=sampleSvc; </span><span>}));</span>
kita kemudian boleh membuat skop baru untuk pengawal menggunakan $ ROOTSCOPE. $ Kaedah baru. Selepas membuat contoh pengawal, kami mempunyai semua bidang dan kaedah pada skop $ baru ini.
<span>it('should show alert when title is not passed into showDialog', function(){ </span> <span>var message="Some message"; </span> sampleSvcObj<span>.showDialog(message); </span> <span>expect(mockWindow.alert).toHaveBeenCalledWith(message); </span> <span>expect(mockModalSvc.showModalDialog).not.toHaveBeenCalled(); </span><span>}); </span> <span>it('should show modal when title is passed into showDialog', function(){ </span> <span>var message="Some message"; </span> <span>var title="Some title"; </span> sampleSvcObj<span>.showDialog(message, title); </span> <span>expect(mockModalSvc.showModalDialog).toHaveBeenCalledWith({ </span> <span>message: message, </span> <span>title: title </span> <span>}); </span> <span>expect(mockWindow.alert).not.toHaveBeenCalled(); </span><span>});</span>
Sebagai pengawal menambah medan dan kaedah ke $ skop, kita boleh menyemak sama ada ia ditetapkan ke nilai yang betul dan jika kaedah mempunyai logik yang betul. Pengawal sampel di atas menambah ungkapan biasa untuk memeriksa nombor yang sah. Mari tambahkan spesifikasi untuk menguji tingkah laku ungkapan biasa:
angular<span>.module('controllers',[]) </span> <span>.controller('FirstController', ['$scope','dataSvc', function($scope<span>, dataSvc</span>) { </span> $scope<span>.saveData = function () { </span> dataSvc<span>.save($scope.bookDetails).then(function (result) { </span> $scope<span>.bookDetails = {}; </span> $scope<span>.bookForm.$setPristine(); </span> <span>}); </span> <span>}; </span> $scope<span>.numberPattern = <span>/<span>^\d*$</span>/</span>; </span> <span>}]);</span>
Jika pengawal memulakan sebarang objek dengan nilai lalai, kita boleh menyemak nilai mereka dalam spec.
Untuk menguji kaedah SaveData, kita perlu menetapkan beberapa nilai untuk objek bookdetails dan bookform. Objek -objek ini akan terikat kepada unsur -unsur UI, jadi dicipta pada masa runtime apabila pandangan disusun. Seperti yang telah disebutkan, kita perlu memulakannya secara manual dengan beberapa nilai sebelum memanggil kaedah Savedata.
coretan berikut menguji kaedah ini:
<span>module(function($provide){ </span> $provide<span>.factory('dataSvc', ['$q', function($q) </span> <span>function save(data){ </span> <span>if(passPromise){ </span> <span>return $q.when(); </span> <span>} else { </span> <span>return $q.reject(); </span> <span>} </span> <span>} </span> <span>return{ </span> <span>save: save </span> <span>}; </span> <span>}]); </span><span>});</span>
Menguji pengawal yang menggunakan pengawal sebagai sintaks lebih mudah daripada menguji yang menggunakan $ skop. Dalam kes ini, contoh pengawal memainkan peranan model. Akibatnya, semua tindakan dan objek boleh didapati dalam contoh ini.
Pertimbangkan pengawal berikut:
<span>beforeEach(inject(function($rootScope<span>, $controller, dataSvc</span>){ </span> scope<span>=$rootScope.$new(); </span> mockDataSvc<span>=dataSvc; </span> <span>spyOn(mockDataSvc,'save').andCallThrough(); </span> firstController <span>= $controller('FirstController', { </span> <span>$scope: scope, </span> <span>dataSvc: mockDataSvc </span> <span>}); </span><span>}));</span>
Proses menggunakan pengawal ini adalah serupa dengan proses yang dibincangkan sebelumnya. Satu -satunya perbezaan ialah, kita tidak perlu membuat skop $.
<span>it('should have assigned right pattern to numberPattern', function(){ </span> <span>expect(scope.numberPattern).toBeDefined(); </span> <span>expect(scope.numberPattern.test("100")).toBe(true); </span> <span>expect(scope.numberPattern.test("100aa")).toBe(false); </span><span>});</span>
Oleh kerana semua ahli dan kaedah dalam pengawal ditambah kepada contoh ini, kita boleh mengaksesnya menggunakan rujukan contoh.
coretan berikut menguji medan NumberPattern yang ditambahkan ke pengawal di atas:
<span>it('should call save method on dataSvc on calling saveData', function(){ </span> scope<span>.bookDetails = { </span> <span>bookId: 1, </span> <span>name: "Mastering Web application development using AngularJS", </span> <span>author:"Peter and Pawel" </span> <span>}; </span> scope<span>.bookForm = { </span> <span>$setPristine: jasmine.createSpy('$setPristine') </span> <span>}; </span> passPromise <span>= true; </span> scope<span>.saveData(); </span> scope<span>.$digest(); </span> <span>expect(mockDataSvc.save).toHaveBeenCalled(); </span> <span>expect(scope.bookDetails).toEqual({}); </span> <span>expect(scope.bookForm.$setPristine).toHaveBeenCalled(); </span><span>});</span>
Pernyataan kaedah Savedata tetap sama. Satu -satunya perbezaan dalam pendekatan ini adalah dengan cara kita memulakan nilai -nilai kepada objek bookdetails dan bookform.
coretan berikut menunjukkan spec:
angular<span>.module('services', []) </span> <span>.service('sampleSvc', ['$window', 'modalSvc', function($<span>window, modalSvc</span>){ </span> <span>this.showDialog = function(message<span>, title</span>){ </span> <span>if(title){ </span> modalSvc<span>.showModalDialog({ </span> <span>title: title, </span> <span>message: message </span> <span>}); </span> <span>} else { </span> $<span>window.alert(message); </span> <span>} </span> <span>}; </span> <span>}]);</span>
Pembekal digunakan untuk mendedahkan API untuk konfigurasi seluruh aplikasi yang mesti dibuat sebelum permohonan bermula. Sebaik sahaja fasa konfigurasi aplikasi AngularJS berakhir, interaksi dengan penyedia tidak dibenarkan. Oleh itu, pembekal hanya boleh diakses dalam blok konfigurasi, atau blok penyedia lain. Kami tidak dapat memperoleh contoh penyedia menggunakan blok suntikan, sebaliknya kita perlu lulus panggilan balik ke blok modul.
mari kita pertimbangkan pembekal berikut yang bergantung kepada pembekal kedua (appconstants) yang tetap (lain -lain):
<span>var mockWindow, mockModalSvc, sampleSvcObj; </span><span>beforeEach(function(){ </span> <span>module(function($provide){ </span> $provide<span>.service('$window', function(){ </span> <span>this.alert= jasmine.createSpy('alert'); </span> <span>}); </span> $provide<span>.service('modalSvc', function(){ </span> <span>this.showModalDialog = jasmine.createSpy('showModalDialog'); </span> <span>}); </span> <span>}); </span> <span>module('services'); </span><span>}); </span> <span>beforeEach(inject(function($<span>window, modalSvc, sampleSvc</span>){ </span> mockWindow<span>=$window; </span> mockModalSvc<span>=modalSvc; </span> sampleSvcObj<span>=sampleSvc; </span><span>}));</span>
Untuk menguji ini, kita perlu mengejek kebergantungan. Anda dapat melihat bagaimana untuk melakukan ini dalam kod sampel.
Sebelum menguji pembekal, kita perlu memastikan bahawa modul itu dimuatkan dan siap. Dalam ujian, pemuatan modul ditangguhkan sehingga blok suntikan dilaksanakan atau, ujian pertama dilaksanakan. Dalam beberapa projek, saya telah melihat beberapa ujian yang menggunakan ujian pertama kosong untuk memuatkan modul. Saya bukan peminat pendekatan ini kerana ujian tidak melakukan apa -apa dan menambah kiraan kepada jumlah ujian anda. Sebaliknya, saya menggunakan blok suntikan kosong untuk mendapatkan modul yang dimuatkan.
coretan berikut mendapat rujukan dan memuat modul:
<span>it('should show alert when title is not passed into showDialog', function(){ </span> <span>var message="Some message"; </span> sampleSvcObj<span>.showDialog(message); </span> <span>expect(mockWindow.alert).toHaveBeenCalledWith(message); </span> <span>expect(mockModalSvc.showModalDialog).not.toHaveBeenCalled(); </span><span>}); </span> <span>it('should show modal when title is passed into showDialog', function(){ </span> <span>var message="Some message"; </span> <span>var title="Some title"; </span> sampleSvcObj<span>.showDialog(message, title); </span> <span>expect(mockModalSvc.showModalDialog).toHaveBeenCalledWith({ </span> <span>message: message, </span> <span>title: title </span> <span>}); </span> <span>expect(mockWindow.alert).not.toHaveBeenCalled(); </span><span>});</span>
Sekarang kita mempunyai semua rujukan, kita boleh memanggil kaedah yang ditakrifkan dalam pembekal dan menguji mereka:
angular<span>.module('controllers',[]) </span> <span>.controller('FirstController', ['$scope','dataSvc', function($scope<span>, dataSvc</span>) { </span> $scope<span>.saveData = function () { </span> dataSvc<span>.save($scope.bookDetails).then(function (result) { </span> $scope<span>.bookDetails = {}; </span> $scope<span>.bookForm.$setPristine(); </span> <span>}); </span> <span>}; </span> $scope<span>.numberPattern = <span>/<span>^\d*$</span>/</span>; </span> <span>}]);</span>
Ujian unit menjadi rumit pada masa -masa, tetapi ia bernilai menghabiskan masa di atasnya kerana ia memastikan ketepatan permohonan. AngularJS menjadikannya lebih mudah untuk menguji unit kod yang ditulis menggunakan rangka kerja. Saya harap artikel ini memberi anda idea yang cukup untuk mengembangkan dan meningkatkan ujian dalam aplikasi anda. Dalam artikel masa depan, kami akan terus melihat cara menguji kepingan kod anda yang lain.
ujian unit adalah aspek penting dalam perkembangan AngularJS. Ia membantu mengesahkan fungsi komponen individu, seperti perkhidmatan, pengawal, dan penyedia, secara berasingan. Ini memastikan setiap komponen berfungsi seperti yang diharapkan sebelum ia diintegrasikan ke dalam aplikasi yang lebih besar. Ujian unit boleh membantu mengenal pasti pepijat awal dalam proses pembangunan, menjadikannya lebih mudah dan kurang mahal untuk diperbaiki. Ia juga membantu mengekalkan kualiti kod dan meningkatkan kebolehpercayaan keseluruhan aplikasi.
Atas ialah kandungan terperinci Ujian Unit di AngularJS: Perkhidmatan, Pengawal & Penyedia. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!