จากบทความที่ผ่านมา เราได้เรียนรู้การเขียน unit test directive เบื้องต้นไปแล้ว directive ที่เราสร้างมักจะมี template อยู่ด้วย เป็น HTML ต่างๆ เพื่อแสดงเป็น UI ของ directive
ถ้า template ของเราเป็น html ไม่เยอะมาก การจับ template เข้าไปรวมอยู่กับ directive file ก็คงไม่มีปัญหา อย่างเช่นที่เราทำกันในตัวอย่างทีาผ่านมา
แต่ชีวิตจริง เราอาจจะมี directive ที่ยาวกว่านี้ เช่นใน codesanook ผมก็สร้างพวกปุ่ม editor เป็น markdown html template จึงค่อนข้างยาวจะให้ใส่เข้าไปกับ directive คงไม่สะดวก แยกออกมาเป็น file จะดีกว่า
ดังนั้นให้เรามันจะสร้าง directive ด้วยการกำหนดค่า templateUrl เพื่อแยก template ออกมาอีก file
โดยเราเราจะเรียนเรียนรู้กันดังนี้
สร้าง file emailSubscriptionDirectiveExternalTemplate.js โดย copy จาก emailSubscriptionDirective.js วางไว้ใน folder src แล้วแก้ไขคำสั่งเป็นดังนี้
var app = angular.module('emailSubscriptionExternalTemplateTestApp', []);
app.directive('emailSubscriptionExternalTemplate', function () {
var emailSuscriptionDirectiveExternalTemplate = {
restrict: 'E',
scope: {
email: "="
},
controller: function ($scope) {
$scope.hasSubscribed = false;
$scope.subscribe = function () {
$scope.hasSubscribed = true;
console.log("Thank you for subscribing, we will send email to " + $scope.email);
};
},
templateUrl: 'emailSubscriptionTemplate.html'
};
return emailSuscriptionDirectiveExternalTemplate;
});
อธิบาย
src/emailSubscriptionTemplate.html
<div>
<input id="txtEmail" type="text" ng-model="email"/>
</div>
<div>
<input id="btnSubscribe" type="button" ng-click="subscribe()"/>
</div>
สร้าง test script file ใหม่โดย copy จาก emailSubscriptionDirectiveSpec.js และตั้งชื่อว่า emailSubscriptionDirectiveExternalTemplateSpec.js เก็บไว้ใน folder spec แล้วแก้ไข file เป็นดังนี้
spec/emailSubscriptionDirectiveExternalTemplateSpec.js
describe("emailSubscriptionDirectiveExternalTemplate", function () {
it('input elements should be defined', function () {
var el = null;
var scope = null;
angular.mock.module('emailSubscriptionExternalTemplateTestApp');
angular.mock.inject(function ($compile, $rootScope) {
$rootScope.email = null;
var htmlElement = angular.element(
'<email-subscription-external-template email="email"></email-subscription>');
el = $compile(htmlElement)($rootScope);
$rootScope.$digest();
scope = el.isolateScope();
});
var emailTextInput = el.find("#txtEmail");
expect(emailTextInput).toBeDefined();
expect(emailTextInput.attr('id')).toBe("txtEmail");
var button = el.find("#btnSubscribe");
expect(button).toBeDefined();
});
});
อธิบายคำสั่ง
เราจะมาทดสอบการทำงานของ directive กัน ว่าสามารถ load element ต่างๆ ที่อยู่ใน external template ได้ไหม โดยเข้าไปที่ root folder ของ project เปิด Windows command line หรือ Linux terminal และพิมพ์คำสั่งต่อไปนี้
npm test
ผลลัพธ์ที่ได้
error แสดงออกมาเป็น Error: Unexpected request: GET emailSubscriptionTemplate.html หมายความว่า directive พยายามที่จะโหลด emailSubscriptionTemplate.html หากถ้าคำสั่งนี้ อยู่ใน production code ไม่ได้อยู่ใน unit test แบบนี้
การทดสอบโดยเปิด browser ปกติ Angular ก็จะโหลด template ได้ถูกต้อง แต่พออยู่ใน unit test เราไม่ทดสอบกับ external service เช่น http request, database access กันครับ เพราะทำให้ unit test ช้า และยากที่จะ clean up data ให้อยู่ใน state ที่เราต้องการ เราต้อง isolate พวกนี้ ออกไปโดยทำเป็น mock object แทน
ปัญหาของเราตอนนี้คือ directive โหลด external template ไม่ได้ ดังนั้น เราจะทำอย่างไรดีน้ออ
ก่อนอื่นเลย ให้เรามาทำความเข้าใจลักษณ์การทำงานของ exteranal template ก่อนครับ
ให้เราติดตั้ง karma plugin ที่ชื่อว่า karma-ng-html2js-preprocessor
npm install karma-ng-html2js-preprocessor --save-dev
จากนั้นให้เข้าไปแก้ไข karma.config.js เป็นดังนี้ครับ
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
files: [
'lib/jquery-2.2.0.min.js',
'lib/angular-1.5.0/angular.min.js',
'lib/angular-1.5.0/angular-mocks.js',
'src/**/*.js',
'spec/**/*.js',
'src/**/*.html'
],
exclude: [],
preprocessors: {
'src/**/*.html': ['ng-html2js']
},
ngHtml2JsPreprocessor: {
cacheIdFromPath: function (filePath) {
console.log("filePath " + filePath);
var cacheId = filePath.replace("src/", "");
console.log("cacheId " + cacheId);
return cacheId;
},
moduleName: 'ngTemplates'
},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['PhantomJS'],
singleRun: false,
concurrency: Infinity
})
};
เข้าไปแก้ไข file emailSubscriptionDirectiveExternalTemplate โดยเพิ่มโหลด ngTemplates module เข้าไป
angular.mock.module('emailSubscriptionExternalTemplateTestApp','ngTemplates');
หลังจากกำหนดค่าต่างๆ ของ karma.config.js แล้ว เรามาลองทดสอบการทำงานอีกครั้ง
npm test
ผลลัพธ์การทำงานถูกต้อง
สังเกตว่าเราได้ log cacheId มาด้วย
ใครมีความเห็น คำแนะนำใดๆ เขียนเข้ามาได้เลยครับ ขอบคุณครับ
codesanook และไม่เครียดกับการ code นะครับ
เมื่อโหลดไปแล้วใช้คำสั่ง npm install package ต่างๆ ก็จะโหลดให้โดยอัตโนมัติครับ