我有一个接口,意图根据输入参数对象操作DOM元素。我想先写单元测试。我们的想法是使用Angular中提供的Render2。
export interface ModifyDomTree {
modify(data: SomeData) : ElementRef;
}
这是实现(不知道这将如何出现),但为它编写测试。
export class ModifyDomTreeImpl implements ModifyDomTree {
constructor(private render: Renderer2) {
}
modify(data: SomeData) : ElementRef{
return null;
}
}
在测试中我不想使用Renderer2的模拟我想使用Angular将使用的实际Renderer2。如何在测试中注入或实例化实际的Angular Render2?
规范将是
describe('ModifyDomTreeImpl', () => {
let data: SomeData;
let modifyDomTree: ModifyDomTree;
beforeEach(() => {
//let render: Renderer2 = mock(Renderer2); ?? How Do I inject the real Angular thing here
modifyDomTree = new ModifyDomTreeImpl(render);
});
it('should convert a data into a text node', () => {
data = mock(SomeData);
when(data.value).thenReturn('Groot!!');
const result: ElementRef = modifyDomTree.convert(data);
expect(result).toBeDefined();
expect(result).not.toBeNull();
expect(result.nativeElement).toBeDefined();
expect(result.nativeElement).toBeDefined();
expect(result.nativeElement.childNodes).toBeDefined();
expect(result.nativeElement.childNodes.length).toEqual(1);
expect(result.nativeElement.childNodes.length[0]).toEqual('text');
expect(result.nativeElement.childNodes.length[0].data).toEqual('Groot!!');
});
});
用角度声音直接操作DOM元素是一个非常糟糕的主意。我首先尝试理解我真正想要实现的目标,并且如果有更好的方法来实现它,那么动态修改dom。角度的基础是直接避免DOM操作
我理解这可能不容易理解......所以,你必须考虑实现中的组件。在这种情况下,您有一些案例和一些适合这些案例的组件(或者有一些选项)。 div / css中提供了所有选项。但是仅在需要时启用/可见(模板中的ngIf条件)。想想你已经实现了所有可用的选项,只有自定义。在这种情况下,这并不禁止实现DOM操作(通过角度)仅用于在页面创建/或服务更新时创建不同类型的组件:
例:
import { Component, OnInit , ComponentFactoryResolver, ViewContainerRef, ViewChild} from '@angular/core';
import { NicolabelComponent } from './nicolabel/nicolabel.component';
@Component({
selector: 'app-nicoview',
templateUrl: '
<button (click)="addNicoLabel()">Add </button>
<div #mydiv style="width:100px;height:200px">;
</div>
',
styles: []
})
export class NicoviewComponent implements OnInit {
@ViewChild('mydiv', {read: ViewContainerRef}) mydiv;
labels: NicolabelComponent[] = [];
ngOnInit() {
}
// `ViewContainerRef` from the component itself
constructor(private viewContainerRef:ViewContainerRef, private componentFactoryResolver: ComponentFactoryResolver) {}
labelClicked(label)
{
console.log("click : ");
console.log(label.text);
}
addNicoLabel()
{
console.log("clicked");
// to be created dynamicalaly this component must be declared in app.module.ts as an entryComponents
let factory = this.componentFactoryResolver.resolveComponentFactory(NicolabelComponent);
let created_component = this.mydiv.createComponent(factory);
// this.labels.push(created_component );
}
}
这应该与Angular 6+一起使用
在component.spec.ts中,使用提供程序注入渲染器。然后,您可以检索并监视它以确认测试。
let renderer2: Renderer2;
...
beforeEach(async( () => {
TestBed.configureTestingModule({
...
providers: [Renderer2] // This is the angular renderer
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(YourComponent);
// grab the renderer
renderer2 = fixture.componentRef.injector.get<Renderer2>(Renderer2 as Type<Renderer2>);
// and spy on it
spyOn(renderer2, 'addClass').and.callThrough();
// or replace
spyOn(renderer2, 'addClass').and.callFake(..);
// etc
});
it('should call renderer', () => {
expect(renderer2.addClass).toHaveBeenCalledWith(jasmine.any(Object), 'css-class');
});