【问题标题】:Angular unit tests are leaking stylesAngular 单元测试是泄漏样式
【发布时间】:2020-05-10 05:59:35
【问题描述】:

在我们的项目中,我们有数千个单元测试,它们变得越来越慢。我确实对其进行了调试,发现 CPU 时间主要用于渲染。

经过更多调试,我发现在 Jasmine 测试页面 DOM 中有数千个 <style> 标签,这似乎是性能问题的核心。

我已经在空的ng new 项目上尝试过了。我所做的只是给AppComponent添加一些样式:

app.component.css

.test { color: red; }

当我使用ng test 运行默认单元测试(有 3 个预定义)并在 Jasmine 测试页面上打开 chrome 控制台时,结果如下 - 样式出现了 3 次!:

在运行数千个测试时,由于有数千个 <style> 标签,性能会受到很大影响。

有人知道如何在每次测试后清理业力/角度吗?

我正在使用 angular 6.1.1 和 angular/cli 6.0.1

【问题讨论】:

  • AppComponent是什么类型的视图封装?
  • 默认的,如果我没记错的话就是ViewEncapsulation.Emulated

标签: angular jasmine


【解决方案1】:

我认为您遇到了以下错误:https://github.com/angular/angular/issues/31834

将以下行添加到全局 afterEach 确实加快了测试套件的执行速度:

window.document.querySelectorAll("style").forEach((style: HTMLStyleElement) => style.remove());

【讨论】:

【解决方案2】:

好的,我尝试了更残酷的方法,似乎它正在工作。我创建了一个包含 1400 个相同测试的示例应用程序(它只是呈现 @angular/material 按钮并检查它)。

运行此测试套件需要 14 分钟。

添加这些行后:

afterEach(() => {
    const head = document.getElementsByTagName('head')[0];

    const styles = document.getElementsByTagName('style');


    for (let i = 0; i < styles.length; i++) {
      head.removeChild(styles[i]);
    }
});

运行它需要 80 秒。

在我看来,Karma 应该以某种方式处理这个问题,因为我敢打赌这是很常见的问题

【讨论】:

  • 疑问:是否应该在破坏每个测试用例后删除样式,或者我们可以在整个测试套件执行完毕后利用afterAll() 删除它们?
  • 应该在 afterEach 中完成,因为每个测试用例都在向 html 添加样式
【解决方案3】:

组件配置为使用ViewEncapsulation.Emulated,这意味着app.component.css 文件应该只包含:host 样式和其他主机选择器。

Angular 将为组件注入&lt;style&gt; 标签,并在运行时将其绑定到主机视图。当组件被销毁时,host 样式被移除,但是您在 host 范围之外包含了额外的选择器。所以浏览器将它们保持原样,因为它们没有绑定到主机。

全局的任何额外样式都应通过为项目定义的style.css文件包含在内。

另一种方法是使用ViewEncapsulation.None,而不是使用:host 选择器。

更新:

Angular 6 添加了ViewEncapsulation.ShadowDom 设置。

https://w3c.github.io/webcomponents/spec/shadow/

本规范描述了一种将多个 DOM 树组合成一个层次结构的方法,以及这些树如何在文档中相互交互,从而实现更好的 DOM 组合。

Angular 文档提供了以下示例:

 styles: [`
    :host {
      display: block;
      border: 1px solid black;
    }
    h1 {
      color: blue;
    }
    .red {
      background-color: red;
    }
  `],

尝试改用这种封装模式。看起来它解决了您遇到的问题。

【讨论】:

  • 好的,我不确定我是否理解正确。当我将样式表更改为::host{ color:red } 时,结果完全相同,样式不会从 DOM 中删除。使用ViewEncapsulation.ShadowDom,如果没有任何更改,则并非所有浏览器都支持,对吧?
  • 我刚刚尝试了shadow dom封装。是的,它确实解决了单元测试的问题,但应用程序在非 Chrome 浏览器中停止工作(尝试过 Edge)
  • 我也尝试过Viewencapsulation.None,但结果与仿真结果相同
【解决方案4】:

我为此创建了一个可以使用的函数trimLeftoverStyles,你可以从我的开源库s-ng-dev-utils 中获得它。它只会删除组件添加的样式;如果您添加了在测试套件中使用的全局样式,它将不理会它们。如果您按照建议在 beforeEach 中调用它,它还会在测试结束时使您的组件保持正确的样式,以便您可以正确地看到它以进行调试。

【讨论】:

    猜你喜欢
    • 2016-01-05
    • 1970-01-01
    • 2018-02-23
    • 1970-01-01
    • 2019-07-15
    • 1970-01-01
    • 2011-09-28
    • 2011-02-28
    • 2010-09-15
    相关资源
    最近更新 更多