如何在 AngularJS 中对控制器php进行单元测试试

AngularJS应用主要依赖于控制器来控制数据在应用程序中的流动。控制器采用ng-controller指令定义。控制器是一个包含属性/属性和JavaScript对象的功能。每个控制器接受$scope参数指定应用程序/模块,由控制器控制。
ng-app=&& ng-controller=&studentController&
在这里,我们已经声明采用ng-controller指令的控制器studentController。作为下一步,我们将定义studentController如下
function studentController($scope) {
$scope.student = {
firstName: &yiibai&,
lastName: &com&,
fullName: function() {
var studentObject;
studentObject = $scope.student;
return studentObject.firstName + & & + studentObject.lastName;
studentController 定义 $scope 作为JavaScript对象参数。
$scope 表示应用程序,使用studentController对象。
$scope.student 是studentController对象的属性。
firstName和lastName是$scope.student 对象的两个属性。我们已经通过了默认值给他们。
fullName 是$scope.student对象的函数,它的任务是返回合并的名称。
在fullName函数中,我们现在要学生对象返回组合的名字。
作为一个说明,还可以定义控制器对象在单独的JS文件,并把有关文件中的HTML页面。
现在可以使用ng-model或使用表达式如下使用studentController学生的属性。
Enter first name: &input type=&text& ng-model=&student.firstName&&&br&
Enter last name: &input type=&text& ng-model=&student.lastName&&&br&
You are entering: {{student.fullName()}}
现在有 student.firstName 和 student.lastname 两个输入框。
现在有 student.fullName()方法添加到HTML。
现在,只要输入first name和lastname输入框中输入什么,可以看到两个名称自动更新。
下面的例子将展示使用控制器。
testAngularJS.html 文件内容如下:
Angular JS Controller
AngularJS Sample Application
ng-app=&& ng-controller=&studentController&
Enter first name:
type=&text& ng-model=&student.firstName&
Enter last name:
type=&text& ng-model=&student.lastName&
You are entering: {{student.fullName()}}
function studentController($scope) {
$scope.student = {
firstName: &Mahesh&,
lastName: &Parashar&,
fullName: function() {
var studentObject;
studentObject = $scope.student;
return studentObject.firstName + & & + studentObject.lastName;
src=&/ajax/libs/angularjs/1.2.15/angular.min.js&
在Web浏览器打开textAngularJS.html,看到以下结果:
加QQ群啦!
JAVA技术群:
MySQL/SQL群:
Python技术群:
大数据开发群: (Java,Python,R,Scala,Hadoop,Spark,zookeeper,kafka等技术)
Web开发群(新群):
(PHP,HTML/HTML5,JS/JQuery,CSS等技术)
Linux技术群(新群): (Redhat/Centos,Ubuntu,Shell,运维,监控等技术)
易百教程移动端:请扫描本页面底部(右侧)二维码关注微信公众号,或直接手机访问:
上一篇:下一篇:新手园地& & & 硬件问题Linux系统管理Linux网络问题Linux环境编程Linux桌面系统国产LinuxBSD& & & BSD文档中心AIX& & & 新手入门& & & AIX文档中心& & & 资源下载& & & Power高级应用& & & IBM存储AS400Solaris& & & Solaris文档中心HP-UX& & & HP文档中心SCO UNIX& & & SCO文档中心互操作专区IRIXTru64 UNIXMac OS X门户网站运维集群和高可用服务器应用监控和防护虚拟化技术架构设计行业应用和管理服务器及硬件技术& & & 服务器资源下载云计算& & & 云计算文档中心& & & 云计算业界& & & 云计算资源下载存储备份& & & 存储文档中心& & & 存储业界& & & 存储资源下载& & & Symantec技术交流区安全技术网络技术& & & 网络技术文档中心C/C++& & & GUI编程& & & Functional编程内核源码& & & 内核问题移动开发& & & 移动开发技术资料ShellPerlJava& & & Java文档中心PHP& & & php文档中心Python& & & Python文档中心RubyCPU与编译器嵌入式开发驱动开发Web开发VoIP开发技术MySQL& & & MySQL文档中心SybaseOraclePostgreSQLDB2Informix数据仓库与数据挖掘NoSQL技术IT业界新闻与评论IT职业生涯& & & 猎头招聘IT图书与评论& & & CU技术图书大系& & & Linux书友会二手交易下载共享Linux文档专区IT培训与认证& & & 培训交流& & & 认证培训清茶斋投资理财运动地带快乐数码摄影& & & 摄影器材& & & 摄影比赛专区IT爱车族旅游天下站务交流版主会议室博客SNS站务交流区CU活动专区& & & Power活动专区& & & 拍卖交流区频道交流区
小富即安, 积分 4181, 距离下一级还需 819 积分
论坛徽章:2
开发者们都一致认为单元测试在开发项目中十分有好处。它们帮助你保证代码的质量,从而确保更稳定的研发,即使需要重构时也更有信心。
& && && && &
测试驱动开发流程图
AngularJS的代码声称其较高的可测性确实是合理的。单单文档中列出端对端的测试实例就能说明。就像AngularJS这样的项目虽然都说单元测试很简单但真正做好却不容易。即使官方文档中以提供了详尽的实例,但在我的实际应用中却还是很有挑战。这里我就简单示范一下我是怎么操作的吧.
Instant Karma
Karma 是来Angular团队针对JavaScript开发的一个测试运行框架。它很方便的实现了自动执行测试任务从而替代了繁琐的手工操作(好比回归测试集或是加载目标测试的依赖关系)Karma 和Angular的协作就好比花生酱和果冻.
只需要在Karma中定义好配置文件启动它,接下来它就会在预期的测试环境下的自动执行测试用例。你可以在配置文件中制定相关的测试环境。angular-seed,是我强烈推荐的可以快速实施的方案。在我近期的项目中Karma 的配置如下:
module.exports = function(config) {
& & config.set({
& && &&&basePath: '../',
& && &&&files: [
& && && && &'app/lib/angular/angular.js',
& && && && &'app/lib/angular/angular-*.js',
& && && && &'app/js/**/*.js',
& && && && &'test/lib/recaptcha/recaptcha_ajax.js',
& && && && &'test/lib/angular/angular-mocks.js',
& && && && &'test/unit/**/*.js'
& && &&&],
& && &&&exclude: [
& && && && &'app/lib/angular/angular-loader.js',
& && && && &'app/lib/angular/*.min.js',
& && && && &'app/lib/angular/angular-scenario.js'
& && &&&],
& && &&&autoWatch: true,
& && &&&frameworks: ['jasmine'],
& && &&&browsers: ['PhantomJS'],
& && &&&plugins: [
& && && && &'karma-junit-reporter',
& && && && &'karma-chrome-launcher',
& && && && &'karma-firefox-launcher',
& && && && &'karma-jasmine',
& && && && &'karma-phantomjs-launcher'
& && &&&],
& && &&&junitReporter: {
& && && && &outputFile: 'test_out/unit.xml',
& && && && &suite: 'unit'
这个跟angular-seed的默认配置类似只不过有以下几点不同:
需要更改浏览器从Chrome 转到PhantomJS, 这样每次跳转时无需再打开新的浏览器窗口,但在OSX系统会有窗口延迟。所以这个插件还有浏览器设置都做了更改。
由于我的应用需要引用Google的Recaptcha服务因此添加了依赖的recaptcha_ajax.js小文件。这个小配置就像在Karma的配置文件中添加一行代码那么简单。
autoWatch确实是个很酷的设置,它会让Karma在有文件更改时自动回归你的测试用例。你可以这样安装Karma:
npm install karma
angular-seed 提供了一个简单的脚本inscripts/test.sh去触发Karma的测试。
用Jasmine设计测试用例
当使用Jasmine----一种行为驱动开发模式的JavaScript测试框架为Angular设计单元测试用例时大部分的资源都已可获取。
这也就是我接下来要说的话题。
如果你要对AngularJS controller做单元测试可以利用Angular的依赖注入dependency injection 功能导入测试场景中controller需要的服务版本还能同时检查预期的结果是否正确。例如,我定义了这个controller去高亮需要导航去的那个页签:
app.controller('NavCtrl', function($scope, $location) {
& & $scope.isActive = function(route) {
& && &&&return route === $location.path();
如果想要测试isActive方法,我会怎么做呢?我将检查$locationservice 变量是否返回了预期值,方法返回的是否预期值。因此在我们的测试说明中我们会定义好局部变量保存测试过程中需要的controlled版本并在需要时注入到对应的controller当中。然后在实际的测试用例中我们会加入断言来验证实际的结果是否正确。整个过程如下:
describe('NavCtrl', function() {
& & var $scope, $location, $rootScope, createC
& & beforeEach(inject(function($injector) {
& && &&&$location = $injector.get('$location');
& && &&&$rootScope = $injector.get('$rootScope');
& && &&&$scope = $rootScope.$new();
& && &&&var $controller = $injector.get('$controller');
& && &&&createController = function() {
& && && && &return $controller('NavCtrl', {
& && && && && & '$scope': $scope
& && && && &});
& && &&&};
& & it('should have a method to check if the path is active', function() {
& && &&&var controller = createController();
& && &&&$location.path('/about');
& && &&&expect($location.path()).toBe('/about');
& && &&&expect($scope.isActive('/about')).toBe(true);
& && &&&expect($scope.isActive('/contact')).toBe(false);
使用整个基本的结构,你就能设计各种类型的测试。由于我们的测试场景使用了本地的环境来调用controller,你也可以多加上一些属性接着执行一个方法清除这些属性,然后再验证一下属性到底有没有被清除。
$httpBackendIs Cool
那么要是你在调用$httpservice请求或是发送数据到服务端呢?还好,Angular提供了一种
$httpBackend的mock方法。这样的话,你就能自定义服务端的响应内容,又或是确保服务端的响应结果能和单元测试中的预期保持一致。
具体细节如下:
describe('MainCtrl', function() {
& & var $scope, $rootScope, $httpBackend, $timeout, createC
& & beforeEach(inject(function($injector) {
& && &&&$timeout = $injector.get('$timeout');
& && &&&$httpBackend = $injector.get('$httpBackend');
& && &&&$rootScope = $injector.get('$rootScope');
& && &&&$scope = $rootScope.$new();
& && &&&var $controller = $injector.get('$controller');
& && &&&createController = function() {
& && && && &return $controller('MainCtrl', {
& && && && && & '$scope': $scope
& && && && &});
& && &&&};
& & afterEach(function() {
& && &&&$httpBackend.verifyNoOutstandingExpectation();
& && &&&$httpBackend.verifyNoOutstandingRequest();
& & it('should run the Test to get the link data from the go backend', function() {
& && &&&var controller = createController();
& && &&&$scope.urlToScrape = '';
& && &&&$httpBackend.expect('GET', '/slurp?urlToScrape=http:%')
& && && && &.respond({
& && && && && & &success&: true,
& && && && && & &links&: [&&, &http://angularjs.org&, &&]
& && && && &});
& && &&&// have to use $apply to trigger the $digest which will
& && &&&// take care of the HTTP request
& && &&&$scope.$apply(function() {
& && && && &$scope.runTest();
& && &&&});
& && &&&expect($scope.parseOriginalUrlStatus).toEqual('calling');
& && &&&$httpBackend.flush();
& && &&&expect($scope.retrievedUrls).toEqual([&&, &http://angularjs.org&, &&]);
& && &&&expect($scope.parseOriginalUrlStatus).toEqual('waiting');
& && &&&expect($scope.doneScrapingOriginalUrl).toEqual(true);
正如你所见,beforeEach call其实都很类似,唯一不同的是我们是从injector获取$httpBackend而并非直接获取。即使如此,创建不同的测试时还会有一些明显的不同之处。对初学者来说,会有一个afterEachcall 方法来确保$httpBackend在每次用例执行后不会有明显的异常请求。如果你观察一下测试场景的设置和$httpBackend方法的应用就会会发现有那么几点不是那么直观的。
实际上调用$httpBackend的方法也算是简单明了但还不够——我们还得在传值给$scope.$apply的方法中把调用封装到实际测试中的$scope.runTest方法上。这样在$digest被触发后才能处理HTTP请求。而如你所见直到我们调用$httpBackend.flush()方法后$httpBackend才会被解析,这也就保证了我们能在调用过程中去验证返回的结果是否正确(在上面的示例中,controller的$scope.parseOriginalUrlStatusproperty属性将被传递给调用者,我们也因此能实时监控)
接下来的几行代码都是在调用过程中检测$scopethat属性的断言。很酷吧?
提示:在某些单元测试中,用户习惯把没有$的范围标记为变量。这个在Angular文档中并没有强制要求或是过分强调,只是我在使用中为了提高可读性和一致性才使用$scopelike这种方式。
也许这就是我做起来对其他人而言只是自然而然能做到的事情之一,但是学习使用Angular编写单元测试一开始对我而言确实是相当痛苦的。我发现自己对如何开始的理解大多来自互联网上各种博客文章和资源的拼拼凑凑,没有真正一致或明确的最佳实践,而是通过自然而然随意的选择。我想针对我最终得到的成果提供一些文档,以帮助那些也许还在坑里面挣扎的其他人,毕竟他们只是想要编写代码而已,而非不得不去了解Angular和Jasmine中所有的怪异特性和独特用法。因此我希望这篇文章能对你有些许帮助。
本文转自:开源中国社区
英文原文:
本文来自ChinaUnix新闻频道,如果查看原文请点:
&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp在 SegmentFault,解决技术问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
一线的工程师、著名开源项目的作者们,都在这里:
获取验证码
已有账号?
问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
我是Angular初学者,在学习中有个关于定义控制器的写法问题。
在刚接触的时候,书上是这样写控制的:
var myApp = angular.module('MyApp', []);
myApp.controller('MyController', function($scope){
// todo...
后来在一些网络上的文章,是这样写的:
var myApp = angular.module('MyApp', []);
myApp.controller('MyController', ['$scope', function($scope){
// todo...
那么问题来了,后者加了个[],是个什么意思?这两种写法有什么不同呢?
在练习中发现同样的实现用这两种都可以。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
第二种写法叫inline-annotation,看文档
这种写法的主要目的是为了避免源码压缩(uglify)时变量名被替换,导致依赖注入失败的问题。
如果对实现有兴趣,可以看
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
是要注入的依赖,这种写法利于以后的代码压缩
同步到新浪微博
分享到微博?
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:&& &文章主题:
交流经验:
总积分:115
级别:普通会员
如何在 AngularJS 中对控制器进行单元测试
开发者们都一致认为单元测试在开发项目中十分有好处。它们帮助你保证代码的质量,从而确保更稳定的研发,即使需要重构时也更有信心。
测试驱动开发流程图
AngularJS的代码声称其较高的可测性确实是合理的。单单文档中列出端对端的测试实例就能说明。就像AngularJS这样的项目虽然都说单元测试很简单但真正做好却不容易。即使官方文档中以提供了详尽的实例,但在我的实际应用中却还是很有挑战。这里我就简单示范一下我是怎么操作的吧.
Instant Karma
&是来Angular团队针对JavaScript开发的一个测试运行框架。它很方便的实现了自动执行测试任务从而替代了繁琐的手工操作(好比回归测试集或是加载目标测试的依赖关系)Karma 和Angular的协作就好比花生酱和果冻.
只需要在Karma中定义好配置文件启动它,接下来它就会在预期的测试环境下的自动执行测试用例。你可以在配置文件中制定相关的测试环境。,是我强烈推荐的可以快速实施的方案。在我近期的项目中Karma 的配置如下:
module.exports = function(config)
config.set(
basePath: '../',
'app/lib/angular/angular.js',
'app/lib/angular/angular-*.js',
'app/js/**/*.js',
'test/lib/recaptcha/recaptcha_ajax.js',
'test/lib/angular/angular-mocks.js',
'test/unit/**/*.js'
exclude: [
'app/lib/angular/angular-loader.js',
'app/lib/angular/*.min.js',
'app/lib/angular/angular-scenario.js'
autoWatch: true,
frameworks: ['jasmine'],
browsers: ['PhantomJS'],
plugins: [
'karma-junit-reporter',
'karma-chrome-launcher',
'karma-firefox-launcher',
'karma-jasmine',
'karma-phantomjs-launcher'
junitReporter:
outputFile: 'test_out/unit.xml',
suite: 'unit'
这个跟的默认配置类似只不过有以下几点不同:
需要更改浏览器从Chrome 转到,&这样每次跳转时无需再打开新的浏览器窗口,但在OSX系统会有窗口延迟。所以这个插件还有浏览器设置都做了更改。
由于我的应用需要引用Google的Recaptcha服务因此添加了依赖的recaptcha_ajax.js小文件。这个小配置就像在Karma的配置文件中添加一行代码那么简单。
autoWatch确实是个很酷的设置,它会让Karma在有文件更改时自动回归你的测试用例。你可以这样安装Karma:
npm install karma
&提供了一个简单的脚本inscripts/test.sh去触发Karma的测试。
用Jasmine设计测试用例
当使用----一种行为驱动开发模式的JavaScript测试框架为Angular设计单元测试用例时大部分的资源都已可获取。
这也就是我接下来要说的话题。
如果你要对AngularJS controller做单元测试可以利用Angular的依赖注入&功能导入测试场景中controller需要的服务版本还能同时检查预期的结果是否正确。例如,我定义了这个controller去高亮需要导航去的那个页签:
app.controller('NavCtrl', function($scope, $location)
$scope.isActive = function(route)
return route === $location.path();
如果想要测试isActive方法,我会怎么做呢?我将检查$locationservice 变量是否返回了预期值,方法返回的是否预期值。因此在我们的测试说明中我们会定义好局部变量保存测试过程中需要的controlled版本并在需要时注入到对应的controller当中。然后在实际的测试用例中我们会加入断言来验证实际的结果是否正确。整个过程如下:
describe('NavCtrl', function()
var $scope, $location, $rootScope, createC
beforeEach(inject(function($injector)
$location = $injector.get('$location');
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
var $controller = $injector.get('$controller');
createController = function()
return $controller('NavCtrl',
'$scope': $scope
it('should have a method to check if the path is active', function()
var controller = createController();
$location.path('/about');
expect($location.path()).toBe('/about');
expect($scope.isActive('/about')).toBe(true);
expect($scope.isActive('/contact')).toBe(false);
使用整个基本的结构,你就能设计各种类型的测试。由于我们的测试场景使用了本地的环境来调用controller,你也可以多加上一些属性接着执行一个方法清除这些属性,然后再验证一下属性到底有没有被清除。
$httpBackendIs Cool
那么要是你在调用$httpservice请求或是发送数据到服务端呢?还好,Angular提供了一种
$httpBackend的mock方法。这样的话,你就能自定义服务端的响应内容,又或是确保服务端的响应结果能和单元测试中的预期保持一致。
具体细节如下:
describe('MainCtrl', function()
var $scope, $rootScope, $httpBackend, $timeout, createC
beforeEach(inject(function($injector)
$timeout = $injector.get('$timeout');
$httpBackend = $injector.get('$httpBackend');
$rootScope = $injector.get('$rootScope');
$scope = $rootScope.$new();
var $controller = $injector.get('$controller');
createController = function()
return $controller('MainCtrl',
'$scope': $scope
afterEach(function()
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
it('should run the Test to get the link data from the go backend', function()
var controller = createController();
$scope.urlToScrape = '';
$httpBackend.expect('GET', '/slurp?urlToScrape=http:%')
&success&: true,
&links&: [&&, &http://angularjs.org&, &&]
// have to use $apply to trigger the $digest which will
// take care of the HTTP request
$scope.$apply(function()
$scope.runTest();
expect($scope.parseOriginalUrlStatus).toEqual('calling');
$httpBackend.flush();
expect($scope.retrievedUrls).toEqual([&&, &http://angularjs.org&, &&]);
expect($scope.parseOriginalUrlStatus).toEqual('waiting');
expect($scope.doneScrapingOriginalUrl).toEqual(true);
正如你所见,beforeEach call其实都很类似,唯一不同的是我们是从injector获取$httpBackend而并非直接获取。即使如此,创建不同的测试时还会有一些明显的不同之处。对初学者来说,会有一个afterEachcall 方法来确保$httpBackend在每次用例执行后不会有明显的异常请求。如果你观察一下测试场景的设置和$httpBackend方法的应用就会会发现有那么几点不是那么直观的。
实际上调用$httpBackend的方法也算是简单明了但还不够——我们还得在传值给$scope.$apply的方法中把调用封装到实际测试中的$scope.runTest方法上。这样在$digest被触发后才能处理HTTP请求。而如你所见直到我们调用$httpBackend.flush()方法后$httpBackend才会被解析,这也就保证了我们能在调用过程中去验证返回的结果是否正确(在上面的示例中,controller的$scope.parseOriginalUrlStatusproperty属性将被传递给调用者,我们也因此能实时监控)
接下来的几行代码都是在调用过程中检测$scopethat属性的断言。很酷吧?
提示:在某些单元测试中,用户习惯把没有$的范围标记为变量。这个在Angular文档中并没有强制要求或是过分强调,只是我在使用中为了提高可读性和一致性才使用$scopelike这种方式。
也许这就是我做起来对其他人而言只是自然而然能做到的事情之一,但是学习使用Angular编写单元测试一开始对我而言确实是相当痛苦的。我发现自己对如何开始的理解大多来自互联网上各种博客文章和资源的拼拼凑凑,没有真正一致或明确的最佳实践,而是通过自然而然随意的选择。我想针对我最终得到的成果提供一些文档,以帮助那些也许还在坑里面挣扎的其他人,毕竟他们只是想要编写代码而已,而非不得不去了解Angular和Jasmine中所有的怪异特性和独特用法。因此我希望这篇文章能对你有些许帮助。
精品视频课程推荐
深入浅出的讲解JavaBen的写法、JavaBean的用法、JavaBean的实现机制、JavaBean对应翻译的代码理解。
本课程专注于数据结构和算法的内容,使用Java来进行代码示例,不空洞的讲解概念和理论,重点放在代码的实现和示例上。
从零开始、全面系统、成体系的讲解数据结构和基本算法,循序渐进的讲述构建软件系统所常见的数据结构和算法。
内容概述:Shiro是目前最热门、最易用、功能超强大的Java权限管理框架,强烈推荐,每个项目都必备的权限管理技术!通过本课程,你将从零开始直到彻底掌握Shiro的相关开发知识,达到可以进行实际项目开发的能力。包括:权限管理基础、Shiro入门、配置、身份认证、授权、Realms、Session管理、和Spring的集成、Web、Cache等众多开发细节技术
技术要点:源码级分析Shiro的授权过程、自定义开发Realm、多个Realms的开发配置、自定义开发AuthenticationStrategy、自定义开发自定义SessionDAO、和Struts2+Spring3的集成(包括修正struts2的bug)、Shiro和SpringMVC+Spring3的集成、包装使用其他的Cache框架、缓存数据同步更新的解决方案等等实际开发中常用的内容
深入浅出的讲解Struts2对AJAX的支持,包括使用stream的Result Type来应用Ajax;使用Struts2提供的Ajax标签;使用JSON插件
系统、完整的学习Spring Web MVC开发的知识。包括:Spring Web MVC入门;理解DispatcherServlet;注解式控制器开发详解;数据类型转换;数据格式化;数据验证; 拦截器;对Ajax的支持;文件上传下载;表单标签等内容;最后以一个综合的CRUD带翻页的应用示例来综合所学的知识
选择一个版面
软件设计专版
Web前端技术
学习问题讨论
面试、就业
版权所有 Copyright(C) 私塾在线学习网

我要回帖

更多关于 idea如何进行单元测试 的文章

 

随机推荐