Angular 中的深度测试组件问题以及使用 debugElement 访问 html

Issue with deep testing components in Angular and accessing html with debugElement

我的 angular 应用程序中有一个组件层次结构,如下所示:

BookmarkContainer 具有以下子组件:

BookmarksListComponentngForBookmarkItemComponent

我正在尝试使用 DebugElement 及其 query() 方法从 BookmarkContainer 的测试中访问 BookmarkItemComponent 的模板。

这是 BookmarkContainer 的代码:

@Component({
  selector: 'app-bookmarks-container',
  templateUrl: './bookmarks.container.html',
  styleUrls: ['./bookmarks.container.scss']
})
export class BookmarksContainerComponent implements OnInit {
  bookmarks$: Observable<Bookmark[]>;
  newBookmark: Bookmark;
  deletedBookmarkId: number;

  constructor(private bookmarkService: BookmarkService) {}

  ngOnInit(): void {
    this.setBookmarks();
  }

  private setBookmarks() {
    this.bookmarks$ = this.bookmarkService
      .currentUser()
      .pipe(
        mergeMap(session => this.bookmarkService.search({ account: session.user.uid }))
      );
  }
}

这里是对应的测试:

describe('BookmarkContainerComponent', () => {
  let component: BookmarksContainerComponent;
  let fixture: ComponentFixture<BookmarksContainerComponent>;
  let httpTestingController: HttpTestingController;

  const aBookmark: Bookmark = {
    id: 42,
    title: 'a bookmark',
    url: 'http://www.example.com',
    account: '1'
  };
  const anotherBookmark: Bookmark = {
    id: 43,
    title: 'another bookmark',
    url: 'http://www.example.fr',
    account: '1'
  };

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule, ReactiveFormsModule],
      declarations: [BookmarksContainerComponent, BookmarksListComponent, BookmarkItemComponent, CreateBookmarkComponent],
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(BookmarksContainerComponent);
    component = fixture.componentInstance;
    httpTestingController = TestBed.inject(HttpTestingController);
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should search bookmarks', fakeAsync(() => {
    const sessionRequest = httpTestingController.expectOne('/api/system/connect');
    sessionRequest.flush({ user: { uid: '1' } });

    const bookmarksRequest = httpTestingController.expectOne('/api/bookmarks_link/search');
    bookmarksRequest.flush([aBookmark, anotherBookmark]);

    //Here I am not sure how to access the li(s) from the BookmarkItemComponent
    const item = fixture.debugElement.query(By.directive(BookmarkItemComponent));

});

我尝试从 BookmarkItemComponent 访问 li(s) 但没有成功:item 总是 null。我怀疑这与 BookmarkItemComponent 的嵌套级别有关...有人可以帮忙吗?

编辑:

BookmarkContainer 的模板:

<app-bookmarks-list
  [bookmarks]="bookmarks$ | async"
  [newBookmark]="newBookmark"
  [updatedBookmark]="updatedBookmark"
  [deletedBookmark]="deletedBookmarkId"
  (deleteBookmarkEvt)="confirmDeleteBookmark($event)"
  (updateBookmarkEvt)="openUpdateForm($event)">
</app-bookmarks-list>
...

BookmarksListComponent 的模板:

<ng-container *ngFor="let bookmark of bookmarks">
  <div class="bookmark">
    <div class="">
      <ul class="bookmark-list">
        <app-bookmark-item
          [bookmark]="bookmark"
          (deleteBookmarkEvt)="deleteBookmark($event)"
          (updateBookmarkEvt)="updateBookmark($event)"
        >
        </app-bookmark-item>
      </ul>
    </div>
  </div>
</ng-container>

BookmarkItemComponent 的模板:

<li class="bookmark-item bookmark-item-{{ bookmark.id }}">
  <span class="bookmark-icon"></span>
  <span class="bookmark-text">
    <a href="{{ bookmark.url }}" target="_blank" title="{{ 'BOOKMARKS.OPEN' | translate}}">
    {{bookmark.title}}
    </a>
  </span>
  <span class="bookmark-actions">
    <a title="{{ 'GLOBAL.UPDATE' | translate}}" (click)="updateBookmark(bookmark)">
      <i class="glyphicon glyphicon-pencil"></i>
    </a>
    <a title="{{ 'GLOBAL.DELETE' | translate}}" (click)="deleteBookmark(bookmark.id)">
      <i class="glyphicon glyphicon-remove "></i>
    </a>
  </span>
</li>

为了在 DOM 中看到 http 响应数据,您需要在 request.flush(...) 之后执行 fixture.detectChanges()。另外,我会删除 fakeAsync 包装。应该不需要。

例如,

it('should search bookmarks', function() {
    const sessionRequest = httpTestingController.expectOne('/api/system/connect');
    sessionRequest.flush({ user: { uid: '1' } });

    const bookmarksRequest = httpTestingController.expectOne('/api/bookmarks_link/search');
    bookmarksRequest.flush([aBookmark, anotherBookmark]);

    // add detectChanges here
    fixture.detectChanges();

    // item should be defined now.
    const item = fixture.debugElement.query(By.directive(BookmarkItemComponent));
});

你能试试在 beforeEach 上添加 async 吗?

beforeEach(async() => {
    fixture = TestBed.createComponent(BookmarksContainerComponent);
    component = fixture.componentInstance;
    httpTestingController = TestBed.inject(HttpTestingController);
    fixture.detectChanges();
  });