测试单独service - 框架new实例测试
代码如下:
@Injectable()
export class ValueService {
value:string;
constructor() { }
getValue() { return this.value}
}
测试用例如下:
//直接new service 实例
let service: ValueService;
beforeEach(() => { service = new ValueService(); });
it('#getValue should return real value', () => {
expect(service.getValue()).toBe('real value');
});
//or
//直接获取服务实例进行测试,通过调用服务,校验逻辑
let service: ValueService;
beforeEach(() => {
TestBed.configureTestingModule({ providers: [ValueService] }); //等效于useClass
});
it('should use ValueService', () => {
service = TestBed.get(ValueService);
expect(service.getValue()).toBe('real value');
});
测试单独的组件 – 代码new 组件实例
代码如下:
@Component({
selector: 'lightswitch-comp',
template: `
<button (click)="clicked()">Click me!</button>
<span>{{message}}</span>`
})
export class LightswitchComponent {
isOn = false;
clicked() { this.isOn = !this.isOn; }
get message() { return `The light is ${this.isOn ? 'On' : 'Off'}`; }
}
测试代码如下:
//直接new
it('#clicked() should set #message to "is on"', () => {
const comp = new LightswitchComponent();
expect(comp.message).toMatch(/is off/i, 'off at first');
comp.clicked();
expect(comp.message).toMatch(/is on/i, 'on after clicked');
});
//获取组件实例,交给框架创建new
let comp:LightswitchComponent;
beforeEach(() => {
TestBed.configureTestingModule({
// provide the component-under-test and dependent service
providers: [
LightswitchComponent,
]
});
// inject both the component and the dependent service.
comp = TestBed.get(LightswitchComponent);
});
it('#clicked() should set #message to "is on"', () => {
const comp = new LightswitchComponent();
expect(comp.message).toMatch(/is off/i, 'off at first');
comp.clicked();
expect(comp.message).toMatch(/is on/i, 'on after clicked');
});
测试service带有依赖 – mock依赖
利用spy mock依赖
代码如下:
@Injectable()
export class MasterService {
constructor(private valueService: ValueService) { }
getValue() { return this.valueService.getValue(); }
}
测试如下:
let masterService: MasterService;
let valueServiceSpy: jasmine.SpyObj<ValueService>;
beforeEach(() => {
const spy = jasmine.createSpyObj('ValueService', ['getValue']);
TestBed.configureTestingModule({
// Provide both the service-to-test and its (spy) dependency
providers: [
MasterService,
//注入服务,mock提供依赖服务的支持,完成MasterService实例创建
{ provide: ValueService, useValue: spy }
]
});
// Inject both the service-to-test and its (spy) dependency
masterService = TestBed.get(MasterService);
valueServiceSpy = TestBed.get(ValueService);
});
it('#getValue should return stubbed value from a spy', () => {
const stubValue = 'stub value';
valueServiceSpy.getValue.and.returnValue(stubValue);
expect(masterService.getValue())
.toBe(stubValue, 'service returned stub value'); //利用mock依赖返回的值,进行期望判断业务逻辑
});
测试组件带有依赖
WelcomeComponent 依赖于 UserService
export class WelcomeComponent implements OnInit {
welcome: string;
constructor(private userService: UserService) { }
ngOnInit(): void {
this.welcome = this.userService.isLoggedIn ?
'Welcome, ' + this.userService.user.name : 'Please log in.';
}
}
测试代码
class MockUserService {
isLoggedIn = true;
user = { name: 'Test User'};
};
beforeEach(() => {
TestBed.configureTestingModule({
// provide the component-under-test and dependent service
providers: [
WelcomeComponent,
{ provide: UserService, useClass: MockUserService }
]
});
// inject both the component and the dependent service.
comp = TestBed.get(WelcomeComponent);
userService = TestBed.get(UserService);
});
it('should not have welcome message after construction', () => {
expect(comp.welcome).toBeUndefined();
});
it('should welcome logged in user after Angular calls ngOnInit', () => {
comp.ngOnInit();
expect(comp.welcome).toContain(userService.user.name);
});
it('should ask user to log in if not logged in after ngOnInit', () => {
userService.isLoggedIn = false;
comp.ngOnInit();
expect(comp.welcome).not.toContain(userService.user.name);
expect(comp.welcome).toContain('log in');
});
组件dom测试
组件创建测试
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BannerComponent } from './banner.component';
describe('BannerComponent', () => {
let component: BannerComponent;
let fixture: ComponentFixture<BannerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ BannerComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BannerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeDefined();
});
});
页面元素固定
it('should contain "banner works!"', () => {
const bannerElement: HTMLElement = fixture.nativeElement;
expect(bannerElement.textContent).toContain('banner works!');
});
it('should have <p> with "banner works!"', () => {
const bannerElement: HTMLElement = fixture.nativeElement;
const p = bannerElement.querySelector('p');
expect(p.textContent).toEqual('banner works!');
});
//如果querySelector不能使用,
it('should find the <p> with fixture.debugElement.query(By.css)', () => {
const bannerDe: DebugElement = fixture.debugElement;
const paragraphDe = bannerDe.query(By.css('p'));
const p: HTMLElement = paragraphDe.nativeElement;
expect(p.textContent).toEqual('banner works!');
});
页面元素动态修改
//页面元素动态修改,测试
it('should display a different test title', () => {
component.title = 'Test Title';
fixture.detectChanges(); //显示的进行修改检测
expect(h1.textContent).toContain('Test Title');
});
//除去显示声明detectChanges,自动检测
TestBed.configureTestingModule({
declarations: [ BannerComponent ],
providers: [
{ provide: ComponentFixtureAutoDetect, useValue: true }
]
});
异步代码测试
使用fakeAsync
it('should get Date diff correctly in fakeAsync', fakeAsync(() => {
const start = Date.now();
tick(100);
const end = Date.now();
expect(end - start).toBe(100);
}));
ajax请求测试
it('should show quote after getQuote (async)', async(() => {
fixture.detectChanges(); // ngOnInit()
expect(quoteEl.textContent).toBe('...', 'should show placeholder');
fixture.whenStable().then(() => { // wait for async getQuote
fixture.detectChanges(); // update view with quote
expect(quoteEl.textContent).toBe(testQuote);
expect(errorMessage()).toBeNull('should not show error');
});
}));
jasmine done
it('should show quote after getQuote (spy done)', (done: DoneFn) => {
fixture.detectChanges();
// the spy's most recent call returns the observable with the test quote
getQuoteSpy.calls.mostRecent().returnValue.subscribe(() => {
fixture.detectChanges(); // update view with quote
expect(quoteEl.textContent).toBe(testQuote);
expect(errorMessage()).toBeNull('should not show error');
done();
});
});
组件嵌套测试
服务依赖错误
TypeError: ctor is not a constructor
问题原因:provide中错误的配置
//错误的
providers: [{provide: OrderService, useClass: new OrderServiceMock()}]
//正确的
providers: [{provide: OrderService, useValue: new OrderServiceMock()}]
路由测试
全局参数配置–let 全局定义即可
window变量引入
import { Injectable } from '@angular/core';
function _window() : any {
// return the global native browser window object
return window;
}
@Injectable()
export class WindowRef {
get nativeWindow() : any {
return _window();
}
}
import { WindowRef } from './WindowRef';
@NgModule({
providers: [ WindowRef ]
})
export class AppModule{}
import { WindowRef } from './WindowRef';
@Component({...})
class MyComponent {
constructor(private winRef: WindowRef) {
// getting the native window obj
console.log('Native window obj', winRef.nativeWindow);
}
}
参考文献
window 变量
本文作者:前端首席体验师(CheongHu)
版权声明: 本文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦