Web 组件的测试工作流程
当你发布供他人使用的代码时,你就肩负着提供安全稳定代码的责任。解决这个问题的方法之一就是测试你的代码。
无论项目多么小,无论项目多么简单,理想情况下都应该进行测试。
我知道现实很残酷,很多情况下检测结果都不会如预期那样——但你始终应该努力去做检测。
免责声明
在本教程中,我们将创建一个简单的输入元素。完成本教程后,您将掌握运用 open-wc 测试工具的技能和知识,并构建一个稳定、易用且经过充分测试的输入组件。
警告
这是一篇深入的教程,展示了使用 Web 组件时的一些陷阱和棘手案例。本教程面向高级用户。您应该具备LitElement和JSDoc 类型的基本知识。了解Mocha、Chai BDD和Karma也可能有所帮助。
我们正在考虑发布一个更容易理解的版本,如果您想看到这样的版本,请在评论中告诉我们。
如果你想一起玩——所有代码都在GitHub上。
让我们开始吧!
在控制台中运行
$ npm init @open-wc
# Results in this flow
✔ What would you like to do today? › Scaffold a new project
✔ What would you like to scaffold? › Web Component
# Select with space! "Testing" => just enter will move one with no selection
✔ What would you like to add? › Testing
✔ Would you like to scaffold examples files for? › Testing
✔ What is the tag name of your application/web component? … a11y-input
✔ Do you want to write this file structure to disk? › Yes
Writing..... done
✔ Do you want to install dependencies? › No
更多详情请参见https://open-wc.org/testing/。
删除src/A11yInput.js
修改src/a11y-input.js为:
import { LitElement, html, css } from 'lit-element';
export class A11yInput extends LitElement {}
customElements.define('a11y-input', A11yInput);
以及test/a11y-input.test.js:
/* eslint-disable no-unused-expressions */
import { html, fixture, expect } from '@open-wc/testing';
import '../src/a11y-input.js';
/**
* @typedef {import('../src/a11y-input.js').A11yInput} A11yInput
*/
describe('a11y input', () => {
it('has by default an empty string as label', async () => {
const el = /** @type {A11yInput} */ (await fixture('<a11y-input></a11y-input>'));
expect(el.label).to.equal('');
});
});
我们目前的测试只包含一个特性(label属性)和一个断言expect。我们使用 Karma 和 Chai 的BDD语法,因此我们将测试集it按它们相关的特性或 API进行分组describe。
让我们运行以下命令来查看一切是否正常:npm run test。
SUMMARY:
✔ 0 tests completed
✖ 1 test failed
FAILED TESTS:
a11y input
✖ has by default an empty string as label
HeadlessChrome 73.0.3683 (Windows 10.0.0)
AssertionError: expected undefined to equal ''
+ expected - actual
-[undefined]
+""
太棒了——果然不出所料(🥁),我们又一次测试失败了:)
让我们切换到监视模式,这样每当您对代码进行更改时,测试都会持续运行。
npm run test:watch
以下代码已添加到上面的视频中src/a11y-input.js:
static get properties() {
return {
label: { type: String },
};
}
constructor() {
super();
this.label = '';
}
目前为止一切顺利?你还在吗?太好了!让我们再加把劲……
添加 Shadow DOM 测试
让我们添加一个断言来测试元素的影子根的内容。
为了确保元素的行为/外观保持一致,我们应该确保其 DOM 结构也保持不变。
所以,让我们将实际的 Shadow DOM 与我们期望的 Shadow DOM 进行比较。
it('has a static shadowDom', async () => {
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input></a11y-input>
`));
expect(el.shadowRoot.innerHTML).to.equal(`
<slot name="label"></slot>
<slot name="input"></slot>
`);
});
正如预期的那样,我们得到:
✖ has a static shadowDom
AssertionError: expected '' to equal '\n <slot name="label"></slot>\n <slot name="input"></slot>\n '
+ expected - actual
+
+ <slot name="label"></slot>
+ <slot name="input"></slot>
+
所以让我们在我们的元素中实现它。
render() {
return html`
<slot name="label"></slot>
<slot name="input"></slot>
`;
}
有意思,测试结果应该是绿色的……但不是🤔 让我们看看。
✖ has a static shadowDom
AssertionError: expected '<!---->\n <slot name="label"></slot>\n <slot name="input"></slot>\n <!---->' to equal '\n <slot name="label"></slot>\n <slot name="input"></slot>\n '
+ expected - actual
-<!---->
- <slot name="label"></slot>
- <slot name="input"></slot>
- <!---->
+
+ <slot name="label"></slot>
+ <slot name="input"></slot>
+
你可能已经注意到那些奇怪的空注释<!---->标签。它们是lit-html用来标记动态部分所在位置的,以便高效地进行更新。然而,在测试过程中,处理这些标签可能会有点麻烦。
如果我们使用innerHTML比较 DOM,就只能依赖简单的字符串相等性。在这种情况下,我们需要精确匹配生成的 DOM 中的空格、注释等等;换句话说,必须完全匹配。实际上,我们只需要测试想要渲染的元素是否被渲染即可。我们想要测试的是影子根的语义内容。
幸运的是,我们已经考虑到了这一点。如果您正在使用该插件@open-wc/testing,它会自动加载@open-wc/semantic-dom-diff供我们使用的 Chai 插件。
那我们来试试吧💪
// old:
expect(el.shadowRoot.innerHTML).to.equal(`...`);
// new:
expect(el).shadowDom.to.equal(`
<slot name="label"></slot>
<slot name="input"></slot>
`);
砰🎉
a11y input
✔ has by default an empty string as a label
✔ has a static shadowDom
shadowDom.to.equal() 的工作原理是什么?
- 它获取了
innerHTML影子根 - 解析它(实际上,浏览器会解析它——不需要任何库)
- 将其规范化(可能将每个标签/属性放在单独的行上)
- 解析并规范化预期的 HTML 字符串
- 将两个规范化的 DOM 字符串传递给 chai 的默认比较函数。
- 如果失败,则分组,并以清晰的方式显示任何差异。
如果您想了解更多信息,请查看semantic-dom-diff的文档。
测试“轻量级”DOM
我们可以用轻量级 DOM 做同样的事情。(轻量级 DOM 将由用户提供或我们的默认值提供,即元素的 DOM children)。
it('has 1 input and 1 label in light-dom', async () => {
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input .label=${'foo'}></a11y-input>
`));
expect(el).lightDom.to.equal(`
<label slot="label">foo</label>
<input slot="input">
`);
});
让我们把它付诸实施。
connectedCallback() {
super.connectedCallback();
this.labelEl = document.createElement('label');
this.labelEl.innerText = this.label;
this.labelEl.setAttribute('slot', 'label');
this.appendChild(this.labelEl);
this.inputEl = document.createElement('input');
this.inputEl.setAttribute('slot', 'input');
this.appendChild(this.inputEl);
}
我们已经测试了光影领域💪,测试结果一切正常🎉
注意:在 lit 元素的生命周期中使用 DOM API 是一种反模式,但为了实现无障碍访问(a11y),这可能是一个实际的用例——无论如何,它非常适合用于演示目的。
在应用程序中使用我们的元素
现在我们有了基本的 a11y 输入,让我们在我们的应用程序中使用它并进行测试。
我们再次从一个骨架开始。src/my-app.js
/* eslint-disable class-methods-use-this */
import { LitElement, html, css } from 'lit-element';
export class MyApp extends LitElement {}
customElements.define('my-app', MyApp);
我们的测试在test/my-app.test.js;
/* eslint-disable no-unused-expressions */
import { html, fixture, expect } from '@open-wc/testing';
import '../src/my-app.js';
/**
* @typedef {import('../src/my-app.js').MyApp} MyApp
*/
describe('My Filter App', () => {
it('has a heading and a search field', async () => {
const el = /** @type {MyApp} */ (await fixture(html`
<my-app .label=${'foo'}></my-app>
`));
expect(el).shadowDom.to.equal(`
<h1>My Filter App</h1>
<a11y-input></a11y-input>
`);
});
});
运行测试 => 失败,然后我们添加实现。src/a11y-input.js
render() {
return html`
<h1>My Filter App</h1>
<a11y-input></a11y-input>
`;
}
哦不!现在应该是绿色的了……
SUMMARY:
✔ 3 tests completed
✖ 1 test failed
FAILED TESTS:
My Filter App
✖ has a heading and a search field
AssertionError: expected '<h1>\n My Filter App\n</h1>\n<a11y-input>\n <label slot="label">\n </label>\n <input slot="input">\n</a11y-input>\n' to equal '<h1>\n My Filter App\n</h1>\n<a11y-input>\n</a11y-input>\n'
+ expected - actual
<h1>
My Filter App
</h1>
<a11y-input>
- <label slot="label">
- </label>
- <input slot="input">
</a11y-input>
发生了什么事?
你还记得我们之前做过一个专门的测试来确保 a11y-input 的 light-dom 功能正常吗?
所以即使用户只是输入<a11y-input></a11y-input>代码——实际输出结果是……
<a11y-input>
<label slot="label"></label>
<input slot="input">
</a11y-input>
例如,它a11y-input实际上是在你的 Shadow DOM 内部创建节点my-app。这太荒谬了!在我们的示例中,我们假设这就是我们想要的。
那么我们该如何测试它呢?
幸运的.shadowDom是,它还有另一张王牌;它允许我们忽略 dom 的某些部分。
expect(el).shadowDom.to.equal(`
<h1>My Filter App</h1>
<a11y-input></a11y-input>
`, { ignoreChildren: ['a11y-input'] });
我们甚至可以指定以下属性:
ignoreChildrenignoreTagsignoreAttributes(全局或针对特定标签)
更多详情请参见semantic-dom-diff。
快照测试
如果你有很多庞大的 DOM 树,手动编写和维护所有这些 expect 语句将会非常困难。
为了帮助你解决这个问题,可以使用半自动/自动快照功能。
所以如果我们修改代码
// from
expect(el).shadowDom.to.equal(`
<slot name="label"></slot>
<slot name="input"></slot>
`);
// to
expect(el).shadowDom.to.equalSnapshot();
如果我们现在执行npm run test它,它将创建一个文件__snapshots__/a11y input.md并填充类似这样的内容。
# `a11y input`
#### `has a static shadowDom`
``html
<slot name="label">
</slot>
<slot name="input">
</slot>
``
以前需要手动编写的内容现在可以在初始化时自动生成,或者通过强制执行npm run test:update-snapshots。
如果文件__snapshots__/a11y input.md已存在,它会将文件与输出进行比较,如果您的 html 输出发生更改,您将会收到错误提示。
FAILED TESTS:
a11y input
✖ has a static shadowDom
HeadlessChrome 73.0.3683 (Windows 10.0.0)
AssertionError: Received value does not match stored snapshot 0
+ expected - actual
-<slot name="label-wrong">
+<slot name="label">
</slot>
<slot name="input">
-</slot>
+</slot>
更多详情请参见semantic-dom-diff。
我觉得关于比较 DOM 树就到此为止吧……
是时候换个话题了🤗
代码覆盖率
在使用 open-wc 进行测试时,另一个有用的指标是代码覆盖率。
那么它是什么意思?我们又该如何获取它呢?代码覆盖率衡量的是测试覆盖了多少if代码。如果代码中存在测试未覆盖的行、语句、函数或分支(例如`/`else语句),则覆盖率得分会受到影响。我们只需要
一个简单的测试npm run test,即可获得以下结果:
=============================== Coverage summary ===============================
Statements : 100% ( 15/15 )
Branches : 100% ( 0/0 )
Functions : 100% ( 5/5 )
Lines : 100% ( 15/15 )
================================================================================
这意味着我们代码中的所有语句、分支、函数和代码行都得到了测试覆盖。真棒!
那么我们反过来,src/a11y-input.js在添加测试之前先添加代码。假设我们想通过自定义元素直接访问输入框的值,并且当它的值为“cat”时,我们想记录一些内容。
get value() {
return this.inputEl.value;
}
set value(newValue) {
if (newValue === 'cat') {
console.log('We like cats too :)');
}
this.inputEl.value = newValue;
}
结果截然不同。
SUMMARY:
✔ 4 tests completed
TOTAL: 4 SUCCESS
=============================== Coverage summary ===============================
Statements : 81.82% ( 18/22 )
Branches : 0% ( 0/2 )
Functions : 75% ( 6/8 )
Lines : 81.82% ( 18/22 )
================================================================================
06 04 2019 10:40:45.380:ERROR [reporter.coverage-istanbul]: Coverage for statements (81.82%) does not meet global threshold (90%)
06 04 2019 10:40:45.381:ERROR [reporter.coverage-istanbul]: Coverage for lines (81.82%) does not meet global threshold (90%)
06 04 2019 10:40:45.381:ERROR [reporter.coverage-istanbul]: Coverage for branches (0%) does not meet global threshold (90%)
06 04 2019 10:40:45.381:ERROR [reporter.coverage-istanbul]: Coverage for functions (75%) does not meet global threshold (90%)
我们的代码覆盖率比以前低得多。即使所有测试都成功运行,我们的测试命令甚至也失败了。
这是因为 open-wc 的默认配置将代码覆盖率阈值设置为 90%。
如果我们想提高代码覆盖率,就需要增加测试——那就开始吧!
it('can set/get the input value directly via the custom element', async () => {
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input .value=${'foo'}></a11y-input>
`));
expect(el.value).to.equal('foo');
});
糟糕😱 我们本来想提高覆盖率,但现在我们得先修复一个实际存在的bug😞
FAILED TESTS:
a11y input
✖ can set/get the input value directly via the custom element
TypeError: Cannot set property 'value' of null at HTMLElement.set value [as value]
// ... => long error stack
这真是出乎意料……乍一看,我不太明白这意味着什么……最好检查一些实际节点,并在浏览器中进行检查。
在浏览器中进行调试
当我们使用 watch 运行测试时,karma 会设置一个持久的浏览器环境来运行测试。
- 务必从以下方面开始:
npm run test:watch - 访问http://localhost:9876/debug.html
您可以点击带圆圈的播放按钮,仅运行一次单独的测试。
现在让我们打开 Chrome 开发者工具 (F12),并在测试代码中插入调试器。
it('can set/get the input value directly via the custom element', async () => {
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input .value=${'foo'}></a11y-input>
`));
debugger;
expect(el.value).to.equal('foo');
});
糟糕……错误甚至在此之前就发生了……
像这样的“致命”错误比较棘手,因为它们不是测试失败,而是整个组件的彻底崩溃。
好的,我们setter直接把一些代码放进去。
set value(newValue) {
debugger;
好了,成功了,我们在 Chrome 控制台中输入命令,console.log(this)看看这里有什么。
<a11y-input>
#shadow-root (open)
</a11y-input>
啊,找到了——调用 setter 方法时,Shadow DOM 还没有渲染完成。
所以为了保险起见,我们先添加一个检查。
set value(newValue) {
if (newValue === 'cat') {
console.log('We like cats too :)');
}
if (this.inputEl) {
this.inputEl.value = newValue;
}
}
致命错误已解决🎉
但我们现在有一个失败的测试😭
✖ can set/get the input value directly via the custom element
AssertionError: expected '' to equal 'foo'
我们可能需要改变策略🤔
我们可以将其添加为单独的value属性,并在需要时进行同步。
static get properties() {
return {
label: { type: String },
value: { type: String },
};
}
constructor() {
super();
this.label = '';
this.value = '';
// ...
}
update(changedProperties) {
super.update(changedProperties);
if (changedProperties.has('value')) {
if (this.value === 'cat') {
console.log('We like cats too :)');
}
this.inputEl.value = this.value;
}
}
我们终于恢复营业啦!🎉
好的,bug已修复——我们可以恢复正常覆盖了吗?谢谢🙏
返回报道
通过这项新增测试,我们取得了一些进展。
=============================== Coverage summary ===============================
Statements : 95.83% ( 23/24 )
Branches : 50% ( 2/4 )
Functions : 100% ( 7/7 )
Lines : 95.83% ( 23/24 )
================================================================================
06 04 2019 13:18:54.902:ERROR [reporter.coverage-istanbul]: Coverage for branches (50%) does not meet global threshold (90%)
然而,我们仍未完全实现目标——问题是为什么?
要查看,请coverage/index.html在浏览器中打开该文件。无需网络服务器,只需在浏览器中打开该文件即可——在 Mac 上,您可以使用命令行执行此操作。open coverage/index.html
你会看到类似这样的内容。
点击后,a11y-input.js您可以逐行查看代码执行次数的信息。
这样我们就能立即看到哪些代码行尚未被测试执行。
所以,我们来添加一个测试。
it('logs "We like cats too :)" if the value is "cat"', async () => {
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input .value=${'cat'}></a11y-input>
`));
// somehow check that console.log was called
});
=============================== Coverage summary ===============================
Statements : 100% ( 24/24 )
Branches : 75% ( 3/4 )
Functions : 100% ( 7/7 )
Lines : 100% ( 24/24 )
================================================================================
这样一来,语句部分就恢复到100%了,但分支部分仍然缺少一些东西。
我们来看看为什么?
这E意味着else path not taken,无论何时调用
该函数,changedProperties 中总会有一个属性。updatevalue
我们label也有,所以测试一下很有必要。👍
it('can update its label', async () => {
const el = /** @type {A11yInput} */ (await fixture('<a11y-input label="foo"></a11y-input>'));
expect(el.label).to.equal('foo');
el.label = 'bar';
expect(el.label).to.equal('bar');
});
太棒了!100% 💪 我们赢了🥇
=============================== Coverage summary ===============================
Statements : 100% ( 24/24 )
Branches : 100% ( 4/4 )
Functions : 100% ( 7/7 )
Lines : 100% ( 24/24 )
================================================================================
等等,我们上面的测试还没完成——代码仍然
// somehow check that console.log was called
为什么我们的测试覆盖率能达到100%?
我们先来了解一下代码覆盖率的工作原理🤔
代码覆盖率的测量方法是应用一种特定的函数instrumentation。简而言之,在代码执行之前,它会被修改(instrumented),其行为大致如下:
注:此版本仅为极简化版本,仅用于说明目的。
if (this.value === 'cat') {
console.log('We like cats too :)');
}
// becomes something like this (psoido code)
__instrumented['functionUpdate'] += 1;
if (this.value === 'cat') {
__instrumented['functionUpdateBranch1yes'] += 1;
console.log('We like cats too :)');
} else {
__instrumented['functionUpdateBranch1no'] += 1;
}
基本上,你的代码中会充斥着很多很多标志。根据哪些标志被触发,就会生成一个统计数据。
所以,100% 的测试覆盖率仅仅意味着在所有测试完成后,代码中的每一行都至少执行了一次。它并不意味着你测试了所有内容,也不意味着你的测试断言是正确的。
所以即使我们已经实现了 100% 的代码覆盖率,我们仍然会改进我们的日志测试。
因此,你应该把代码覆盖率看作是一个工具,它只能为你提供指导和帮助,以发现一些缺失的测试,而不是对代码质量的硬性保证。
监视代码
如果你想查看某个函数被调用的频率或调用参数,这叫做监视(spying)。open
-wc 推荐使用久负盛名的sinon包,它提供了许多用于监视和其他相关任务的工具。
npm i -D sinon
所以你可以对特定对象创建一个间谍,然后检查它被调用的频率。
import sinon from 'sinon';
it('outputs "We like cats too :)" if the value is set to "cat"', async () => {
const logSpy = sinon.spy(console, 'log');
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input></a11y-input>
`));
el.value = 'cat';
expect(logSpy.callCount).to.equal(1);
});
糟糕……测试失败了:
AssertionError: expected 0 to equal 1
像这样直接操作全局对象console可能会产生副作用,所以我们最好使用专门的日志函数进行重构。
update(changedProperties) {
super.update(changedProperties);
if (changedProperties.has('value')) {
if (this.value === 'cat') {
this.log('We like cats too :)');
}
this.inputEl.value = this.value;
}
}
log(msg) {
console.log(msg);
}
这样一来,我们的测试代码中就没有全局对象了——太好了🤗
it('logs "We like cats too :)" if the value is set to "cat"', async () => {
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input></a11y-input>
`));
const logSpy = sinon.spy(el, 'log');
el.value = 'cat';
expect(logSpy.callCount).to.equal(1);
});
然而,我们仍然遇到同样的错误。让我们调试一下……boohoo 显然update没有同步——我之前做了个错误的假设🙈 我经常说假设很危险——但我还是会时不时地犯错😢。
那么我们该怎么办呢?遗憾的是,似乎没有公开的 API 可以执行由属性更新触发的同步操作。
让我们为此创建一个 issue:https://github.com/Polymer/lit-element/issues/643。
目前看来,唯一的办法似乎是依赖私有API。🙈
另外,我们需要将值同步操作移到updated每次 DOM 渲染之后执行的位置。
_requestUpdate(name, oldValue) {
super._requestUpdate(name, oldValue);
if (name === 'value') {
if (this.value === 'cat') {
this.log('We like cats too :)');
}
}
}
updated(changedProperties) {
super.updated(changedProperties);
if (changedProperties.has('value')) {
this.inputEl.value = this.value;
}
}
以下是更新后的日志记录测试。
it('logs "We like cats too :)" if the value is set to "cat"', async () => {
const el = /** @type {A11yInput} */ (await fixture(html`
<a11y-input></a11y-input>
`));
const logSpy = sinon.spy(el, 'log');
el.value = 'cat';
expect(logSpy.callCount).to.equal(1);
expect(logSpy.calledWith('We like cats too :)')).to.be.true;
// different values do NOT log
el.value = 'foo';
expect(logSpy.callCount).to.equal(1);
el.value = 'cat';
expect(logSpy.callCount).to.equal(2);
});
哇,比预想的要难一些,但我们做到了💪
SUMMARY:
✔ 7 tests completed
TOTAL: 7 SUCCESS
不使用 Karma 框架运行测试
Karma 框架功能强大且丰富,但有时我们可能希望简化测试流程。我们目前提出的方案的优点在于,除了裸模块说明符之外,我们只使用了浏览器标准的 ES 模块,无需转译。
因此,只需创建一个test/index.html……
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="../node_modules/mocha/mocha.css" rel="stylesheet" />
<script src="../node_modules/mocha/mocha.js"></script>
<script src="../node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
</head>
<body>
<div id="mocha"></div>
<script>
mocha.setup('bdd');
</script>
<script type="module">
import './a11y-input.test.js';
import './my-app.test.js';
mocha.checkLeaks();
mocha.run();
</script>
</body>
</html>
在 Chrome浏览器中打开owc-dev-server,一切正常。
我们已经成功完成了所有设置并运行了webpack——karma太棒了🤗
实现跨浏览器兼容性
我们现在对我们的 Web 组件相当满意了。它已经过测试并涵盖了所有方面;只剩最后一步——我们要确保它在所有浏览器中都能运行并经过测试。
Open WC 推荐使用Browserstack进行跨浏览器测试。如果您还没有设置,现在就可以设置——链接再次奉上——https: //open-wc.org/testing/。
那就运行一下吧。
npm run test:bs
SUMMARY:
✔ 42 tests completed
TOTAL: 42 SUCCESS
是的,效果很好!🤗
如果测试失败,它会在摘要中输出失败的测试结果以及失败的具体浏览器。
SUMMARY:
✔ 40 tests completed
✖ 2 tests failed
FAILED TESTS:
a11y input
✖ has a static shadowDom
Firefox 64.0.0 (Windows 10.0.0)
Safari 12.0.0 (Mac OS X 10.14.0)
expected '<slot name="label">\n</slot>\n<slot name="input">\n</slot>\n<style>\n</style>\n' to equal '<slot name="label">\n</slot>\n<slot name="input">\n</slot>\n'
+ expected - actual
<slot name="label">
</slot>
<slot name="input">
</slot>
-<style>
-</style>
如果您需要调试某个特定的浏览器:
npm run test:legacy:watch- 使用该浏览器(本地浏览器或通过 BrowserStack 访问)访问http://localhost:9876/debug.html
- 选择特定测试(或
it.only()在代码中使用) - 开始调试
另外,如果您想调整要测试的浏览器,您可以进行调整karma.bs.config.js。
例如,如果您想将以下内容添加Firefox ESR到您的列表中。
module.exports = config => {
config.set(
merge(bsSettings(config), createBaseConfig(config), {
browserStack: {
project: 'testing-workflow-for-web-components',
},
browsers: [
'bs_win10_firefox_ESR',
],
// define browsers
// https://www.browserstack.com/automate/capabilities
customLaunchers: {
bs_win10_firefox_ESR: {
base: 'BrowserStack',
browser: 'Firefox',
browser_version: '60',
os: 'Windows',
os_version: '10',
},
},
}),
);
return config;
};
或者您可能只想测试两个特定的浏览器?
merge.strategy({
browsers: 'replace',
})(bsSettings(config), createBaseConfig(config), {
browserStack: {
project: 'testing-workflow-for-web-components',
},
browsers: [
'bs_win10_ie_11',
'bs_win10_firefox_ESR',
],
}),
注意:这使用了webpack 合并策略替换。
快速回顾
- 测试对每个项目都至关重要。务必尽可能多地编写测试用例。
- 尽量保持代码覆盖率高,但请记住,代码覆盖率并非万无一失,所以不必总是达到 100%。
- 在浏览器中通过以下方式进行调试
npm run test:watch。对于旧版浏览器,请使用npm run test:legacy.watch。
接下来是什么?
- 在 CI 系统中运行测试(与 BrowserStack 完美兼容)。请参阅我们的自动化建议。
请在Twitter上关注我们,或者关注我的个人Twitter账号。
也请务必访问open-wc.org查看我们的其他工具和推荐。
感谢Pascal和Benny 的反馈,帮助我将涂鸦变成了一个可以理解的故事。
文章来源:https://dev.to/open-wc/testing-workflow-for-web-components-g73



