Skip to main content
Cypress应用

should

创建断言。断言会自动重试直到通过或超时。

info

.and() 的别名

info

注意: .should() 假设您已熟悉核心概念,如断言

语法

.should(chainers)
.should(chainers, value)
.should(chainers, method, value)
.should(callbackFn)

用法

正确用法

cy.get('.error').should('be.empty') // 断言 '.error' 为空
cy.contains('Login').should('be.visible') // 断言元素可见
cy.wrap({ foo: 'bar' }).its('foo').should('eq', 'bar') // 断言 'foo' 属性等于 'bar'

错误用法

cy.should('eq', '42') // 不应直接链式调用 'cy'

参数

chainers (String)

来自 ChaiChai-jQuerySinon-Chai 的任何有效链式断言。

value (String)

断言链式断言的值。

method (String)

链式断言上调用的方法。

callbackFn (Function)

传递一个可以包含任意数量显式断言的函数。传递给函数的内容即为所产生的内容。

生成结果 了解主题管理

在大多数情况下,.should() 产生与前一命令相同的主题。

cy.get('nav') // 产生 <nav>
.should('be.visible') // 产生 <nav>

然而,一些链式断言会改变主题。在下面的例子中,第二个 .should() 产生字符串 sans-serif,因为链式断言 have.css, 'font-family' 改变了主题。

cy.get('nav') // 产生 <nav>
.should('be.visible') // 产生 <nav>
.should('have.css', 'font-family') // 产生 'sans-serif'
.and('match', /serif/) // 产生 'sans-serif'

.should() 是一个断言,可以安全地链式调用使用主题的进一步命令。

示例

链式断言

断言复选框被禁用

cy.get(':checkbox').should('be.disabled')

当前DOM元素被产生

cy.get('option:first')
.should('be.selected')
.then(($option) => {
// $option 被产生
})

断言类名为 'form-horizontal'

cy.get('form').should('have.class', 'form-horizontal')

断言值不为 'Jane'

cy.get('input').should('not.have.value', 'Jane')

当前主题被产生

cy.get('button')
.should('have.id', 'new-user')
.then(($button) => {
// $button 被产生
})

方法和值

断言锚元素具有href属性

// have.attr 来自 chai-jquery
cy.get('#header a').should('have.attr', 'href')

断言href属性等于 '/users'

cy.get('#header a').should('have.attr', 'href', '/users')

注意: have.attr 断言将主题从原始元素更改为属性的值

cy.get('#header a') // 产生元素
.should('have.attr', 'href') // 产生 "href" 属性
.and('equal', '/users') // 检查 "href" 值

焦点

断言按钮点击后输入框获得焦点

cy.get('#btn-focuses-input').click()
cy.get('#input-receives-focus').should('have.focus') // 等同于 should('be.focused')

函数

.should() 传递函数使您能够对产生的主题进行多个断言。这也使您有机会调整您想要断言的内容。

确保不要在回调函数中包含任何有副作用的代码。回调函数会不断重试,直到其中的所有断言都不抛出错误为止。

不能在 .should() 回调函数中调用Cypress命令。应在 .should() 之前或之后使用Cypress命令。

错误用法

cy.get('p').should(($p) => {
cy.log($p)
// ...
})

正确用法

cy.get('p')
.should(($p) => {
// ...
})
.log()

// 或

cy.get('p').then(($p) => {
// ...
cy.log($p)
})

验证多个 <p> 的长度、内容和类

<div>
<p class="text-primary">Hello World</p>
<p class="text-danger">You have an error</p>
<p class="text-default">Try again later</p>
</div>
cy.get('p').should(($p) => {
// 应找到3个元素
expect($p).to.have.length(3)

// 确保第一个元素包含某些文本内容
expect($p.first()).to.contain('Hello World')

// 使用jquery的map获取所有类
// jquery的map返回一个新的jquery对象
const classes = $p.map((i, el) => {
return Cypress.$(el).attr('class')
})

// 调用classes.get()使其成为普通数组
expect(classes.get()).to.deep.eq([
'text-primary',
'text-danger',
'text-default',
])
})

警告.should() 回调函数返回的任何值都将被忽略。原始主题将被传递给下一个命令。

cy.get('p')
.should(($p) => {
expect($p).to.have.length(3)

return 'foo'
})
.then(($p) => {
// 参数 $p 将是3个元素,而不是 "foo"
})

断言类名包含 heading-

<div class="docs-header">
<div class="main-abc123 heading-xyz987">Introduction</div>
</div>
cy.get('.docs-header')
.find('div')
// .should(cb) 回调函数将被重试
.should(($div) => {
expect($div).to.have.length(1)

const className = $div[0].className

expect(className).to.match(/heading-/)
})
// .then(cb) 回调不会被重试,
// 它要么通过,要么失败
.then(($div) => {
expect($div).to.have.text('Introduction')
})

您甚至可以从回调函数中抛出自己的错误。

cy.get('.docs-header')
.find('div')
.should(($div) => {
if ($div.length !== 1) {
// 可以抛出自己的错误
throw new Error('未找到1个元素')
}

const className = $div[0].className

if (!className.match(/heading-/)) {
throw new Error(`类名 "${className}" 中不包含 "heading-"`)
}
})

断言3个元素的文本内容

下面的示例首先断言有3个元素,然后检查每个元素的文本内容。

<ul class="connectors-list">
<li>Walk the dog</li>
<li>Feed the cat</li>
<li>Write JavaScript</li>
</ul>
cy.get('.connectors-list > li').should(($lis) => {
expect($lis).to.have.length(3)
expect($lis.eq(0)).to.contain('Walk the dog')
expect($lis.eq(1)).to.contain('Feed the cat')
expect($lis.eq(2)).to.contain('Write JavaScript')
})

为了清晰起见,您可以将字符串消息作为第二个参数传递给任何 expect 断言,参见 Chai#expect

cy.get('.connectors-list > li').should(($lis) => {
expect($lis, '3个元素').to.have.length(3)
expect($lis.eq(0), '第一个元素').to.contain('Walk the dog')
expect($lis.eq(1), '第二个元素').to.contain('Feed the cat')
expect($lis.eq(2), '第三个元素').to.contain('Write JavaScript')
})

这些字符串消息将显示在命令日志中,为每个断言提供更多上下文。

带有消息的Expect断言

比较两个元素的文本值

下面的示例获取一个元素中包含的文本并将其保存在闭包变量中。然后测试获取另一个元素中的文本,并在规范化后断言两个文本值相同。

<div class="company-details">
<div class="title">Acme Developers</div>
<div class="identifier">ACMEDEVELOPERS</div>
</div>
const normalizeText = (s) => s.replace(/\s/g, '').toLowerCase()

// 保存标题元素的文本
let titleText

cy.get('.company-details')
.find('.title')
.then(($title) => {
// 保存第一个元素的文本
titleText = normalizeText($title.text())
})

cy.get('.company-details')
.find('.identifier')
.should(($identifier) => {
// 可以在比较前处理文本
const idText = normalizeText($identifier.text())

// 标题元素的文本应已设置
expect(idText, 'ID').to.equal(titleText)
})

多个断言

链式多个断言

Cypress使链式断言更加容易。

在此示例中,我们使用 .and(),它与 .should() 相同。

// 我们的主题不会被第一个断言改变,
// 因此可以继续使用基于DOM的断言
cy.get('option:first').should('be.selected').and('have.value', 'Metallica')

等待断言通过

Cypress不会解析您的命令,直到所有断言都通过。

// 应用程序代码
$('button').click(function () {
$button = $(this)

setTimeout(() => {
$button.removeClass('inactive').addClass('active')
}, 1000)
})
cy.get('button')
.click()
.should('have.class', 'active')
.and('not.have.class', 'inactive')

注意事项

主题

如何知道哪些断言会改变主题,哪些会保持主题不变?

来自 ChaiChai-jQuery 的链式断言总是会记录它们的返回值。

使用回调函数不会改变产生的内容

函数中返回的任何内容都会被忽略。Cypress总是强制命令产生前一cy命令产生的内容(在下面的示例中是 <button>

cy.get('button')
.should(($button) => {
expect({ foo: 'bar' }).to.deep.eq({ foo: 'bar' })

return { foo: 'bar' } // 返回被忽略,.should() 产生 <button>
})
.then(($button) => {
// 可以对 <button> 进行任何操作
})

差异

.then().should()/.and() 有什么区别?

使用 .then() 允许你在回调函数中使用生成的主题(yielded subject),当你需要操作某些值或执行某些操作时应该使用它。

另一方面,当在 .should().and() 中使用回调函数时,会有特殊的逻辑来重新运行回调函数,直到其中没有断言抛出为止。你应当注意 .should().and() 回调函数中的副作用,避免你不希望被多次执行的操作。

规则

要求 了解命令链

  • .should() 需要链式调用前一命令。

超时设置 了解超时机制

  • .should() 将继续重试其指定的断言,直到超时。
cy.get('input', { timeout: 10000 }).should('have.value', '10')
// 这里的超时将传递给 '.should()'
// 它将重试最多10秒
cy.get('input', { timeout: 10000 }).should(($input) => {
// 这里的超时将传递给 '.should()'
// 除非断言更早抛出,
// 所有断言将重试最多10秒
expect($input).to.not.be('disabled')
expect($input).to.not.have.class('error')
expect($input).to.have.value('US')
})

命令日志

断言导航中应有8个子元素

cy.get('.left-nav>.nav').children().should('have.length', 8)

上述命令将在命令日志中显示为:

命令日志 should

点击命令日志中的 assert 时,控制台输出如下:

控制台日志 should

历史

版本变更
11.0.0如果在回调中使用Cypress命令则抛出错误
0.11.4允许回调函数参数
< 0.3.3添加 .should() 命令

另请参阅