บทช่วยสอนแอป AngularJS แรกของคุณ ตอนที่ 2: เครื่องมือสำหรับนั่งร้าน การสร้าง และการทดสอบ

เผยแพร่แล้ว: 2022-03-11

บทนำ

ด้วยเครื่องมือมากมายที่สามารถช่วยในการพัฒนาแอปพลิเคชัน AngularJS หลายคนรู้สึกว่ามันเป็นเฟรมเวิร์กที่ซับซ้อนอย่างยิ่ง ซึ่งไม่ได้เป็นเช่นนั้นเลย นั่นเป็นหนึ่งในเหตุผลหลักที่ฉันเริ่มชุดบทช่วยสอนนี้

ในส่วนที่หนึ่ง เราได้กล่าวถึงพื้นฐานของกรอบงาน AngularJS และเริ่มต้นด้วยการเขียนแอปพลิเคชันแรกของเรา โพสต์นี้ออกแบบมาสำหรับผู้เริ่มต้น หากคุณเป็นนักพัฒนา AngularJS ที่มีประสบการณ์มากกว่า คุณอาจสนใจที่จะทำความเข้าใจคำสั่งหรือเรื่องราวของ AngularJS ที่ใช้งานเมื่อเริ่มต้นที่กำลังเติบโต

ในบทช่วยสอนนี้ เราจะแยกเลเยอร์ลอจิกของแอปพลิเคชันและเรียนรู้วิธีดำเนินการตั้งค่าโปรเจ็กต์ AngularJS ที่เหมาะสม ซึ่งรวมถึงการสร้างนั่งร้าน การจัดการการพึ่งพา และการเตรียมการทดสอบ (ทั้งยูนิตและแบบ end-to-end) เราจะทำสิ่งนี้โดยใช้เครื่องมือ AngularJS เหล่านี้: Yeoman, Grunt และ Bower จากนั้นเราจะทบทวนกระบวนการเขียนและทดสอบจัสมินโดยใช้กรรม

Karma, Jasmine, Grunt, Bower, Yeoman… เครื่องมือทั้งหมดนี้คืออะไร?

มีเครื่องมือในการพัฒนามากมายที่ช่วยในการนั่งร้าน AngularJS ทดสอบ AngularJS และสร้างแอปอย่างเหมาะสม

หากคุณทำงานกับ JavaScript มีความเป็นไปได้สูงที่คุณรู้จักเครื่องมือเหล่านี้อย่างน้อยบางส่วน แม้ว่าคุณจะยังใหม่กับ Angular ก็ตาม แต่เพื่อช่วยให้แน่ใจว่ามีพื้นฐานร่วมกัน ฉันจะหลีกเลี่ยงการตั้งสมมติฐานใดๆ มาทบทวนโดยย่อแต่ละเทคโนโลยีเหล่านี้และมีประโยชน์สำหรับ:

  • Karma (ก่อนหน้านี้รู้จักกันในชื่อ Testacular) เป็นตัวทดสอบ JavaScript ของ Google และเป็นตัวเลือกที่เป็นธรรมชาติสำหรับการทดสอบ AngularJS นอกเหนือจากการอนุญาตให้คุณทำการทดสอบบนเบราว์เซอร์จริง (รวมถึงเบราว์เซอร์บนโทรศัพท์/แท็บเล็ต) มันยังเป็น เฟรมเวิร์กการทดสอบที่ไม่เชื่อเรื่องพระเจ้า ซึ่งหมายความว่าคุณสามารถใช้ร่วมกับกรอบ การ ทดสอบที่คุณเลือกได้ (เช่น จัสมิน มอคค่า หรือ QUnit เป็นต้น)

  • จัสมินจะเป็นกรอบการทดสอบที่เราเลือก อย่างน้อยสำหรับโพสต์นี้ ไวยากรณ์ของมันค่อนข้างคล้ายกับ RSpec หากคุณเคยใช้งานมาก่อน (หากคุณยังไม่ได้ทำ ไม่ต้องกังวล เราจะตรวจสอบรายละเอียดเพิ่มเติมในภายหลังในบทช่วยสอนนี้)

  • Grunt เป็น task runner ที่ช่วยให้ทำงานซ้ำๆ ได้โดยอัตโนมัติ เช่น การลดขนาด คอมไพล์ (หรือบิลด์) การทดสอบ และการตั้งค่าการแสดงตัวอย่างแอปพลิเคชัน AngularJS ของคุณ

  • Bower เป็นโปรแกรมจัดการแพ็คเกจที่ช่วยคุณค้นหาและติดตั้งการพึ่งพาแอปพลิเคชันทั้งหมดของคุณ เช่น เฟรมเวิร์ก CSS, ไลบรารี JavaScript และอื่นๆ มันทำงานบน git เหมือนกับ Rails Bundler และหลีกเลี่ยงความจำเป็นในการดาวน์โหลดและอัปเดตการพึ่งพาด้วยตนเอง

  • Yeoman เป็นชุดเครื่องมือที่มีส่วนประกอบหลัก 3 ส่วน ได้แก่ Grunt, Bower และเครื่องมือนั่งร้าน Yo Yo สร้างรหัสต้นแบบด้วยความช่วยเหลือของเครื่องกำเนิดไฟฟ้า (ซึ่งเป็นเพียงแม่แบบนั่งร้าน) และกำหนดค่า Grunt และ Bower สำหรับโครงการของคุณโดยอัตโนมัติ คุณสามารถหาตัวสร้างสำหรับเฟรมเวิร์ก JavaScript เกือบทุกแบบ (Angular, Backbone, Ember เป็นต้น) แต่เนื่องจากเรามุ่งเน้นที่ Angular เราจะใช้โปรเจ็กต์ตัวสร้างเชิงมุม

แล้วเราจะเริ่มต้นที่ไหน?

สิ่งแรกที่เราต้องทำคือติดตั้งเครื่องมือที่เราต้องการ

หากคุณยังไม่ได้ติดตั้ง git, node.js และ npm ให้ดำเนินการติดตั้ง

จากนั้นเราจะไปที่บรรทัดคำสั่งและเรียกใช้คำสั่งต่อไปนี้เพื่อติดตั้งเครื่องมือของ Yeoman:

 npm install -g yo grunt-cli bower

และอย่าลืมว่าเราจะใช้เครื่องมือสร้าง AngularJS ดังนั้นคุณจะต้องติดตั้งด้วย:

 npm install -g generator-angular

ตกลง ตอนนี้เราพร้อมที่จะ...

นั่งร้าน/สร้างแอปพลิเคชัน AngularJS ของเรา

ครั้งที่แล้ว เรายืมรหัสสำเร็จรูปจากโครงการเมล็ดเชิงมุมด้วยตนเอง คราวนี้ เราจะให้โย (ร่วมกับเครื่องกำเนิดเชิงมุม) ทำแทนเรา

สิ่งที่เราต้องทำคือสร้างโฟลเดอร์โปรเจ็กต์ใหม่ ไปที่โฟลเดอร์นั้นแล้วเรียกใช้:

 yo angular

เราจะนำเสนอตัวเลือกบางอย่าง เช่น จะรวม Bootstrap และ Compass หรือไม่ ในตอนนี้ สมมุติว่า ไม่ ใช้กับ Compass และ ใช่ กับ Bootstrap จากนั้น เมื่อได้รับแจ้งเกี่ยวกับโมดูลที่จะรวม (ทรัพยากร คุกกี้ ฆ่าเชื้อ และเส้นทาง) เราจะเลือกเฉพาะ angular-route.js

ตอนนี้ควรสร้างโครงนั่งร้านโครงการของเรา (อาจใช้เวลาสักครู่) รวมเข้ากับ Karma และกำหนดค่าไว้ล่วงหน้าทั้งหมด

หมายเหตุ: โปรดทราบ ว่าเรากำลังจำกัดโมดูลที่นี่เฉพาะโมดูลที่เราใช้ในแอปพลิเคชันที่เราสร้างขึ้นในส่วนที่หนึ่งของบทช่วยสอนนี้ เมื่อคุณทำสิ่งนี้สำหรับโครงการของคุณเอง คุณจะต้องกำหนดว่าจะต้องรวมโมดูลใดบ้าง

ตอนนี้ เนื่องจากเราจะใช้จัสมิน มาเพิ่มอะแดปเตอร์ karma-jasmine ในโครงการของเรากัน:

 npm install karma-jasmine --save-dev

ในกรณีที่เราต้องการทำการทดสอบบนอินสแตนซ์ของ Chrome ให้เพิ่ม karma-chrome-launcher :

 npm install karma-chrome-launcher --save-dev

ตกลง ถ้าเราทำทุกอย่างถูกต้อง โครงสร้างไฟล์โครงการของเราตอนนี้ควรมีลักษณะดังนี้:

แผนผังไฟล์โครงการตัวอย่างโดยใช้เครื่องมือ AngularJS เหล่านี้จะมีลักษณะดังนี้

รหัสแอปพลิเคชันแบบคงที่ของเราไปที่ไดเรกทอรี app/ และไดเรกทอรี test/ จะมี (ใช่ คุณเดาได้!) การทดสอบของเรา ไฟล์ที่เราเห็นบนรูทคือไฟล์กำหนดค่าโครงการของเรา มีหลายสิ่งที่ต้องเรียนรู้เกี่ยวกับแต่ละรายการ แต่สำหรับตอนนี้ เราจะใช้การกำหนดค่าเริ่มต้นเท่านั้น มาเริ่มใช้งานแอพของเรากันเป็นครั้งแรก ซึ่งเราทำได้ง่ายๆ ด้วยคำสั่งต่อไปนี้:

 grunt serve

แล้วโว้ย! แอพของเราควรปรากฏขึ้นต่อหน้าเรา!

เล็กน้อยเกี่ยวกับ Bower สำหรับ AngularJS

ก่อนที่จะเข้าสู่ส่วนที่สำคัญจริงๆ (เช่น การทดสอบ) เรามาใช้เวลาสักครู่เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับ Bower ดังที่ได้กล่าวไว้ก่อนหน้านี้ Bower เป็นผู้จัดการแพ็คเกจของเรา การเพิ่ม lib หรือปลั๊กอินในโครงการของเราสามารถทำได้โดยใช้คำสั่ง bower install ตัวอย่างเช่น หากต้องการรวม modernizr สิ่งที่เราต้องทำมีดังต่อไปนี้ (ในไดเร็กทอรีโครงการของเราแน่นอน):

 bower install modernizr

โปรดทราบว่าแม้ว่าสิ่งนี้จะทำให้ modernizr เป็นส่วนหนึ่งของโครงการของเรา (จะอยู่ในไดเรกทอรี app/bower_components ) เรายังคงรับผิดชอบในการรวมไว้ในแอปพลิเคชันของเรา (หรือจัดการเมื่อควรรวมไว้) ตามที่เราต้องการ จะทำอย่างไรกับ lib ที่เพิ่มด้วยตนเอง วิธีหนึ่งในการทำเช่นนี้คือเพียงเพิ่มแท็ก <script> ต่อไปนี้ใน index.html ของเรา:

 <script src="bower_components/modernizr/modernizr.js"></script>

อีกทางหนึ่ง เราสามารถใช้ไฟล์ bower.json เพื่อจัดการการขึ้นต่อกันของเรา หลังจากปฏิบัติตามทุกขั้นตอนจนถึงตอนนี้ ไฟล์ bower.json ควรมีลักษณะดังนี้:

 { "name": "F1FeederApp", "version": "0.0.0", "dependencies": { "angular": "1.2.15", "json3": "~3.2.6", "es5-shim": "~2.1.0", "jquery": "~1.11.0", "bootstrap": "~3.0.3", "angular-route": "1.2.15" }, "devDependencies": { "angular-mocks": "1.2.15", "angular-scenario": "1.2.15" } }

ไวยากรณ์ค่อนข้างอธิบายตนเองได้ แต่มีข้อมูลเพิ่มเติมที่นี่

จากนั้นเราสามารถเพิ่มการพึ่งพาใหม่เพิ่มเติมที่เราต้องการ จากนั้นสิ่งที่เราต้องมีคือคำสั่งต่อไปนี้เพื่อติดตั้ง:

 bower install

มาเขียนแบบทดสอบกัน!

ตกลง ตอนนี้ได้เวลาเริ่มต้นจากจุดที่เราค้างไว้ในตอนที่หนึ่งแล้วเขียนการทดสอบสำหรับแอป AngularJS ของเรา

แต่ก่อนอื่น มีปัญหาเล็กน้อยที่เราต้องแก้ไข: แม้ว่านักพัฒนาของ generator-angular จะใช้เทมเพลตโปรเจ็กต์ของพวกเขาบนโปรเจ็กต์ angular-seed (ซึ่งเป็นเทมเพลตเชิงมุมอย่างเป็นทางการ) ด้วยเหตุผลบางอย่างที่ฉันไม่เข้าใจจริงๆ พวกเขาตัดสินใจ เพื่อเปลี่ยนหลักการตั้งชื่อโฟลเดอร์ของ app (เปลี่ยน css เป็น styles , js เป็น scripts เป็นต้น)

ด้วยเหตุนี้ แอปที่เราเขียนแต่แรกจึงมีเส้นทางที่ไม่สอดคล้องกับโครงที่เราเพิ่งสร้างขึ้น เพื่อแก้ปัญหานี้ ให้ดาวน์โหลดโค้ดของแอปจากที่นี่และทำงานกับเวอร์ชันนั้นจากจุดนี้เป็นต้นไป (ส่วนใหญ่เป็นแอปเดียวกันกับที่เราเขียนในตอนแรก แต่มีเส้นทางที่อัปเดตเพื่อให้ตรงกับการตั้งชื่อเชิงมุมของตัวสร้าง)

หลังจากดาวน์โหลดแอป ให้ไปที่โฟลเดอร์ tests/spec/controllers และสร้างไฟล์ชื่อ drivers.js ซึ่งประกอบด้วยรายการต่อไปนี้:

 describe('Controller: driversController', function () { // First, we load the app's module beforeEach(module('F1FeederApp')); // Then we create some variables we're going to use var driversController, scope; beforeEach(inject(function ($controller, $rootScope, $httpBackend) { // Here, we create a mock scope variable, to replace the actual $scope variable // the controller would take as parameter scope = $rootScope.$new(); // Then we create an $httpBackend instance. I'll talk about it below. httpMock = $httpBackend; // Here, we set the httpBackend standard reponse to the URL the controller is // supposed to retrieve from the API httpMock.expectJSONP( "http://ergast.com/api/f1/2013/driverStandings.json?callback=JSON_CALLBACK").respond( {"MRData": {"StandingsTable": {"StandingsLists" : [{"DriverStandings":[ { "Driver": { "givenName": 'Sebastian', "familyName": 'Vettel' }, "points": "397", "nationality": "German", "Constructors": [ {"name": "Red Bull"} ] }, { "Driver": { "givenName": 'Fernando', "familyName": 'Alonso' }, "points": "242", "nationality": "Spanish", "Constructors": [ {"name": "Ferrari"} ] }, { "Driver": { "givenName": 'Mark', "familyName": 'Webber' }, "points": "199", "nationality": "Australian", "Constructors": [ {"name": "Red Bull"} ] } ]}]}}} ); // Here, we actually initialize our controller, passing our new mock scope as parameter driversController = $controller('driversController', { $scope: scope }); // Then we flush the httpBackend to resolve the fake http call httpMock.flush(); })); // Now, for the actual test, let's check if the driversList is actually retrieving // the mock driver array it('should return a list with three drivers', function () { expect(scope.driversList.length).toBe(3); }); // Let's also make a second test checking if the drivers attributes match against // the expected values it('should retrieve the family names of the drivers', function () { expect(scope.driversList[0].Driver.familyName).toBe("Vettel"); expect(scope.driversList[1].Driver.familyName).toBe("Alonso"); expect(scope.driversList[2].Driver.familyName).toBe("Webber"); }); });

นี่คือชุดทดสอบสำหรับ driverscontroller ของเรา อาจดูเหมือนโค้ดจำนวนมาก แต่จริงๆ แล้วส่วนใหญ่เป็นเพียงการประกาศข้อมูลจำลอง มาดูองค์ประกอบที่สำคัญจริงๆ กัน:

  • วิธี describe() กำหนดชุดทดสอบของเรา
  • it() แต่ละตัวเป็นข้อกำหนดการทดสอบที่เหมาะสม
  • ทุก beforeEach() จะถูกดำเนินการก่อนการทดสอบแต่ละครั้ง

องค์ประกอบที่สำคัญที่สุด (และอาจทำให้สับสน) ที่นี่คือบริการ $httpBackend ที่เราสร้างอินสแตนซ์บนตัวแปร httpMock บริการนี้ทำหน้าที่เป็นแบ็กเอนด์ปลอมและตอบสนองต่อการเรียก API ของเราในการทดสอบการทำงาน เช่นเดียวกับเซิร์ฟเวอร์จริงของเราในการผลิต ในกรณีนี้ โดยใช้ expectJSONP() เราตั้งค่าให้ขัดขวางคำขอ JSONP ใด ๆ ที่ส่งไปยัง URL ที่กำหนด (อันเดียวกับที่เราใช้เพื่อรับข้อมูลจากเซิร์ฟเวอร์) และให้ส่งคืนรายการคงที่พร้อมไดรเวอร์สามตัว เลียนแบบ การตอบสนองของเซิร์ฟเวอร์จริง สิ่งนี้ทำให้เราสามารถทราบได้อย่างแน่นอนว่าอะไรควรจะกลับมาจากคอนโทรลเลอร์ ดังนั้นเราจึงสามารถเปรียบเทียบผลลัพธ์กับสิ่งที่คาดหวังได้โดยใช้ฟังก์ชัน expect() หากตรงกันการทดสอบจะผ่าน

การรันการทดสอบทำได้ง่ายดายด้วยคำสั่ง:

 grunt test

ชุดทดสอบสำหรับตัวควบคุมรายละเอียดไดรเวอร์ ( drivercontroller ) ควรจะค่อนข้างคล้ายกับที่เราเพิ่งเห็น ฉันแนะนำให้คุณลองคิดดูเอาเองว่าเป็นแบบฝึกหัด

แล้วการทดสอบ AngularJS แบบ end-to-end ล่ะ?

ทีม Angular เพิ่งเปิดตัวนักวิ่งใหม่สำหรับการทดสอบแบบ end-to-end ที่เรียกว่า Protractor ใช้ webdriver เพื่อโต้ตอบกับแอปพลิเคชันที่ทำงานในเบราว์เซอร์และยังใช้เฟรมเวิร์กการทดสอบจัสมินตามค่าเริ่มต้น ดังนั้นไวยากรณ์จะมีความสอดคล้องอย่างมากกับการทดสอบหน่วยของเรา

เนื่องจาก Protractor เป็นเครื่องมือที่ค่อนข้างใหม่ แม้ว่าการรวมเข้ากับ Yeoman stack และ generator-angular ยังคงต้องการงานกำหนดค่าในปริมาณที่พอเหมาะ ด้วยเหตุนี้ และความตั้งใจของฉันที่จะทำให้บทช่วยสอนนี้เรียบง่ายที่สุดเท่าที่จะเป็นไปได้ แผนของฉันคือการอุทิศโพสต์ในอนาคตโดยเฉพาะเพื่อครอบคลุมการทดสอบแบบ end-to-end ใน AngularJS ในเชิงลึกเท่านั้น

บทสรุป

ณ จุดนี้ในชุดบทช่วยสอน เราได้เรียนรู้วิธีนั่งร้านแอป Angular ด้วย yo จัดการการพึ่งพาด้วย bower และเขียน/เรียกใช้การทดสอบโดยใช้ karma และ protractor โปรดจำไว้ว่า บทช่วยสอนนี้มีขึ้นเพื่อเป็นการแนะนำเครื่องมือและแนวทางปฏิบัติของ AngularJS เท่านั้น เราไม่ได้วิเคราะห์ใด ๆ ของพวกเขาที่นี่ในเชิงลึกมาก

เป้าหมายของเราคือช่วยให้คุณเริ่มต้นเส้นทางนี้ จากนี้ไป ขึ้นอยู่กับคุณแล้วที่จะเรียนรู้ทุกอย่างเกี่ยวกับเฟรมเวิร์กและชุดเครื่องมือที่น่าทึ่งนี้

ภาคผนวก: หมายเหตุ (สำคัญ) บางส่วนจากผู้เขียน

หลังจากอ่านบทช่วยสอนนี้แล้ว บางคนอาจถามว่า “เดี๋ยวก่อน คุณไม่ควรทำสิ่งเหล่านี้ทั้งหมดก่อนที่จะเริ่มเขียนโค้ดแอปของคุณจริงๆ หรือ นี่ควรเป็นส่วนหนึ่งของบทช่วยสอนนี้ไม่ใช่หรือ?”

คำตอบสั้น ๆ ของฉันคือ ไม่ ตามที่เราเห็นในตอนแรก คุณไม่จำเป็นต้องรู้ข้อมูลทั้งหมดนี้เพื่อเขียนโค้ดแอป Angular แรกของคุณ เครื่องมือส่วนใหญ่ที่เราได้พูดคุยกันในโพสต์นี้ได้รับการออกแบบมาเพื่อช่วยคุณเพิ่มประสิทธิภาพเวิร์กโฟลว์การพัฒนาและฝึกฝนการพัฒนาที่ขับเคลื่อนด้วยการทดสอบ (TDD)

และเมื่อพูดถึง TDD แนวคิดพื้นฐานที่สุดของ TDD ก็คือแนวคิดที่ถูกต้อง กล่าวคือ เขียนการทดสอบของคุณก่อนที่จะเขียนโค้ดของคุณ แม้ว่าบางคนใช้แนวความคิดนั้นมากเกินไป TDD เป็นวิธีการพัฒนา ไม่ใช่ วิธีการเรียนรู้ ดังนั้น การเขียน การทดสอบของคุณก่อนที่จะเขียนโค้ดจึงเป็นเรื่องที่สมเหตุสมผล ในขณะที่ การเรียนรู้วิธี เขียนการทดสอบของคุณก่อนที่จะเรียนรู้วิธีเขียนโค้ดไม่สมเหตุสมผล

โดยส่วนตัวแล้วฉันคิดว่านี่เป็นเหตุผลหลักว่าทำไมบทช่วยสอนเชิงมุมอย่างเป็นทางการจึงอาจรู้สึกซับซ้อนและแทบจะเป็นไปไม่ได้เลยที่จะติดตามสำหรับผู้ที่ไม่มีประสบการณ์ MVC/TDD ส่วนหน้ามาก่อน นั่นเป็นหนึ่งในเหตุผลหลักที่ทำให้ฉันเริ่มชุดบทช่วยสอนนี้

คำแนะนำส่วนตัวของฉันสำหรับผู้ที่เรียนรู้ที่จะสำรวจโลกของ AngularJS คือ อย่ากดดันตัวเองมากเกินไป คุณไม่จำเป็นต้องเรียนรู้ทุกอย่างพร้อมกัน (ทั้งๆ ที่คนอื่นบอกคุณเป็นอย่างอื่น!) ขึ้นอยู่กับประสบการณ์ก่อนหน้าของคุณกับเฟรมเวิร์กส่วนหน้า/การทดสอบอื่นๆ AngularJS อาจเข้าใจยากในตอนแรก ดังนั้นจงเรียนรู้สิ่งที่คุณต้องการเรียนรู้ทั้งหมดจนกว่าคุณจะสามารถเขียนแอปง่ายๆ ของคุณเองได้ และเมื่อคุณคุ้นเคยกับพื้นฐานของเฟรมเวิร์กแล้ว คุณก็ไม่ต้องกังวลใจในการเลือกและนำแนวทางปฏิบัติในการพัฒนาระยะยาวมาใช้ได้ดีที่สุด คุณ.

แน่นอน นั่นเป็นความเห็นที่ต่ำต้อยของฉัน และไม่ใช่ทุกคนที่จะเห็นด้วยกับแนวทางนั้น (และทีมพัฒนาของ Angular อาจส่งนักฆ่ารับจ้างตามฉันไปเมื่อฉันเผยแพร่สิ่งนี้) แต่นั่นเป็นวิสัยทัศน์ของฉัน และฉันค่อนข้างแน่ใจว่าคนจำนวนมาก ออกไปที่นั่นใครจะเห็นด้วยกับฉัน

ที่เกี่ยวข้อง: บทช่วยสอน Angular 6: คุณสมบัติใหม่พร้อมพลังใหม่