条件测试用下列的编程范式可以清晰表示出来:
If X, then Y, else Z
如今,现代 JavaScript 应用程序是高度动态和可变的。 它们的状态和 DOM 在一段时间内不断变化。
条件测试的问题在于它只能在状态稳定后才能使用。 在现代应用程序中,知道状态何时稳定通常是不可能的。
对人类而言——如果从现在起 10 毫秒或 100 毫秒发生变化,我们甚至可能不会注意到这种变化并假设状态总是相同的。
对于机器人来说——即使是 10 毫秒也代表着数十亿+ 的时钟周期。 时间尺度的差异令人难以置信。
人也有直觉。 如果您单击一个按钮并看到一个加载微调器,您将假定状态处于不断变化中,并会自动等待它完成。
机器人没有直觉——它会完全按照编程的方式去做。
为了说明这一点,让我们举一个简单的例子来尝试有条件地测试不稳定状态。
The DOM is unstable
下面是我们的应用代码,在随机的时间间隔内,将新创建的 button 元素添加 active CSS 类。
// your app code
// random amount of time
const random = Math.random() * 100
// create a <button> element
const btn = document.createElement('button')
// attach it to the body
document.body.appendChild(btn)
setTimeout(() => {
// add the class active after an indeterminate amount of time
btn.setAttribute('class', 'active')
}, random)
看下面的测试代码:
// your cypress test code
it('does something different based on the class of the button', () => {
// RERUN THIS TEST OVER AND OVER AGAIN
// AND IT WILL SOMETIMES BE TRUE, AND
// SOMETIMES BE FALSE.
cy.get('button').then(($btn) => {
if ($btn.hasClass('active')) {
// do something if it's active
} else {
// do something else
}
})
})
我们会随机进入 if 和 else 分支。
Server side rendering
如果 web 应用的源代码在服务器端渲染,并且不存在随后通过 JavaScript 异步修改 DOM 的可能性,这种应用是极佳的进行条件测试的备选。
load 事件发生之后,DOM 不会再发生变化,这意味着其已经达到了一个稳定的状态(a stable state of truth).
Client side rendering
对于现代 Web 应用来说,当 load 事件发生时,屏幕上通常什么都还没有渲染。JavaScript 在此时会动态加载内容,并执行渲染。
这种情况下,不可能依靠 DOM 进行条件测试,除非我们有 100% 的把握,找到某个时间点,该时间点到达时,所有的异步渲染都已经结束,并且也不存在 pending network requests, setTimeouts, intervals, postMessage, or async/await code. 所有的 web 开发者都明白这是一件不可能的事情。
即使我们的应用里使用了 Zone.js, 也没办法捕捉到所有的异步编程点。
A/B campaign
使用 url 参数,显式告诉服务器我们期望返回的数据内容:
// tell your back end server which campaign you want sent
// so you can deterministically know what it is ahead of time
cy.visit('https://app.com?campaign=A')
...
cy.visit('https://app.com?campaign=B')
...
cy.visit('https://app.com?campaign=C')
It is crucial that you understand how your application works else you will write flaky tests.
看下面这段代码:
// app code
$('button').on('click', (e) => {
// do something synchronously randomly
if (Math.random() < 0.5) {
// append an input
$('<input />').appendTo($('body'))
} else {
// or append a textarea
$('<textarea />').appendTo($('body'))
}
})
应用程序会随机的在 body 里插入 input 或者 textarea.
我们可以使用如下代码进行条件测试:
// click the button causing the new
// elements to appear
cy.get('button').click()
cy.get('body')
.then(($body) => {
// synchronously query from body
// to find which element was created
if ($body.find('input').length) {
// input was found, do something else here
return 'input'
}
// else assume it was textarea
return 'textarea'
})
.then((selector) => {
// selector is a string that represents
// the selector we could use to find it
cy.get(selector).type(`found the element by selector ${selector}`)
})
在应用达到一个稳定的状态前,我们应采取措施,阻止 Cypress 进一步执行命令。
网友评论