如何使用 Jasmine 对 angular2 的 canActivate 保护方法进行单元测试?
How to unit-test canActivate guard method of angular2 using Jasmine?
很抱歉提出此类问题。但是我找不到任何关于编写 canActivate 保护文件测试的博客或 youtube 教程。官方文档中也没有提到任何内容。
任何帮助将不胜感激。
因为没有人回答我的问题,所以我粘贴我的代码片段以供参考,以帮助可能遇到这种情况的人。
sampleLoggedIn.guard.ts
import {Injectable} from '@angular/core';
import {Router, CanActivate} from '@angular/router';
import {StorageService} from '../storage.service';
@Injectable()
export class LoggedInGuard implements CanActivate {
constructor(private router: Router, private storageService: StorageService) {
}
/**Overriding canActivate to guard routes
*
* This method returns true if the user is not logged in
* @returns {boolean}
*/
canActivate() {
if (this.storageService.isLoggedIn) {
return true;
} else {
this.router.navigate(['home']);
return false;
}
}
}
sampleLoggedIn.guard.spec.ts
import {TestBed, async} from '@angular/core/testing';
import {FormsModule} from '@angular/forms';
import {HttpModule} from '@angular/http';
import {CommonModule} from '@angular/common';
import 'rxjs/Rx';
import 'rxjs/add/observable/throw';
import {Router} from '@angular/router';
import 'rxjs/add/operator/map';
import {LoggedInGuard} from './loggedin.guard';
import {StorageService} from '../storage.service';
import {CookieService} from 'angular2-cookie/core';
describe('Logged in guard should', () => {
let loggedInGuard: LoggedInGuard;
let storageService: StorageService;
let router = {
navigate: jasmine.createSpy('navigate')
};
// async beforeEach
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule, CommonModule, HttpModule],
providers: [LoggedInGuard, StorageService, CookieService,
{provide: Router, useValue: router}
]
})
.compileComponents(); // compile template and css
}));
// synchronous beforeEach
beforeEach(() => {
loggedInGuard = TestBed.get(LoggedInGuard);
storageService = TestBed.get(StorageService);
});
it('be able to hit route when user is logged in', () => {
storageService.isLoggedIn = true;
expect(loggedInGuard.canActivate()).toBe(true);
});
it('not be able to hit route when user is not logged in', () => {
storageService.isLoggedIn = false;
expect(loggedInGuard.canActivate()).toBe(false);
});
});
这个问题已经很老了——但由于我现在正试图自己寻找一些详细的单元测试文档,所以我只想把我的方法放在这里。一般来说,如果我的守卫/服务/组件/任何我认为这些都应该被模拟而不是真正的服务应该被使用。由于这些服务不是我们想要在守卫的单元测试中测试的 - 我们只想测试守卫。
所以这是一个通用示例,我将如何为返回可观察对象的守卫做这件事:
import { MyGuard } from './path/to/your/guard';
import { TestBed } from '@angular/core/testing';
import { finalize } from 'rxjs/operators';
describe('MyGuard Test', () => {
const createMockRoute = (id: string) => {
return {
params: { id: id }
} as any;
};
const createMockRouteState = () => null;
let guard: MyGuard;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
MyGuard,
]
});
guard = TestBed.get(MyGuard);
});
it('should not be able to activate invalid route', done => {
const route = createMockRoute(null);
const state = createMockRouteState();
const res$ = guard.canActivate(route, state);
res$.pipe(finalize(done)).subscribe(res => expect(res).toBeFalsy());
});
});
这就是我在你的具体情况下会做的(应该与 angular 6 一起使用,canActivate 也应该采用 2 个参数):
import { LoggedInGuard } from './loggedin.guard';
import { TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { StorageService } from '../storage.service';
describe('LoggedInGuard', () => {
let guard: LoggedInGuard;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
LoggedInGuard,
{ provide: Router, useClass: { navigate: () => null } },
{ provide: StorageService, useClass: { } }
]
});
guard = TestBed.get(LoggedInGuard);
});
it('should not be able to activate when logged out', () => {
const storageService = TestBed.get(StorageService);
storageService.isLoggedIn = false;
const res = guard.canActivate(null, null);
expect(res).toBeFalsy();
});
it('should be able to activate when logged in', () => {
const storageService = TestBed.get(StorageService);
storageService.isLoggedIn = true;
const res = guard.canActivate(null, null);
expect(res).toBeTruthy();
});
});
如果你的Guard是异步的,可以用异步测试来测试:
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed, waitForAsync } from '@angular/core/testing';
import { Observable, of } from 'rxjs';
describe('MyGuard', () => {
let guard: MyGuard;
let service: MyAsyncService;
// async beforeEach
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
],
providers: [
MyGuard,
MyAsyncService,
],
});
}));
// synchronous beforeEach
beforeEach(() => {
guard = TestBed.inject(MyGuard);
service = TestBed.inject(MyAsyncService);
});
it('should allow if service reports as allowed', (done) => {
service.canFoo = (): Observable<boolean> => of(true);
guard.canActivate(null, null).subscribe({
next: (allowed: boolean) => {
expect(allowed).toBeTrue();
done();
},
error: err => {
fail(err);
},
});
});
it('should reject if service reports as not allowed', () => {
service.canFoo = (): Observable<boolean> => of(false);
guard.canActivate(null, null).subscribe({
next: (allowed: boolean) => {
expect(allowed).toBeFalse();
done();
},
error: err => {
fail(err);
},
});
});
});
很抱歉提出此类问题。但是我找不到任何关于编写 canActivate 保护文件测试的博客或 youtube 教程。官方文档中也没有提到任何内容。
任何帮助将不胜感激。
因为没有人回答我的问题,所以我粘贴我的代码片段以供参考,以帮助可能遇到这种情况的人。
sampleLoggedIn.guard.ts
import {Injectable} from '@angular/core';
import {Router, CanActivate} from '@angular/router';
import {StorageService} from '../storage.service';
@Injectable()
export class LoggedInGuard implements CanActivate {
constructor(private router: Router, private storageService: StorageService) {
}
/**Overriding canActivate to guard routes
*
* This method returns true if the user is not logged in
* @returns {boolean}
*/
canActivate() {
if (this.storageService.isLoggedIn) {
return true;
} else {
this.router.navigate(['home']);
return false;
}
}
}
sampleLoggedIn.guard.spec.ts
import {TestBed, async} from '@angular/core/testing';
import {FormsModule} from '@angular/forms';
import {HttpModule} from '@angular/http';
import {CommonModule} from '@angular/common';
import 'rxjs/Rx';
import 'rxjs/add/observable/throw';
import {Router} from '@angular/router';
import 'rxjs/add/operator/map';
import {LoggedInGuard} from './loggedin.guard';
import {StorageService} from '../storage.service';
import {CookieService} from 'angular2-cookie/core';
describe('Logged in guard should', () => {
let loggedInGuard: LoggedInGuard;
let storageService: StorageService;
let router = {
navigate: jasmine.createSpy('navigate')
};
// async beforeEach
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FormsModule, CommonModule, HttpModule],
providers: [LoggedInGuard, StorageService, CookieService,
{provide: Router, useValue: router}
]
})
.compileComponents(); // compile template and css
}));
// synchronous beforeEach
beforeEach(() => {
loggedInGuard = TestBed.get(LoggedInGuard);
storageService = TestBed.get(StorageService);
});
it('be able to hit route when user is logged in', () => {
storageService.isLoggedIn = true;
expect(loggedInGuard.canActivate()).toBe(true);
});
it('not be able to hit route when user is not logged in', () => {
storageService.isLoggedIn = false;
expect(loggedInGuard.canActivate()).toBe(false);
});
});
这个问题已经很老了——但由于我现在正试图自己寻找一些详细的单元测试文档,所以我只想把我的方法放在这里。一般来说,如果我的守卫/服务/组件/任何我认为这些都应该被模拟而不是真正的服务应该被使用。由于这些服务不是我们想要在守卫的单元测试中测试的 - 我们只想测试守卫。 所以这是一个通用示例,我将如何为返回可观察对象的守卫做这件事:
import { MyGuard } from './path/to/your/guard';
import { TestBed } from '@angular/core/testing';
import { finalize } from 'rxjs/operators';
describe('MyGuard Test', () => {
const createMockRoute = (id: string) => {
return {
params: { id: id }
} as any;
};
const createMockRouteState = () => null;
let guard: MyGuard;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
MyGuard,
]
});
guard = TestBed.get(MyGuard);
});
it('should not be able to activate invalid route', done => {
const route = createMockRoute(null);
const state = createMockRouteState();
const res$ = guard.canActivate(route, state);
res$.pipe(finalize(done)).subscribe(res => expect(res).toBeFalsy());
});
});
这就是我在你的具体情况下会做的(应该与 angular 6 一起使用,canActivate 也应该采用 2 个参数):
import { LoggedInGuard } from './loggedin.guard';
import { TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { StorageService } from '../storage.service';
describe('LoggedInGuard', () => {
let guard: LoggedInGuard;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
LoggedInGuard,
{ provide: Router, useClass: { navigate: () => null } },
{ provide: StorageService, useClass: { } }
]
});
guard = TestBed.get(LoggedInGuard);
});
it('should not be able to activate when logged out', () => {
const storageService = TestBed.get(StorageService);
storageService.isLoggedIn = false;
const res = guard.canActivate(null, null);
expect(res).toBeFalsy();
});
it('should be able to activate when logged in', () => {
const storageService = TestBed.get(StorageService);
storageService.isLoggedIn = true;
const res = guard.canActivate(null, null);
expect(res).toBeTruthy();
});
});
如果你的Guard是异步的,可以用异步测试来测试:
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed, waitForAsync } from '@angular/core/testing';
import { Observable, of } from 'rxjs';
describe('MyGuard', () => {
let guard: MyGuard;
let service: MyAsyncService;
// async beforeEach
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
],
providers: [
MyGuard,
MyAsyncService,
],
});
}));
// synchronous beforeEach
beforeEach(() => {
guard = TestBed.inject(MyGuard);
service = TestBed.inject(MyAsyncService);
});
it('should allow if service reports as allowed', (done) => {
service.canFoo = (): Observable<boolean> => of(true);
guard.canActivate(null, null).subscribe({
next: (allowed: boolean) => {
expect(allowed).toBeTrue();
done();
},
error: err => {
fail(err);
},
});
});
it('should reject if service reports as not allowed', () => {
service.canFoo = (): Observable<boolean> => of(false);
guard.canActivate(null, null).subscribe({
next: (allowed: boolean) => {
expect(allowed).toBeFalse();
done();
},
error: err => {
fail(err);
},
});
});
});