I'm having some trouble unit testing the router in my application, which is built on the Angular ui router. What I want to test is whether state transitions change the URL appropriately (there will be more complicated tests later, but this is where I'm starting.)
在我的应用程序中,我有一些问题单元测试路由器,它构建在有棱角的ui路由器上。我想测试的是状态转换是否适当地更改了URL(稍后将有更复杂的测试,但这正是我要开始的地方)。
Here is the relevant portion of my application code:
以下是我的申请代码的相关部分:
angular.module('scrapbooks')
.config( function($stateProvider){
$stateProvider.state('splash', {
url: "/splash/",
templateUrl: "/app/splash/splash.tpl.html",
controller: "SplashCtrl"
})
})
And the testing code:
测试代码:
it("should change to the splash state", function(){
inject(function($state, $rootScope){
$rootScope.$apply(function(){
$state.go("splash");
});
expect($state.current.name).to.equal("splash");
})
})
Similar questions on * (and the official ui router test code) suggest wrapping the $state.go call in $apply should be enough. But I've done that and the state is still not updating. $state.current.name remains empty.
关于*(以及官方的ui路由器测试代码)的类似问题建议包装$state。去申请$应用应该足够了。但我已经做了,状态仍然没有更新。state.current.name美元仍然是空的。
6 个解决方案
#1
124
Been having this issue as well, and finally figured out how to do it.
我也遇到了这个问题,最后找到了解决办法。
Here is a sample state:
下面是一个示例状态:
angular.module('myApp', ['ui.router'])
.config(['$stateProvider', function($stateProvider) {
$stateProvider.state('myState', {
url: '/state/:id',
templateUrl: 'template.html',
controller: 'MyCtrl',
resolve: {
data: ['myService', function(service) {
return service.findAll();
}]
}
});
}]);
The unit test below will cover testing the URL w/ params, and executing the resolves which inject its own dependencies:
下面的单元测试将包含对URL w/ params的测试,并执行注入自己依赖项的解析:
describe('myApp/myState', function() {
var $rootScope, $state, $injector, myServiceMock, state = 'myState';
beforeEach(function() {
module('myApp', function($provide) {
$provide.value('myService', myServiceMock = {});
});
inject(function(_$rootScope_, _$state_, _$injector_, $templateCache) {
$rootScope = _$rootScope_;
$state = _$state_;
$injector = _$injector_;
// We need add the template entry into the templateCache if we ever
// specify a templateUrl
$templateCache.put('template.html', '');
})
});
it('should respond to URL', function() {
expect($state.href(state, { id: 1 })).toEqual('#/state/1');
});
it('should resolve data', function() {
myServiceMock.findAll = jasmine.createSpy('findAll').and.returnValue('findAll');
// earlier than jasmine 2.0, replace "and.returnValue" with "andReturn"
$state.go(state);
$rootScope.$digest();
expect($state.current.name).toBe(state);
// Call invoke to inject dependencies and run function
expect($injector.invoke($state.current.resolve.data)).toBe('findAll');
});
});
#2
18
If you want to check only the current state's name it's easier to use $state.transitionTo('splash')
如果您只想检查当前状态的名称,那么使用$state. transitionto ('splash')会更简单
it('should transition to splash', inject(function($state,$rootScope){
$state.transitionTo('splash');
$rootScope.$apply();
expect($state.current.name).toBe('splash');
}));
#3
15
I realize this is slightly off topic, but I came here from Google looking for a simple way to test a route's template, controller, and URL.
我意识到这有点偏离主题,但是我从谷歌来到这里寻找一种简单的方法来测试路由的模板、控制器和URL。
$state.get('stateName')
will give you
会给你
{
url: '...',
templateUrl: '...',
controller: '...',
name: 'stateName',
resolve: {
foo: function () {}
}
}
in your tests.
在你的测试。
So your tests could look something like this:
你的测试可以是这样的:
var state;
beforeEach(inject(function ($state) {
state = $state.get('otherwise');
}));
it('matches a wild card', function () {
expect(state.url).toEqual('/path/to/page');
});
it('renders the 404 page', function () {
expect(state.templateUrl).toEqual('views/errors/404.html');
});
it('uses the right controller', function () {
expect(state.controller).toEqual(...);
});
it('resolves the right thing', function () {
expect(state.resolve.foo()).toEqual(...);
});
// etc
#4
1
For a state
that without resolve
:
对于一个没有决心的国家:
// TEST DESCRIPTION
describe('UI ROUTER', function () {
// TEST SPECIFICATION
it('should go to the state', function () {
module('app');
inject(function ($rootScope, $state, $templateCache) {
// When you transition to the state with $state, UI-ROUTER
// will look for the 'templateUrl' mentioned in the state's
// configuration, so supply those templateUrls with templateCache
$templateCache.put('app/templates/someTemplate.html');
// Now GO to the state.
$state.go('someState');
// Run a digest cycle to update the $state object
// you can also run it with $state.$digest();
$state.$apply();
// TEST EXPECTATION
expect($state.current.name)
.toBe('someState');
});
});
});
NOTE:-
注意:-
For a nested state we may need to supply more than one template. For ex. if we have a nested state core.public.home
and each state
, i.e. core
, core.public
and core.public.home
has a templateUrl
defined, we will have to add $templateCache.put()
for each state's templateUrl
key:-
对于嵌套状态,我们可能需要提供多个模板。对于exp,如果我们有一个嵌套的状态core.public。家庭和每个州,即核心,核心。公共和core.public。home有一个templateUrl定义,我们需要为每个州的templateUrl键添加$templateCache.put()。
$templateCache.put('app/templates/template1.html'); $templateCache.put('app/templates/template2.html'); $templateCache.put('app/templates/template3.html');
美元templateCache.put(app /模板/ template1.html);美元templateCache.put(app /模板/ template2.html);美元templateCache.put(app /模板/ template3.html);
Hope this helps. Good Luck.
希望这个有帮助。祝你好运。
#5
1
You could use $state.$current.locals.globals
to access all resolved values (see the code snippet).
您可以使用状态。current.locals美元。访问所有已解析值的全局变量(参见代码片段)。
// Given
$httpBackend
.expectGET('/api/users/123')
.respond(200, { id: 1, email: 'test@email.com');
// When
$state.go('users.show', { id: 123 });
$httpBackend.flush();
// Then
var user = $state.$current.locals.globals['user']
expact(user).to.have.property('id', 123);
expact(user).to.have.property('email', 'test@email.com');
In ui-router 1.0.0 (currently beta) you could try to invoke $resolve.resolve(state, locals).then((resolved) => {})
in the specs. For instance https://github.com/lucassus/angular-webpack-seed/blob/9a5af271439fd447510c0e3e87332959cb0eda0f/src/app/contacts/one/one.state.spec.js#L29
在ui-router 1.0.0(当前测试版)中,您可以尝试调用$resolve。解析(状态,局部)然后(解析)=>{}在规范中。例如https://github.com/lucassus/angular-webpack-seed/blob/9a5af271439fd447510c0e3e87332959cb0eda0f/src/app/contacts/one/one.state.spec.js气密性好
#6
1
If you're not interested in anything in the content of the template, you could just mock $templateCache:
如果您对模板内容不感兴趣,可以模拟$templateCache:
beforeEach(inject(function($templateCache) {
spyOn($templateCache,'get').and.returnValue('<div></div>');
}
#1
124
Been having this issue as well, and finally figured out how to do it.
我也遇到了这个问题,最后找到了解决办法。
Here is a sample state:
下面是一个示例状态:
angular.module('myApp', ['ui.router'])
.config(['$stateProvider', function($stateProvider) {
$stateProvider.state('myState', {
url: '/state/:id',
templateUrl: 'template.html',
controller: 'MyCtrl',
resolve: {
data: ['myService', function(service) {
return service.findAll();
}]
}
});
}]);
The unit test below will cover testing the URL w/ params, and executing the resolves which inject its own dependencies:
下面的单元测试将包含对URL w/ params的测试,并执行注入自己依赖项的解析:
describe('myApp/myState', function() {
var $rootScope, $state, $injector, myServiceMock, state = 'myState';
beforeEach(function() {
module('myApp', function($provide) {
$provide.value('myService', myServiceMock = {});
});
inject(function(_$rootScope_, _$state_, _$injector_, $templateCache) {
$rootScope = _$rootScope_;
$state = _$state_;
$injector = _$injector_;
// We need add the template entry into the templateCache if we ever
// specify a templateUrl
$templateCache.put('template.html', '');
})
});
it('should respond to URL', function() {
expect($state.href(state, { id: 1 })).toEqual('#/state/1');
});
it('should resolve data', function() {
myServiceMock.findAll = jasmine.createSpy('findAll').and.returnValue('findAll');
// earlier than jasmine 2.0, replace "and.returnValue" with "andReturn"
$state.go(state);
$rootScope.$digest();
expect($state.current.name).toBe(state);
// Call invoke to inject dependencies and run function
expect($injector.invoke($state.current.resolve.data)).toBe('findAll');
});
});
#2
18
If you want to check only the current state's name it's easier to use $state.transitionTo('splash')
如果您只想检查当前状态的名称,那么使用$state. transitionto ('splash')会更简单
it('should transition to splash', inject(function($state,$rootScope){
$state.transitionTo('splash');
$rootScope.$apply();
expect($state.current.name).toBe('splash');
}));
#3
15
I realize this is slightly off topic, but I came here from Google looking for a simple way to test a route's template, controller, and URL.
我意识到这有点偏离主题,但是我从谷歌来到这里寻找一种简单的方法来测试路由的模板、控制器和URL。
$state.get('stateName')
will give you
会给你
{
url: '...',
templateUrl: '...',
controller: '...',
name: 'stateName',
resolve: {
foo: function () {}
}
}
in your tests.
在你的测试。
So your tests could look something like this:
你的测试可以是这样的:
var state;
beforeEach(inject(function ($state) {
state = $state.get('otherwise');
}));
it('matches a wild card', function () {
expect(state.url).toEqual('/path/to/page');
});
it('renders the 404 page', function () {
expect(state.templateUrl).toEqual('views/errors/404.html');
});
it('uses the right controller', function () {
expect(state.controller).toEqual(...);
});
it('resolves the right thing', function () {
expect(state.resolve.foo()).toEqual(...);
});
// etc
#4
1
For a state
that without resolve
:
对于一个没有决心的国家:
// TEST DESCRIPTION
describe('UI ROUTER', function () {
// TEST SPECIFICATION
it('should go to the state', function () {
module('app');
inject(function ($rootScope, $state, $templateCache) {
// When you transition to the state with $state, UI-ROUTER
// will look for the 'templateUrl' mentioned in the state's
// configuration, so supply those templateUrls with templateCache
$templateCache.put('app/templates/someTemplate.html');
// Now GO to the state.
$state.go('someState');
// Run a digest cycle to update the $state object
// you can also run it with $state.$digest();
$state.$apply();
// TEST EXPECTATION
expect($state.current.name)
.toBe('someState');
});
});
});
NOTE:-
注意:-
For a nested state we may need to supply more than one template. For ex. if we have a nested state core.public.home
and each state
, i.e. core
, core.public
and core.public.home
has a templateUrl
defined, we will have to add $templateCache.put()
for each state's templateUrl
key:-
对于嵌套状态,我们可能需要提供多个模板。对于exp,如果我们有一个嵌套的状态core.public。家庭和每个州,即核心,核心。公共和core.public。home有一个templateUrl定义,我们需要为每个州的templateUrl键添加$templateCache.put()。
$templateCache.put('app/templates/template1.html'); $templateCache.put('app/templates/template2.html'); $templateCache.put('app/templates/template3.html');
美元templateCache.put(app /模板/ template1.html);美元templateCache.put(app /模板/ template2.html);美元templateCache.put(app /模板/ template3.html);
Hope this helps. Good Luck.
希望这个有帮助。祝你好运。
#5
1
You could use $state.$current.locals.globals
to access all resolved values (see the code snippet).
您可以使用状态。current.locals美元。访问所有已解析值的全局变量(参见代码片段)。
// Given
$httpBackend
.expectGET('/api/users/123')
.respond(200, { id: 1, email: 'test@email.com');
// When
$state.go('users.show', { id: 123 });
$httpBackend.flush();
// Then
var user = $state.$current.locals.globals['user']
expact(user).to.have.property('id', 123);
expact(user).to.have.property('email', 'test@email.com');
In ui-router 1.0.0 (currently beta) you could try to invoke $resolve.resolve(state, locals).then((resolved) => {})
in the specs. For instance https://github.com/lucassus/angular-webpack-seed/blob/9a5af271439fd447510c0e3e87332959cb0eda0f/src/app/contacts/one/one.state.spec.js#L29
在ui-router 1.0.0(当前测试版)中,您可以尝试调用$resolve。解析(状态,局部)然后(解析)=>{}在规范中。例如https://github.com/lucassus/angular-webpack-seed/blob/9a5af271439fd447510c0e3e87332959cb0eda0f/src/app/contacts/one/one.state.spec.js气密性好
#6
1
If you're not interested in anything in the content of the template, you could just mock $templateCache:
如果您对模板内容不感兴趣,可以模拟$templateCache:
beforeEach(inject(function($templateCache) {
spyOn($templateCache,'get').and.returnValue('<div></div>');
}