在 Angular 中使用 Jasmine 使用 *ngIf 指令时,如何对元素是不是可见进行单元测试

Posted

技术标签:

【中文标题】在 Angular 中使用 Jasmine 使用 *ngIf 指令时,如何对元素是不是可见进行单元测试【英文标题】:How do I unit test if an element is visible when the *ngIf directive is used using Jasmine in Angular在 Angular 中使用 Jasmine 使用 *ngIf 指令时,如何对元素是否可见进行单元测试 【发布时间】:2018-12-19 20:38:55 【问题描述】:

我有一个 Angular 6 应用程序并编写了一些单元测试,试图仅根据 *ngIf 指令的布尔结果来确定元素是否可见。

标记:

<div class="header" *ngIf="show">
    <div>...</div>
</div>

规格文件:

it('should hide contents if show is false', () => 
    const button = debugElement.query(By.css('button')).nativeElement;
    button.click();   // this will change show to false
    fixture.detectChanges();
    expect(debugElement.query(By.css('.header')).nativeElement.style.hidden).toBe(true);
);

我似乎无法从 div 中获取 hidden 属性。 Angular 是否使用另一种方法使用 *ngIf 指令从 DOM 中隐藏元素?我需要从nativeElement 获得另一个属性吗?

谢谢!

【问题讨论】:

【参考方案1】:

如果元素是隐藏的,则不会在 dom 中渲染。

你可以检查

expect(fixture.debugElement.query(By.css('.header'))).toBeUndefined();

EDIT : toBeNull() 在上述情况下效果更好

expect(fixture.debugElement.query(By.css('.header'))).toBeNull();

您在获取按钮元素时也遇到了语法错误。 nativeElement 不是函数。

这样改:

const button = fixture.debugElement.query(By.css('button')).nativeElement;

【讨论】:

最终对我有用的是检查节点本身,就像您在此答案的第一行中所做的那样。然而,有趣的是,我最终没有检查undefined,而是使用toBeNull() 函数检查节点是否为null。这似乎对我有用。 小心将fixture.debugElement.queryexpect(...) 的调用结合使用。这可能会导致浏览器出现内存不足错误。发生在我身上。见***.com/a/48606192/2042301 我也有同样的问题,这确实是正确的答案。但是,我们(出于某些原因)不是默认的更改检测:ChangeDetectionStrategy.OnPush.。结果在浏览器中该元素被自动删除,但在单元测试中我必须手动调用“ChangeDetectorRef.detectChanges()”才能使其工作 即使 ngIf 的计算结果为 false,这里的代码似乎也能传递给我...【参考方案2】:

当使用ngIf 测试组件是否显示时,我尝试获取元素(在这种情况下您使用,即debugElement.query(By.css('.header')).nativeElement),如果应该显示它,我希望它是真实的,否则假的。

类似这样的:

it('should hide contents if show is false', () => 
    // should be rendered initially
    expect(debugElement.query(By.css('.header')).nativeElement).toBeTruthy();
    //trigger change
    const button = debugElement.query(By.css('button')).nativeElement;
    button.click();   // this will change show to false
    fixture.detectChanges();
    // should not be rendered
    expect(debugElement.query(By.css('.header')).nativeElement).toBeFalsy();
);

另外,请记住,有时您需要使用ComponentFixture#whenStable 来检测灯具何时稳定,如下所示:

  it('should hide contents if show is false', () => 
    const fixture = TestBed.createComponent(AppComponent);
    fixture.whenStable().then(() => 
      // same test code here
    );
  );

请参阅此 working test 了解 component 哪个 resembles this scenario。

见 [GitHub repository]

【讨论】:

不应该将第二个示例测试函数标记为async,因为我们正在那里处理承诺吗? @Werek - 因为我们不是在等待承诺,而是利用then 前往回调世界,所以函数签名不必用async 标记。【参考方案3】:

*ngIf 条件编写测试用例使用toBeNull 条件。

试试下面的代码,它对我有用。

expect(fixture.debugElement.query(By.css('.header'))).toBeNull();

【讨论】:

【参考方案4】:

将'show'的值更改为true后,您需要添加fixture.detectChanges()

component.show = true;
fixture.detectChanges()

【讨论】:

【参考方案5】:

使用 ngIf 时,angular 会完全从标记中删除节点。所以你需要检查这个元素不存在。

Documentation says:

ngIf 计算表达式,然后分别在表达式为真或假时呈现 then 或 else 模板。

更准确地说,它只是没有渲染

【讨论】:

谢谢!我还尝试测试 nativeElement 是否未定义并且它一直未能通过测试说 Expected htmlNode to be undefined 所以它似乎是在检查节点的值而不是它是否存在于 DOM 中 @J-man 检查 fixture.debugElement.query(By.css('.header')).nativeElement

以上是关于在 Angular 中使用 Jasmine 使用 *ngIf 指令时,如何对元素是不是可见进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Angular 和 Jasmine 模拟 socket.io

如何使用 Jasmine 为私有方法编写 Angular / TypeScript 单元测试

如何在 Angular 7 中使用 Karma/Jasmine 为 App_Initializer 编写单元测试用例

如何在不使用 Angular 的 spyOn 的情况下检查服务中的方法是不是在 Jasmine 单元测试中被调用?

在 Jasmine/Karma 测试中使用 mockError 时,apollo-angular 会抛出错误

使用 Angular Promise 进行 Jasmine 测试无法使用 TypeScript 解决