如何使用 ng-template 测试模态元素以及触发它的操作?

Fot*_*los 5 unit-testing karma-jasmine angular7

我阅读了Angular 测试,但不确定是否有任何关于在模态中测试元素以及如何检查自定义操作的参考。我的目的是编写必要的测试表明我将确保我的函数和模态按预期工作。

由于模态被隐藏,检查模态元素是否出现的测试失败。所以我想这里缺少一些东西。

这是我的photos.components.ts文件:

import {Component, OnInit, ViewEncapsulation} from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-photos',
  templateUrl: './photos.component.html',
  styleUrls: ['./photos.component.scss'],
  encapsulation: ViewEncapsulation.None
})
export class PhotosComponent implements OnInit {

  constructor(private modalService: NgbModal) { }

  openDarkModal(content) {
    this.modalService.open(content, { windowClass: 'dark-modal', size: 'lg', centered: true });
  }

  ngOnInit() {
  }

}
Run Code Online (Sandbox Code Playgroud)

这是我的photos.component.html文件:

<div>
  <div class="col-lg-4 col-sm-6 mb-3">
    <a><img (click)="openDarkModal(content)" id="photo-one" class="img-fluid z-depth-4 relative waves-light" src="#" alt="Image" data-toggle="content" data-target="#content"></a>
  </div>
</div>

<!-- Dark Modal -->
<ng-template #content let-modal id="ng-modal">
  <div class="modal-header dark-modal">
    <img (click)="modal.dismiss('Cross click')" id="modal-image" class="embed-responsive-item img-fluid" src="#" alt="Image" allowfullscreen>
  </div>
    <div class="justify-content-center flex-column flex-md-row list-inline">
      <ul class="list-inline flex-center text-align-center text-decoration-none" id="modal-buttons-list">
        <li><a style="color: white;" href="#"><button mdbBtn type="button" size="sm" class="waves-light" color="indigo" mdbWavesEffect><i class="fab fa-facebook-f"></i></button></a></li>
        <li><a style="color: white;" href="#"><button mdbBtn type="button" size="sm" class="waves-light" color="cyan" mdbWavesEffect><i class="fab fa-twitter"></i></button></a></li>
        <li><a style="color: white;" href="#"><button mdbBtn type="button" size="sm" class="waves-light btn btn-blue-grey" mdbWavesEffect><i class="fas fa-envelope"></i></button></a></li>
      </ul>
    </div>
</ng-template>
Run Code Online (Sandbox Code Playgroud)

这是我在photos.component.spec.ts文件中的位置:

<div>
  <div class="col-lg-4 col-sm-6 mb-3">
    <a><img (click)="openDarkModal(content)" id="photo-one" class="img-fluid z-depth-4 relative waves-light" src="#" alt="Image" data-toggle="content" data-target="#content"></a>
  </div>
</div>

<!-- Dark Modal -->
<ng-template #content let-modal id="ng-modal">
  <div class="modal-header dark-modal">
    <img (click)="modal.dismiss('Cross click')" id="modal-image" class="embed-responsive-item img-fluid" src="#" alt="Image" allowfullscreen>
  </div>
    <div class="justify-content-center flex-column flex-md-row list-inline">
      <ul class="list-inline flex-center text-align-center text-decoration-none" id="modal-buttons-list">
        <li><a style="color: white;" href="#"><button mdbBtn type="button" size="sm" class="waves-light" color="indigo" mdbWavesEffect><i class="fab fa-facebook-f"></i></button></a></li>
        <li><a style="color: white;" href="#"><button mdbBtn type="button" size="sm" class="waves-light" color="cyan" mdbWavesEffect><i class="fab fa-twitter"></i></button></a></li>
        <li><a style="color: white;" href="#"><button mdbBtn type="button" size="sm" class="waves-light btn btn-blue-grey" mdbWavesEffect><i class="fas fa-envelope"></i></button></a></li>
      </ul>
    </div>
</ng-template>
Run Code Online (Sandbox Code Playgroud)

我需要Dark Modal 中元素的测试用例和openDarkModal的测试。除了代码之外,对于初学者的 Angular 7 测试中的参考将不胜感激。

Sha*_*vek 6

让我帮你解决这个问题。让我们说你有

应用程序组件.html

<div id="title">
    {{title}}
</div>
<ng-template #content
             let-modal
             id="ng-modal">
  <div class="modal-header dark-modal">
    Header
  </div>
  <div class="justify-content-center flex-column flex-md-row list-inline">
    Body
  </div>
</ng-template>
Run Code Online (Sandbox Code Playgroud)

app.component.ts

import { Component, ViewChild, TemplateRef } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'AngularProj';
  @ViewChild('content') modalRef: TemplateRef<any>;
}
Run Code Online (Sandbox Code Playgroud)

您需要以spec稍微不同的方式编写文件:

app.component.spec.ts

import { TestBed, async, ComponentFixture } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { ViewChild, Component, OnInit, AfterContentInit, TemplateRef } from '@angular/core';
import { By } from '@angular/platform-browser';

@Component({
  template: `
    <ng-container *ngTemplateOutlet="modal"> </ng-container>
    <app-root></app-root>
  `,
})
class WrapperComponent implements AfterContentInit {
  @ViewChild(AppComponent) appComponentRef: AppComponent;
  modal: TemplateRef<any>;
  ngAfterContentInit() {
    this.modal = this.appComponentRef.modalRef;
  }
}

describe('AppComponent', () => {
  let app: AppComponent;
  let fixture: ComponentFixture<WrapperComponent>;
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [WrapperComponent, AppComponent],
    }).compileComponents();
  }));
  beforeEach(() => {
    fixture = TestBed.createComponent(WrapperComponent);
    const wrapperComponent = fixture.debugElement.componentInstance;
    app = wrapperComponent.appComponentRef;
    fixture.detectChanges();
  });
  it('should create the app', async(() => {
    expect(app).toBeDefined();
  }));
  it('should have title in HtmL ', () => {
    const titleText = (fixture.debugElement.nativeElement.querySelector('#title').innerText);
    expect(titleText).toBe('AngularProj');
  });
  it('should have Header in HtmL ', () => {
    const headerText = (fixture.debugElement.queryAll(By.css('.modal-header.dark-modal'))[0].nativeElement.innerText);
    expect(headerText).toBe('Header');
  });
});
Run Code Online (Sandbox Code Playgroud)
  1. 如您所见,我app-root用一个示例测试组件 ( WrapperComponent)包裹了。
  2. 因为,app-roothas ng-template,所以它不会自己呈现。这会造成一个棘手的情况,因为我们需要渲染app.component.
  3. ng-template通过创建 @ViewChild('content') modalRef: TemplateRef<any>;然后使用它来渲染内部来公开WrapperComponent

我知道这似乎是一种黑客行为,但在我阅读的所有文章中,这就是我们实现这一目标的方式。


用于测试类似的东西:

openDarkModal(content) {
    this.modalService.open(content, { windowClass: 'dark-modal', size: 'lg', centered: true });
 }
Run Code Online (Sandbox Code Playgroud)

您可以使用spy,但在此之前modalService公开以便可以对其进行监视:

constructor(public modalService: NgbModal) { }
Run Code Online (Sandbox Code Playgroud)

您还可以使用jasmine.createSpyObj并保留该服务private

然后在spec

   import { NgbModalModule } from '@ng-bootstrap/ng-bootstrap';

   TestBed.configureTestingModule({ 
      imports: [NgbModalModule], 
      declarations: [PhotosComponent, /*WrapperComponent*/], 
      schemas: [NO_ERRORS_SCHEMA], 
    })

// and in it block
  it('should call modal Service open function when clicked ', async(() => {
    spyOn(component.modalService,'open').and.callThrough();
    const openModalEle= fixture.debugElement.nativeElement.querySelector('#photo-one'));
     openModalEle.click();
     expect(component.modalService.open).toHaveBeenCalled();    
  }));
Run Code Online (Sandbox Code Playgroud)

更新:

使用 Angular 11,您需要进行一些更改:

@Component({
  template: `
    <div>
      <ng-container *ngTemplateOutlet="modal"> </ng-container>
    </div>
    <app-root> </app-root> 
  `,
})
class WrapperComponent implements AfterViewInit {
  @ViewChild(AppComponent) appComponentRef: AppComponent;
  modal: TemplateRef<any>;
  constructor(private cdr: ChangeDetectorRef) {}
  ngAfterViewInit() {
    this.modal = this.appComponentRef.modalRef;
    this.cdr.detectChanges();
  }
}
describe('AppComponent', () => {
  let fixture: ComponentFixture<WrapperComponent>;
  let wrapperComponent: WrapperComponent;

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [WrapperComponent, AppComponent],
        imports: [ModalModule.forRoot()],
      }).compileComponents();
    })
  );
  beforeEach(() => {
    fixture = TestBed.createComponent(WrapperComponent);
    wrapperComponent = fixture.debugElement.componentInstance;
    fixture.detectChanges();
  });
  it('should create the app', () => {
    expect(wrapperComponent).toBeDefined();
    expect(wrapperComponent.appComponentRef).toBeDefined();
  });
});
Run Code Online (Sandbox Code Playgroud)

  • @shashank,将 `ViewChild` 切换为 `static: true` 修复了 `AppComponent` 的未定义问题。但是,我无法让包装器组件包含“ng-template” DOM 结构。它总是将其创建为它所创建的组件实例的同级。我仍然有兴趣在 stackblitz 等中看到一个工作示例。我确实解决了单元测试问题,只需从 DOM 中选择同级窗口元素(例如使用“.nextSibling”) (2认同)