手记

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)="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 许可协议。转载请注明出处!

0人推荐
随时随地看视频
慕课网APP