Angular測試用例編寫總結

測試單獨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 me!/<button>
{{message}}`
})
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依賴返回的值,進行期望判斷業務邏輯
});
/<valueservice>

測試組件帶有依賴

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();
});

});
/<bannercomponent>

頁面元素固定

 it('should contain "banner works!"', () => {
const bannerElement: HTMLElement = fixture.nativeElement;
expect(bannerElement.textContent).toContain('banner works!');
});
it('should have

with "banner works!"', () => {
const bannerElement: HTMLElement = fixture.nativeElement;
const p = bannerElement.querySelector('p');
expect(p.textContent).toEqual('banner works!');
});
//如果querySelector不能使用,
it('should find the

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);
}
}

參考文獻

本文作者:前端首席體驗師(CheongHu)

聯繫郵箱:[email protected]


分享到:


相關文章: