在前后台分离开发中,我们在进行前台开发时,后台的工作正在进行中,或是还没有进行。这就要求我们在合作开发中,做下以下两点:
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
- 第十四章:重构服务层