在前后臺分離開發中,我們在進行前臺開發時,后臺的工作正在進行中,或是還沒有進行。這就要求我們在合作開發中,做下以下兩點:
1. 100%的模擬后臺的數據返回。
2. 當后臺開發完畢后,前臺與后臺能夠無縫對接。
本節中,我們再次對M層進行重構,以達到上述目的。
# 重構M層
我們首先,將業務代碼實現進行剝離,這樣開發起來,架構更清晰。
```
'use strict';
/**
* @ngdoc service
* @name webAppApp.teacher
* @description
* # teacher
* Service in the webAppApp.
*/
angular.module('webAppApp')
.service('teacher', function() {
// 獲取所有教師
var all = function(callback) {
var teachers = [
{ name: '張三', username: 'zhangsan', sex: 0, email: 'zhangsan@yunzhiclub.com' },
{ name: '李四', username: 'lisi', sex: 1, email: 'lisi@yunzhiclub.com' }
];
callback(teachers);
};
// Public API here
return {
// 獲取考場編排信息
all: function(callback) {
all(callback);
},
};
});
```
然后,我們將json數據單獨的建立一個資源文件,并模擬請求這個文件。
app/resource/teacher/all.json
```
{
"teachers":[
{"username":"zhangsan", "name":"張三", "sex":0,"email":"zhangsan@yunzhiclub.com"},
{"username":"lisi", "name":"李四", "sex":1,"email":"lisi@yunzhiclub.com"}]
}
```
> 注意:json文件的格式要求較為嚴格,sublime有json格式的自動查錯功能。在輸寫時,要特別注意數據格式。否則,進行數據請求時,將返回語法錯誤的提示。
請求該模擬數據文件:
```
...
// 獲取所有教師
var all = function(callback) {
// $http.get().then(function1(){}, function2(){}); 鏈式調用 then()中接收兩個參數,類型均為function
$http.get('resource/teacher/all.json').then(function success(response) {
// 數據成功返回
console.log(response);
var teachers = response.data.teachers;
callback(teachers);
}, function error(response) {
console.log('數據請求錯誤:');
console.log(response);
});
};
...
```
# 測試
此時,我們在刷新鏈接[http://localhost:9000/#!/teacher/](http://localhost:9000/#!/teacher/), 教師數據成功返回。
todo:截取到有控制臺的圖
## 單元測試
我們前面說過,每進行一項開發,我們均需要有單元測試的支持,來保證我們程序開的正確性。當然,這也是分層分工合作的前提,比如團隊現在給你的任務是做M層開發。并不涉及到C層與V層,那我們如果在只有M層的前提下,來保證代碼的質量呢?答案是唯一的:單元測試。
上面,我們將內部的hardcode變得資源文件后,涉及到了資源請求引起異步的問題。由于異步的存在,這使得我們原來的單元測試失效。
> javascript在兩種情況下將異步執行:1.進行資源請求時. 2.執行timeout()進行延時時。
為了排除其它單元測試對我們的影響,我們先去除C層測試中的測試代碼:
test/spec/controllers/teacher/index.js
```
...
it('測試M層的靜態數據', function() {
// expect(scope.teachers.length).toBe(2);
// expect(scope.teachers[0].username).toBe('zhangsan');
});
...
```
然后改寫M層的測試代碼:
test/spec/services/teacher.js
```
...
it('應該取出來所有的教師數據', function() {
// $httpBackend.flush();
var teachers;
console.log('開始執行異步請求函數');
teacher.all(function(data) {
teachers = data;
console.log('獲取教師數據如下:');
console.log(teachers);
});
console.log('結束執行異步請求函數');
});
...
```
然后查看控制臺:
```
cket AskAaTUxX9RPo52mAAAA with id 64850930
LOG: '開始執行異步請求函數'
LOG: '結束執行異步請求函數'
PhantomJS 2.1.1 (Mac OS X 0.0.0): Executed 5 of 5 SUCCESS (0.008 secs / 0.041 secs)
```
是的,正如我們看到的一樣,karma單元測試工具,并沒有成功的執行帶有資源請求的回調函數。
## ngMock
鑒于此,angularjs給出了ngMock來解決這個問題, 我們在此使用其`$httpBackend`服務。
> ngMock官方文檔:[https://docs.angularjs.org/api/ngMock](https://docs.angularjs.org/api/ngMock), $httpBackend[https://docs.angularjs.org/api/ngMock/service/$httpBackend](https://docs.angularjs.org/api/ngMock/service/$httpBackend)
test/spec/services/teacher.js
```
'use strict';
describe('Service: teacher', function() {
// load the service's module
beforeEach(module('webAppApp'));
// instantiate service
var teacher, $httpBackend;
// 引用$httpBackend
beforeEach(inject(function(_teacher_, _$httpBackend_) {
teacher = _teacher_;
$httpBackend = _$httpBackend_;
// 定義請求 URL
var url = 'resource/teacher/all.json';
// 定義返回數據。注意此處的json數據的寫法與xxx.json文件中的json數據的寫的法的異同。
var data = {
teachers: [
{ username: 'zhangsan', name: '張三', sex: 0, email: 'zhangsan@yunzhiclub.com' },
{ username: 'lisi', name: '李四', sex: 1, email: 'lisi@yunzhiclub.com' }
]
};
// 進行模似數據請求配置.當請求方法為GET,資源名為resource/teacher/index/all.json, 返回data數據.
$httpBackend.when('GET', url).respond(data);
}));
it('should do something', function() {
expect(!!teacher).toBe(true);
});
it('應該取出來所有的教師數據', function() {
// 調用teacher中的all方法,并在回調函數中執行測試
teacher.all(function(teachers){
expect(teachers.length).toBe(2);
});
// 模擬數據請求
$httpBackend.flush();
});
});
```
我們在`beforeEach`進行了模擬請求的配置,這保證了每執行一次`it()`,`beforeEach`中的代碼都會被執行一次。`$httpBackend.flush();`相當于執行了數據的模擬請求。這為我們執行異步的回調函數創造了條件.
> git checkout -f step8.2.3
<hr />
延伸:
可能你已經發現了,在進行單元測試時,我們實際上并沒有真正的請求`app/resource/teacher/index/all.json`這個json文件。那么是不是這個文件的存在就失去意義了呢?
答案是否定的,這個json文件存在很有必要,原因最少有以下幾點:
1. 為C層開發的數據對接提供基礎.
2. 為后臺的數據對接提供示例.
3. 為后期我們可能引用的e2e(端對端)測試提供基礎。
> e2e(端對端)測試:一個神奇的可以自動替我們在頁面中輸入內容,點擊按鈕,并查看結果的測試方法。
<hr />
參考資料:
google關鍵字:`angularjs $http test`
> Angular-mock之使用$httpBackend服務測試$http: [https://segmentfault.com/a/1190000003716613](https://segmentfault.com/a/1190000003716613)
> How to unit test Angular’s $resource and $http: [http://blog.davidjs.com/2013/09/tricky-unit-testing-of-httpbackend/](http://blog.davidjs.com/2013/09/tricky-unit-testing-of-httpbackend/)
- README
- 第一章:準備
- 第二章:Hello World!
- 第一節:查看工程文件
- 第二節:JDK、JRE與環境變量
- 第三節:index.jsp
- 第三章:Hello Struts
- 第一節:Web.xml
- 第二節:單入口
- 第三節:Hello Struts
- 第四節:觸發C層
- 第四章:建立數據表
- 第一節:建立實體類
- 第二節:測試一
- 第三節:測試二
- 第四節:引入Hibernate
- 第五節:配置Hibernate
- 第六節:建立連接
- 第七節:實體類映射數據表
- 第八節:完善數據表
- 第五章:教師管理
- 第一節:增加數據--add
- 第二節:增加數據--save
- 1 獲取傳入數據數據
- 2 數據寫入測試
- 3 對接C層
- 第三節:數據列表
- 1 獲取數據
- 2 重構代碼
- 3 C層對接--初始化
- 4 C層添加數據
- 5 V層顯示數據
- 6 獲取數據庫中數據
- 7 顯示性別
- 8 分頁
- 9 條件查詢
- 第四節:修改數據
- 1 edit
- 2 update
- 第五節:刪除數據
- 第六節:總結
- 第六章:重構C層
- 第一節:繼承ActionSupport類
- 第二節:數據驗證
- 第七章:前臺分離(前臺)
- 第一節:環境搭建
- 第二節:運行環境
- 第三節:共享開發環境
- 第四節:生產環境
- 第八章:前臺開發(前臺)
- 第一節:本地化
- 第二節:教師列表
- 1 引入M層
- 2 模擬后臺返回數據
- 3 C與M對接
- 4 C與V對接
- 第九章:前后臺對接(前后臺)
- 第一節:后臺輸出json(后臺)
- 第二節:對接前臺(全棧)
- 第二節:對接API(前臺)
- 第二節:跨域請求(后臺)
- 第三節:重構代碼(前臺)
- 第十章:重構后臺M層
- 第一節:數據訪問DAO層
- 第二節:項目整體重構
- 第十一章:用戶登陸(前后臺)
- 第一節:制定規范
- 第二節:定制測試用例
- 第三節:后臺輸入測試代碼(后臺)
- 第四節:postman(后臺)
- 第五節:新建用戶登陸模塊(前臺)
- 第六節:代碼重構(前臺)
- 第十二章:班級管理(前后臺)
- 第一節:班級列表
- 1 原型開發
- 2 制定規范
- 3 后臺對接開發
- 4 前臺對接開發
- 第二節:Add
- 1 原型開發
- 2 制定規范
- 3 后臺對接開發
- 4 前臺對接開發
- 第三節:Save
- 1 制定規范
- 2 后臺對接開發
- 3 前臺對接開發
- 第四節:Edit
- 1 原型開發
- 2 制定規范
- 3 后臺對接開發
- 4 前臺對接開發
- 第五節:Update
- 1 制定規范
- 2 后臺對接開發
- 3 前臺對接開發
- 第六節:Delete
- 1 制定規范
- 2 后臺對接開發
- 3 前臺對接開發
- 第七節:小結
- 第十三章:班級管理(API)
- 第一節:ER圖
- 第二節:create
- 1 實體層
- 2 dao層
- 3 service(server)層
- 4 action層
- 第三節:ManyToOne
- 第四節:Read
- 1 service(server)層
- 2 action層
- 第五節:update
- 1 service(server)層
- 2 action層
- 第六節:update
- 第十四章:重構服務層