$watch回调每次在浏览器中触发,但在测试期间只触发一次
$watch callback fires every time in browser but just once during the test
我有一些代码应该存储有关过滤器更改的信息。在浏览器中,每次更改值时都会触发$watch
回调。然而,当我试图在茉莉花/因果报应测试中复制这一点时,它在第一次改变后就着火了。
控制器
var tableFilter = this
;
var init0 = true
;
$scope.$watch( 'tableFilter.config.period', function () {
console.log('watch');
if ( !init0 ) {
console.log('set dirty');
tbFilterConfig.set( 'pristine', false );
tbFilterConfig.setDirty( 'period' );
}
init0 = false;
} );
测试
describe( '$scope $watch', function () {
it( 'should add period to dirty after second change', function () {
$scope.$apply(console.log(1), controller.config.period = 'test');
expect( tbFilterConfigObj.get( 'dirty' ) ).toEqual( [] );
$scope.$apply(console.log(2), controller.config.period = 'test2');
expect( tbFilterConfigObj.get( 'dirty' ) ).toEqual( [ 'period' ] );
} );
});
控制台输出:
LOG: 1
LOG: 'watch'
LOG: 2
PhantomJS 1.9.8 (Windows 8 0.0.0) Controller: TableFilterCtrl $scope $watch should add period to dirty after second change FAILED
Expected [ ] to equal [ 'period' ].
at C:/Projects/trackback-network-insight-ui/test/spec/controllers/table_filter.js:74
但在浏览器中:
setTimeout(function ( ) {
$scope.$apply(console.log(1), tableFilter.config.period = 'test');
$scope.$apply(console.log(2), tableFilter.config.period = 'test2');
}, 1000);
给我:
LOG: watch
LOG: 1
LOG: watch
LOG: set dirty
LOG: 2
LOG: watch
LOG: set dirty
这是意料之中的行为。
那我的考试怎么了?提前感谢!
编辑
完整控制器代码:
angular.module( 'insightApp' )
.controller( 'TableFilterCtrl', [ '$rootScope', 'tbConfig', '$scope', '$http', 'API_PATH', 'uiAlerts', 'tbFilterConfig',
function ( $rootScope, tbConfig, $scope, $http, API_PATH, uiAlerts, tbFilterConfig ) {
var tableFilter = this
, newData = {}
;
/* EXTRACT INITIAL DATA OR LOAD DATA IF TYPE OF DATA IS STRING */
/* FETCH !!SELECT!! DATA FROM API */
function fetchData( url, name ) {
return $http.post( API_PATH + url, tbFilterConfig.get() )
.success( function ( data ) {
newData[ name ] = data;
} )
.error( function ( error ) {
uiAlerts.set( {
error: 'Ooops! We were unable to get filter data.'
} );
console.error( error );
} );
}
tableFilter.filters = tbConfig.get( 'filters' );
tableFilter.reports = tbConfig.get( 'reports' );
tableFilter.config = tbFilterConfig.getRaw();
/* RESET VALUES OF ALL DEPENDENT FILTERS */
function resetDependent( name ) {
var filters = tbConfig.get( 'filters' )
;
filters.forEach( function ( filter ) {
if ( filter.dependencies && filter.dependencies.indexOf( name ) > -1 ) {
if ( tbFilterConfig.get( filter.key ) !== filter.defaultVal ) {
tbFilterConfig.set( filter.key, filter.defaultVal );
tbFilterConfig.setDirty( filter.key );
}
}
} );
}
/* GET INITIAL DATA FOR SELECT OPTIONS */
tableFilter.getData = function ( name, data, defaultVal ) {
if ( !(name in newData) ) {
newData[ name ] = [];
if ( typeof data === 'string' ) {
fetchData( data, name );
} else if ( typeof data === 'object' ) {
newData[ name ] = data;
} else {
console.error( 'Unexpected data type:', typeof data, data );
}
if ( !tbFilterConfig.get( name ) ) {
tbFilterConfig.set( name, defaultVal );
}
var init = true;
$scope.$watch( 'tableFilter.config.' + name, function () {
if ( !init ) {
resetDependent( name );
tbFilterConfig.set( 'pristine', false );
tbFilterConfig.setDirty( name );
}
init = false;
} );
}
return newData[ name ];
};
/* WATCH FIXED PROPERTIES: PERIOD, DATE_FROM, DATE_TO */
var init0 = true
, init1 = true
, init2 = true
;
$scope.$watch( 'tableFilter.config.period', function () {
if ( !init0 ) {
tbFilterConfig.set( 'pristine', false );
tbFilterConfig.setDirty( 'period' );
}
init0 = false;
} );
/* WATCH FIXED PROPERTIES DATE_FORM */
$scope.$watch( 'tableFilter.config.date_from', function () {
if ( !init1 ) {
tbFilterConfig.set( 'pristine', false );
tbFilterConfig.setDirty( 'date_from' );
}
init1 = false;
} );
/* WATCH FIXED PROPERTIES DATE_TO */
$scope.$watch( 'tableFilter.config.date_to', function () {
if ( !init2 ) {
tbFilterConfig.set( 'pristine', false );
tbFilterConfig.setDirty( 'date_to' );
}
init2 = false;
} );
/* UPDATE FILTER DATA */
tableFilter.updateSelectData = function ( url, name, dependencies ) {
if ( typeof url === 'string' ) {
var touched = false;
/* CHECK IF DEPENDENCIES CHANGED */
for ( var i = 0; i < dependencies.length; i++ ) {
if ( tableFilter.config.dirty.indexOf( dependencies[ i ] ) > -1 ) {
touched = true;
break;
}
}
/* IF DEPENDENCIES CHANGED GET NEW DATA */
if ( touched ) {
return fetchData( url, name );
}
}
};
} ] );
和测试:
describe( 'Controller: TableFilterCtrl', function () {
// load the controller's module
beforeEach( function () {
module( 'insightApp' );
} );
var controller
, $scope
, alerts
, tbFilterConfigObj
;
// Initialize the controller and a mock scope
beforeEach( inject( function ( $controller, $rootScope, uiAlerts, tbFilterConfig ) {
tbFilterConfigObj = tbFilterConfig;
$scope = $rootScope.$new();
alerts = uiAlerts;
controller = $controller( 'TableFilterCtrl', {
$scope: $scope
} );
$scope.$apply();
} ) );
describe( '$scope $watch', function () {
it( 'should add period to dirty after second change', function () {
$scope.$apply( console.log( 1 ), controller.config.period = 'test' );
expect( tbFilterConfigObj.get( 'dirty' ) ).toEqual( [] );
$scope.$apply( console.log( 2 ), controller.config.period = 'test2' );
expect( tbFilterConfigObj.get( 'dirty' ) ).toEqual( [ 'period' ] );
} );
} );
});
EDIT2
我对代码质量感到抱歉,但它还没有生产出来。
AngularJS在浏览器中运行时,它会运行digest
周期来触发所有观察者及其回调。这就是Angular工作的原因,它是自动发生的。
这种机制在单元测试期间不起作用。运行测试时,需要通过调用$apply
或$digest
手动触发摘要机制。
让我们来分析一下测试规范中发生的事情(它在控制台日志中显示得很好):
-
$scope.$apply(console.log(1), controller.config.period = 'test');
->将1
写入控制台;改变CCD_ 7的值。 -
$scope.$apply(console.log(2), controller.config.period = 'test2');
->由于$apply
首次触发tableFilter.config.period
的观察者回调;将2
写入控制台;更改config.period.
的值
在那之后,没有任何东西会第二次触发观察者的回调。
解决方案很简单:在每个expect
语句之前添加对$scope.$digest()
(或$scope.$apply()
)的调用。
相关文章:
- Javascript返回值只在循环中返回一次
- Jquery FadeIn FadeOut 只工作一次
- Javascript html每点击一次就会更改url
- 如何在chrome扩展中存储数据/结果,以及如何使用setTimeout使其只被调用一次
- Rails操作只调用一次,但我在ajax中每秒钟都调用一次
- jQuery滚动功能只工作一次
- 刷新导致我的帖子“;张贴“;再一次
- 引导程序崩溃一次只能看到一个
- 有没有一个抽象层,这样我就可以集成一次,然后使用pusher、pubnub或faye
- 加载器组件仅加载一次
- 根据Angular.JS上一次的内容禁用选择
- 一次又一次地在新的和相同的选项卡中打开一个url
- 只在宽度以下和宽度以上各准备一次
- 如果30秒未单击,请应用CSS一次,将其删除,然后重新迭代
- $watch回调每次在浏览器中触发,但在测试期间只触发一次
- 之前每个测试都会调用每个测试,而不仅仅是一次
- 从命令行一次运行大量摩卡测试的好方法是什么
- 一次测试 2 个以上值的相等性的快速方法
- 让Mocha等待,然后再进行下一次测试
- 如何测试方法是否只被调用了一次,而不是第二次在茉莉