测试将 class 添加到 angular 中的 <body> 7
Test adding class to the <body> in angular 7
我有这个根 AppComponent
监听服务的变化,然后在 document.body
[=22= 上添加或删除 CSS class ]
import { Component, OnInit, Renderer2 } from '@angular/core';
import { SideMenuService } from './core/side-menu/side-menu.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
static readonly sideMenuClass: string = 'side-menu-open';
constructor(public sideMenuService: SideMenuService, private renderer2: Renderer2) { }
ngOnInit(): void {
this.sideMenuService.isOpenChange.subscribe((value: boolean) => {
if (value) {
this.renderer2.addClass(document.body, AppComponent.sideMenuClass);
} else {
this.renderer2.removeClass(document.body, AppComponent.sideMenuClass);
}
});
}
}
然后我在我的 *.spec.ts
文件中有这个,其中大部分是我阅读
import { TestBed, async, ComponentFixture, tick } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { CoreModule } from './core/core.module';
import { AppComponent } from './app.component';
import { Renderer2, Type } from '@angular/core';
describe('AppComponent', () => {
let fixture: ComponentFixture<AppComponent>;
let app: AppComponent;
let renderer2: Renderer2;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule,
CoreModule
],
declarations: [
AppComponent
],
providers: [Renderer2]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
app = fixture.debugElement.componentInstance;
//Spy on the renderer
renderer2 = fixture.componentRef.injector.get<Renderer2>(Renderer2 as Type<Renderer2>);
spyOn(renderer2, 'addClass').and.callThrough();
});
it(`should toggle a class on the <body> tag when opening/closing the side-menu via the side-menu service`, () => {
app.sideMenuService.open();
fixture.detectChanges();
console.log(fixture.debugElement.nativeElement, document.body)
expect(renderer2.addClass).toHaveBeenCalledWith(jasmine.any(Object), AppComponent.sideMenuClass);
});
});
但是,现在它给了我错误信息
Expected spy addClass to have been called with [ , 'side-menu-open' ] but it was never called.
我需要做什么才能正确测试此组件?我在这里走对了吗?
编辑:
这是side-menu.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SideMenuService {
isOpen: boolean = false;
isOpenChange: Subject<boolean> = new Subject<boolean>();
constructor() {
this.isOpenChange.subscribe((value: boolean) => {
this.isOpen = value;
});
}
open(): void {
this.isOpenChange.next(true);
}
close(): void {
this.isOpenChange.next(false);
}
}
我最终没有使用渲染器并注入了一个我可以在测试中模拟的 window 服务。
在我的 app.module.ts
我有
@NgModule({
declarations: [
AppComponent
],
imports: [ ... ],
providers: [
{provide: 'Window', useValue: window},
...
],
bootstrap: [AppComponent]
})
export class AppModule { }
在app.component.ts
中:
import { Component, OnInit, Inject } from '@angular/core';
import { SideMenuService } from './services/side-menu/side-menu.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
static readonly sideMenuClass: string = 'side-menu-open';
constructor(@Inject('Window') private window: Window, public sideMenuService: SideMenuService) { }
ngOnInit(): void {
this.sideMenuService.isOpen$.subscribe((value: boolean) => {
if (value) {
this.window.document.body.classList.add(AppComponent.sideMenuClass);
} else {
this.window.document.body.classList.remove(AppComponent.sideMenuClass);
}
});
}
}
神奇的是,我发现 karma-viewport
项目添加了业力测试的能力来修改测试 window(例如响应式屏幕尺寸等)
https://github.com/squidfunk/karma-viewport
添加这个允许我在 app.component.spec.ts
中使用 karma testig iframe 作为 window
并且测试通过!
describe('Component: App', () => {
let fixture: ComponentFixture<AppComponent>;
let app: AppComponent;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ ... ],
declarations: [
AppComponent
],
providers: [
{provide: 'Window', useValue: viewport.context.contentWindow},
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
app = fixture.debugElement.componentInstance;
});
it(`should toggle a class on the <body> tag when opening/closing the side-menu via the side-menu service`, () => {
if (viewport.context && viewport.context.contentDocument) {
const testBody = viewport.context.contentDocument.body;
fixture.detectChanges(); //do this initially to trigger `ngOnInit()`
app.sideMenuService.open();
fixture.detectChanges();
expect(testBody.className).toContain(AppComponent.sideMenuClass);
app.sideMenuService.close();
fixture.detectChanges();
expect(testBody.className).not.toContain(AppComponent.sideMenuClass);
} else {
fail('Could not locate the karma testing iframe document!');
}
});
});
我有这个根 AppComponent
监听服务的变化,然后在 document.body
[=22= 上添加或删除 CSS class ]
import { Component, OnInit, Renderer2 } from '@angular/core';
import { SideMenuService } from './core/side-menu/side-menu.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
static readonly sideMenuClass: string = 'side-menu-open';
constructor(public sideMenuService: SideMenuService, private renderer2: Renderer2) { }
ngOnInit(): void {
this.sideMenuService.isOpenChange.subscribe((value: boolean) => {
if (value) {
this.renderer2.addClass(document.body, AppComponent.sideMenuClass);
} else {
this.renderer2.removeClass(document.body, AppComponent.sideMenuClass);
}
});
}
}
然后我在我的 *.spec.ts
文件中有这个,其中大部分是我阅读
import { TestBed, async, ComponentFixture, tick } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { CoreModule } from './core/core.module';
import { AppComponent } from './app.component';
import { Renderer2, Type } from '@angular/core';
describe('AppComponent', () => {
let fixture: ComponentFixture<AppComponent>;
let app: AppComponent;
let renderer2: Renderer2;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule,
CoreModule
],
declarations: [
AppComponent
],
providers: [Renderer2]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
app = fixture.debugElement.componentInstance;
//Spy on the renderer
renderer2 = fixture.componentRef.injector.get<Renderer2>(Renderer2 as Type<Renderer2>);
spyOn(renderer2, 'addClass').and.callThrough();
});
it(`should toggle a class on the <body> tag when opening/closing the side-menu via the side-menu service`, () => {
app.sideMenuService.open();
fixture.detectChanges();
console.log(fixture.debugElement.nativeElement, document.body)
expect(renderer2.addClass).toHaveBeenCalledWith(jasmine.any(Object), AppComponent.sideMenuClass);
});
});
但是,现在它给了我错误信息
Expected spy addClass to have been called with [ , 'side-menu-open' ] but it was never called.
我需要做什么才能正确测试此组件?我在这里走对了吗?
编辑:
这是side-menu.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SideMenuService {
isOpen: boolean = false;
isOpenChange: Subject<boolean> = new Subject<boolean>();
constructor() {
this.isOpenChange.subscribe((value: boolean) => {
this.isOpen = value;
});
}
open(): void {
this.isOpenChange.next(true);
}
close(): void {
this.isOpenChange.next(false);
}
}
我最终没有使用渲染器并注入了一个我可以在测试中模拟的 window 服务。
在我的 app.module.ts
我有
@NgModule({
declarations: [
AppComponent
],
imports: [ ... ],
providers: [
{provide: 'Window', useValue: window},
...
],
bootstrap: [AppComponent]
})
export class AppModule { }
在app.component.ts
中:
import { Component, OnInit, Inject } from '@angular/core';
import { SideMenuService } from './services/side-menu/side-menu.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
static readonly sideMenuClass: string = 'side-menu-open';
constructor(@Inject('Window') private window: Window, public sideMenuService: SideMenuService) { }
ngOnInit(): void {
this.sideMenuService.isOpen$.subscribe((value: boolean) => {
if (value) {
this.window.document.body.classList.add(AppComponent.sideMenuClass);
} else {
this.window.document.body.classList.remove(AppComponent.sideMenuClass);
}
});
}
}
神奇的是,我发现 karma-viewport
项目添加了业力测试的能力来修改测试 window(例如响应式屏幕尺寸等)
https://github.com/squidfunk/karma-viewport
添加这个允许我在 app.component.spec.ts
中使用 karma testig iframe 作为 window
并且测试通过!
describe('Component: App', () => {
let fixture: ComponentFixture<AppComponent>;
let app: AppComponent;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ ... ],
declarations: [
AppComponent
],
providers: [
{provide: 'Window', useValue: viewport.context.contentWindow},
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AppComponent);
app = fixture.debugElement.componentInstance;
});
it(`should toggle a class on the <body> tag when opening/closing the side-menu via the side-menu service`, () => {
if (viewport.context && viewport.context.contentDocument) {
const testBody = viewport.context.contentDocument.body;
fixture.detectChanges(); //do this initially to trigger `ngOnInit()`
app.sideMenuService.open();
fixture.detectChanges();
expect(testBody.className).toContain(AppComponent.sideMenuClass);
app.sideMenuService.close();
fixture.detectChanges();
expect(testBody.className).not.toContain(AppComponent.sideMenuClass);
} else {
fail('Could not locate the karma testing iframe document!');
}
});
});