我們在上一步中為開發(fā)應(yīng)用打基礎(chǔ)做了很多工作,現(xiàn)在我們將做一些簡單的事情;我們將添加全文搜索(是的,它很簡單?。N覀冞€將編寫一個端到端測試,因為一個好的端到端測試可以幫上大忙。它監(jiān)視著你的應(yīng)用,并在發(fā)生回歸時迅速報告。
把工作空間重置到第三步
git checkout -f step-3
刷新你的瀏覽器或在線檢查這一步:Step 3 Live Demo
下面列出了第二步和第三步之間最重要的區(qū)別。你可以在GitHub里看到完整的差異。
我們對控制器不作修改。
app/index.html:
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<!--Sidebar content-->
Search: <input ng-model="query">
</div>
<div class="col-md-10">
<!--Body content-->
<ul class="phones">
<li ng-repeat="phone in phones | filter:query">
{{phone.name}}
<p>{{phone.snippet}}</p>
</li>
</ul>
</div>
</div>
</div>
我們添加了一個標(biāo)準(zhǔn)HTML<input>
元素標(biāo)記,并使用Angular的filter函數(shù)來處理repeat指令的輸入。
這使用戶輸入搜索條件,并在手機列表中快速看到搜索結(jié)果。新的代碼演示如下:
數(shù)據(jù)綁定:這是Angular的一個核心功能。當(dāng)網(wǎng)頁載入時,Angular把輸入框的名稱綁定到數(shù)據(jù)模塊的同名的變量上,并保持兩者同步。
在代碼中,用戶打字到輸入框的數(shù)據(jù)(命名為query
)很快可以作為一個篩選器輸入到列表迭代器(phone in phones | filter:
query
)中。在改變數(shù)據(jù)模塊的時候,導(dǎo)致迭代器的輸入發(fā)生變化,迭代器有效地更新了DOM,以反映模塊的當(dāng)前狀態(tài)。
使用filter
篩選器:filter函數(shù)使用了query
值發(fā)創(chuàng)建一個新的數(shù)列,只包含匹配query
的記錄。
ngRepeat
自動更新了視力,以響應(yīng)filter
篩選器返回的手機數(shù)字的變化。該處理對開發(fā)者來說是完全透明的。
在第二步中,我們學(xué)會了如何編寫并運行單元測試。對于測試我們的用JavaScript編寫的應(yīng)用程序的控制器和其它組件,單元測試是完美的,但是測試DOM操作或測試我們的應(yīng)用程序的接通不太方便。針對這些,一個端到端的測試是一個更好的選擇。
該搜索功能完全是通過模板和數(shù)據(jù)綁定來實現(xiàn)的,我們將編寫我們第一個端到端的測試,以驗證該功能起了什么作用。
test/e2e/scenarios.js
:
describe('PhoneCat App', function() {
describe('Phone list view', function() {
beforeEach(function() {
browser.get('app/index.html');
});
it('should filter the phone list as a user types into the search box', function() {
var phoneList = element.all(by.repeater('phone in phones'));
var query = element(by.model('query'));
expect(phoneList.count()).toBe(3);
query.sendKeys('nexus');
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
expect(phoneList.count()).toBe(2);
});
});
});
這個測試驗證了搜索框以及迭代器是否正確地接通了。注意,在Angular中,編寫端到端測試是如此地容易。雖然這個示例只針對一個簡單的測試,但是它確實很容易測試任何功能化的、可讀的、端到端的測試。
甚至雖然測試的句法看起來很像我們的用Jasmine編寫的控制器單元測試,但是端到端測試使用Protractor的API。在http://angular.github.io/protractor/#/api可以讀到Protractor的API。
與Karma很像的是針對單元測試的測試運行者,我們使用Protractor以運行端到端測試。用npm run protractor
來嘗試它。端到端測試很慢,所以與單元測試不同,在運行測試之后Protractor將退出,不會自動在每次文件更改時重新運行測試套裝。要想重新運行測試套裝,需要再次執(zhí)行npm run protractor
。
通過添加一個綁定到index.html
模板的{{query}}
來顯示query
模塊當(dāng)前的值,并看到當(dāng)你在輸入框中打字時,它如何變化。
讓我們看到我們可以取得query
模板的當(dāng)前值,模塊出現(xiàn)在HTML網(wǎng)頁的標(biāo)題上。
把一個端到端測試添加到describe
塊中,test/e2e/scenarios.js
看起來將如這:
describe('PhoneCat App', function() {
describe('Phone list view', function() {
beforeEach(function() {
browser.get('app/index.html');
});
var phoneList = element.all(by.repeater('phone in phones'));
var query = element(by.model('query'));
it('should filter the phone list as a user types into the search box', function() {
expect(phoneList.count()).toBe(3);
query.sendKeys('nexus');
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
expect(phoneList.count()).toBe(2);
});
it('should display the current filter value in the title bar', function() {
query.clear();
expect(browser.getTitle()).toMatch(/Google Phone Gallery:\s*$/);
query.sendKeys('nexus');
expect(browser.getTitle()).toMatch(/Google Phone Gallery: nexus$/);
});
});
});
運行protractor(npm run protractor
),看到測試失敗了。
你可能認(rèn)為你只需要用以下方式向標(biāo)題標(biāo)簽添加{{query}}
:
<title>Google Phone Gallery: {{query}}</title>
然而,當(dāng)你重載入這個網(wǎng)頁的時候,你不會看到想要的結(jié)果。這是因為“查詢”模塊駐留在作用域內(nèi),由ng-controller="PhoneListCtrl"
指令在body元素上定義。
<body ng-controller="PhoneListCtrl">
如果你想要從<title>
元素上綁定查詢模塊,你必須把ngController
聲明移動到HTML元素上,因為它是body元素和title元素常用的父元素。
<html ng-app="phonecatApp" ng-controller="PhoneListCtrl">
確保從body元素中移除ng-controller
聲明。
重新運行rpm run protractor
,看到現(xiàn)在測試已經(jīng)看通過了。
在title元素內(nèi)部使用雙花工作得很好,與此同時,你可能會注意到頁面加載的一瞬間它們確實顯示給用戶了。一個更好的解決方案是使用ngBind指令或ngBindTemplate指令,當(dāng)頁面加載時用戶能看到它們。
<title ng-bind-template="Google Phone Gallery: {{query}}">Google Phone Gallery</title>
我們現(xiàn)在已經(jīng)把全文搜索添加上去了,還包含了一個用來驗證搜索是否起作用的測試!現(xiàn)在讓我們前往第四步 雙路數(shù)據(jù)綁定以學(xué)會如何向手機應(yīng)用添加排序功能。
更多建議: