Skip to main content
Cypress应用

断言

info
你将学习到
  • Cypress支持的Chai、Chai-jQuery和Sinon-Chai断言
  • 如何为常见用例编写断言
  • 如何链式组合断言

Cypress内置了流行的Chai断言库,以及针对SinonjQuery的扩展,为您提供了数十种强大的断言功能。

tip

如果想了解如何使用这些断言,请阅读Cypress入门指南中的断言部分。

Chai

https://github.com/chaijs/chai

以下链式断言可用于BDD风格断言(expect/should)。列出的别名可与原始链式断言互换使用。完整BDD Chai断言列表参见此处

断言链示例
not.should('not.equal', 'Jane')
expect(name).to.not.equal('Jane')
deep.should('deep.equal', { name: 'Jane' })
expect(obj).to.deep.equal({ name: 'Jane' })
nested.should('have.nested.property', 'a.b[1]')
.should('nested.include', {'a.b[1]': 'y'})
expect({a: {b: 'x'}}).to.have.nested.property('a.b')
expect({a: {b: 'x'}}).to.nested.include({'a.b': 'x'})
ordered.should('have.ordered.members', [1, 2])
expect([1, 2]).to.have.ordered.members([1, 2])
expect([1, 2]).not.to.have.ordered.members([2, 1])
any.should('have.any.keys', 'age')
expect(arr).to.have.any.keys('age')
all.should('have.all.keys', 'name', 'age')
expect(arr).to.have.all.keys('name', 'age')
a(类型)
别名: an
.should('be.a', 'string')
expect('test').to.be.a('string')
include()
别名: contain, includes, contains
.should('include', 2)
expect([1,2,3]).to.include(2)
ok.should('not.be.ok')
expect(undefined).to.not.be.ok
true.should('be.true')
expect(true).to.be.true
false.should('be.false')
expect(false).to.be.false
null.should('be.null')
expect(null).to.be.null
undefined.should('be.undefined')
expect(undefined).to.be.undefined
exist.should('exist')
expect(myVar).to.exist
empty.should('be.empty')
expect([]).to.be.empty
arguments
别名: Arguments
.should('be.arguments')
expect(arguments).to.be.arguments
equal()
别名: equals, eq
.should('equal', 42)
expect(42).to.equal(42)
deep.equal().should('deep.equal', { name: 'Jane' })
expect({ name: 'Jane' }).to.deep.equal({ name: 'Jane' })
eql()
别名: eqls
.should('eql', { name: 'Jane' })
expect({ name: 'Jane' }).to.eql({ name: 'Jane' })
greaterThan()
别名: gt, above
.should('be.greaterThan', 5)
expect(10).to.be.greaterThan(5)
least()
别名: gte
.should('be.at.least', 10)
expect(10).to.be.at.least(10)
lessThan()
别名: lt, below
.should('be.lessThan', 10)
expect(5).to.be.lessThan(10)
most()
别名: lte
.should('have.length.of.at.most', 4)
expect('test').to.have.length.of.at.most(4)
within(起始, 结束).should('be.within', 5, 10)
expect(7).to.be.within(5, 10)
instanceOf(构造函数)
别名: instanceof
.should('be.instanceOf', Array)
expect([1, 2, 3]).to.be.instanceOf(Array)
property(名称, [值]).should('have.property', 'name')
expect(obj).to.have.property('name')
deep.property(名称, [值]).should('have.deep.property', 'tests[1]', 'e2e')
expect(deepObj).to.have.deep.property('tests[1]', 'e2e')
ownProperty(名称)
别名: haveOwnProperty, own.property
.should('have.ownProperty', 'length')
expect('test').to.have.ownProperty('length')
ownPropertyDescriptor(名称)
别名: haveOwnPropertyDescriptor
.should('have.ownPropertyDescriptor', 'a')
expect({a: 1}).to.have.ownPropertyDescriptor('a')
lengthOf().should('have.lengthOf', 4)
expect('test').to.have.lengthOf(4)
match(正则表达式)
别名: matches
.should('to.match', /^test/)
expect('testing').to.match(/^test/)
string(字符串).should('have.string', 'test')
expect('testing').to.have.string('test')
keys(键1, [键2], [...])
别名: key
.should('have.keys', 'pass', 'fail')
expect({ pass: 1, fail: 2 }).to.have.keys('pass', 'fail')
throw(构造函数)
别名: throws, Throw
.should('throw', Error)
expect(fn).to.throw(Error)
respondTo(方法)
别名: respondsTo
.should('respondTo', 'getName')
expect(obj).to.respondTo('getName')
itself.should('itself.respondTo', 'getName')
expect(Foo).itself.to.respondTo('bar')
satisfy(方法)
别名: satisfies
.should('satisfy', (num) => num > 0)
expect(1).to.satisfy((num) => num > 0)
closeTo(期望值, 误差范围)
别名: approximately
.should('be.closeTo', 1, 0.5)
expect(1.5).to.be.closeTo(1, 0.5)
members(集合).should('include.members', [3, 2])
expect([1, 2, 3]).to.include.members([3, 2])
oneOf(值列表).should('be.oneOf', [1, 2, 3])
expect(2).to.be.oneOf([1,2,3])
change(函数)
别名: changes
.should('change', obj, 'val')
expect(fn).to.change(obj, 'val')
increase(函数)
别名: increases
.should('increase', obj, 'val')
expect(fn).to.increase(obj, 'val')
decrease(函数)
别名: decreases
.should('decrease', obj, 'val')
expect(fn).to.decrease(obj, 'val')

以下获取器也可用于BDD断言。它们本身不执行任何操作,但能让您编写更清晰的英语句子。

可链式获取器
to, be, been, is, that, which, and, has, have, with, at, of, same

Chai-jQuery

https://github.com/chaijs/chai-jquery

这些链式断言用于DOM对象断言。

通常在使用了DOM命令如:cy.get()cy.contains()等后使用这些断言。

断言链断言示例
attr(名称, [值]).should('have.attr', 'bar')
expect($el).to.have.attr('foo', 'bar')
prop(名称, [值]).should('have.prop', 'disabled', false)
expect($el).to.have.prop('disabled', false)
css(名称, [值]).should('have.css', 'background-color', 'rgb(0, 0, 0)')
expect($el).to.have.css('background-color', 'rgb(0, 0, 0)')
data(名称, [值]).should('have.data', 'foo', 'bar')
expect($el).to.have.data('foo', 'bar')
class(类名).should('have.class', 'foo')
expect($el).to.have.class('foo')
id(ID).should('have.id', 'foo')
expect($el).to.have.id('foo')
html(HTML).should('have.html', 'I love testing')
expect($el).to.have.html('with Cypress')
text(文本).should('have.text', 'I love testing')
expect($el).to.have.text('with Cypress')
value().should('have.value', 'test@dev.com')
expect($el).to.have.value('test@dev.com')
visible.should('be.visible')
expect($el).to.be.visible
hidden.should('be.hidden')
expect($el).to.be.hidden
selected.should('be.selected')
expect($option).not.to.be.selected
checked.should('be.checked')
expect($input).not.to.be.checked
focus[ed].should('have.focus')
expect($input).not.to.be.focused
expect($input).to.have.focus
enabled.should('be.enabled')
expect($input).to.be.enabled
disabled.should('be.disabled')
expect($input).to.be.disabled
empty.should('be.empty')
expect($el).not.to.be.empty
exist.should('exist')
expect($nonexistent).not.to.exist
match(选择器).should('match', ':empty')
expect($emptyEl).to.match(':empty')
contain(文本).should('contain', 'text')
expect($el).to.contain('text')
descendants(选择器).should('have.descendants', 'div')
expect($el).to.have.descendants('div')

Sinon-Chai

https://github.com/domenic/sinon-chai

这些断言链用于cy.stub()cy.spy()的断言。

Sinon.JS属性/方法断言示例
called.should('have.been.called')
expect(spy).to.be.called
callCount.should('have.callCount', 3)
expect(spy).to.have.callCount(n)
calledOnce.should('have.been.calledOnce')
expect(spy).to.be.calledOnce
calledTwice.should('have.been.calledTwice')
expect(spy).to.be.calledTwice
calledThrice.should('have.been.calledThrice')
expect(spy).to.be.calledThrice
calledBefore.should('have.been.calledBefore', spy2)
expect(spy1).to.be.calledBefore(spy2)
calledAfter.should('have.been.calledAfter', spy2)
expect(spy1).to.be.calledAfter(spy2)
calledWithNew.should('have.been.calledWithNew')
expect(spy).to.be.calledWithNew
alwaysCalledWithNew.should('have.always.been.calledWithNew')
expect(spy).to.always.be.calledWithNew
calledOn.should('have.been.calledOn', context)
expect(spy).to.be.calledOn(context)
alwaysCalledOn.should('have.always.been.calledOn', context)
expect(spy).to.always.be.calledOn(context)
calledWith.should('have.been.calledWith', ...args)
expect(spy).to.be.calledWith(...args)
alwaysCalledWith.should('have.always.been.calledWith', ...args)
expect(spy).to.always.be.calledWith(...args)
calledOnceWith.should('have.been.calledOnceWith', ...args)
expect(spy).to.be.calledOnceWith(...args)
calledWithExactly.should('have.been.calledWithExactly', ...args)
expect(spy).to.be.calledWithExactly(...args)
alwaysCalledWithExactly.should('have.always.been.calledWithExactly', ...args)
expect(spy).to.always.be.calledWithExactly(...args)
calledOnceWithExactly.should('have.been.calledOnceWithExactly', ...args)
expect(spy).to.be.calledOnceWithExactly(...args)
calledWithMatch.should('have.been.calledWithMatch',...args)
expect(spy).to.be.calledWithMatch(...args)
alwaysCalledWithMatch.should('have.always.been.calledWithMatch',...args)
expect(spy).to.always.be.calledWithMatch(...args)
returned.should('have.returned', 'foo')
expect(spy).to.have.returned(returnVal)
alwaysReturned.should('have.always.returned', 'foo')
expect(spy).to.have.always.returned(returnVal)
threw.should('have.thrown', TypeError)
expect(spy).to.have.thrown(errorObjOrErrorTypeStringOrNothing)
alwaysThrew.should('have.always.thrown', 'TypeError')
expect(spy).to.have.always.thrown(errorObjOrErrorTypeStringOrNothing)

添加新断言

由于我们使用chai,这意味着您可以按需扩展它。Cypress会自动兼容添加到chai的新断言。您可以:

常见断言

以下是常见的元素断言列表。注意我们如何将上述断言与.should()结合使用。您可能还想阅读Cypress如何重试断言。

长度

// 重试直到找到3个匹配的<li.selected>
cy.get('li.selected').should('have.length', 3)

类名

// 重试直到此输入没有disabled类
cy.get('form').find('input').should('not.have.class', 'disabled')

// 重试直到此文本区域有正确的值
cy.get('textarea').should('have.value', 'foo bar baz')

文本内容

// 断言元素的文本内容完全匹配给定文本
cy.get('[data-testid="user-name"]').should('have.text', 'Joe Smith')
// 断言元素的文本包含给定子字符串
cy.get('[data-testid="address"]').should('include.text', 'Atlanta')
// 重试直到此span不包含'click me'
cy.get('a').parent('span.help').should('not.contain', 'click me')
// 元素的文本应以"Hello"开头
cy.get('[data-testid="greeting"]')
.invoke('text')
.should('match', /^Hello/)
// 使用cy.contains通过文本查找元素
// 匹配给定的正则表达式
cy.contains('[data-testid="greeting"]', /^Hello/)
info

**提示:**关于包含不间断空格实体的文本断言,请阅读如何获取元素的文本内容?

可见性

// 重试直到data-testid为"form-submit"的元素可见
cy.get('[data-testid="form-submit"]').should('be.visible')
// 重试直到文本为"write tests"的列表项可见
cy.contains('[data-testid="todo"] li', 'write tests').should('be.visible')

**注意:**如果存在多个元素,断言be.visiblenot.be.visible的行为不同:

// 重试直到某些元素可见
cy.get('li').should('be.visible')
// 重试直到所有元素不可见
cy.get('li.hidden').should('not.be.visible')

观看短视频"多个元素与should('be.visible')断言",了解如何正确检查元素的可见性。

存在性

// 重试直到加载旋转器不存在
cy.get('[data-testid="loading"]').should('not.exist')

状态

// 重试直到单选按钮被选中
cy.get(':radio').should('be.checked')

CSS

// 重试直到元素有匹配的CSS
cy.get('[data-testid="completed"]').should(
'have.css',
'text-decoration',
'line-through'
)
// 重试直到手风琴CSS有"display: none"属性
cy.get('[data-testid="accordion"]').should('not.have.css', 'display', 'none')

禁用属性

<input type="text" data-testid="example-input" disabled />
cy.get('[data-testid="example-input"]')
.should('be.disabled')
// 从测试中启用此元素
.invoke('prop', 'disabled', false)

cy.get('[data-testid="example-input"]')
// 可以使用"enabled"断言
.should('be.enabled')
// 或否定"disabled"断言
.and('not.be.disabled')

否定断言

断言分为肯定和否定两种。肯定断言的例子:

cy.get('[data-testid="todo-item"]')
.should('have.length', 2)
.and('have.class', 'completed')

否定断言在断言前加上"not"链。否定断言的例子:

cy.contains('first todo').should('not.have.class', 'completed')
cy.get('[data-testid="loading"]').should('not.be.visible')

误通过的测试

否定断言可能会因意外原因通过。假设我们要测试一个Todo列表应用在输入Todo并按下回车后是否添加了新Todo项。

肯定断言

当向列表添加元素并使用肯定断言时,测试断言应用中特定数量的Todo项。

如果应用行为异常,比如添加空白Todo而不是包含文本"Write tests"的新Todo,下面的测试可能仍会错误地通过。

cy.get('[data-testid="todos"]').should('have.length', 2)
cy.get('[data-testid="new-todo"]').type('Write tests{enter}')

// 使用肯定断言检查确切的项目数量
cy.get('[data-testid="todos"]').should('have.length', 3)

否定断言

但在下面的测试中使用否定断言时,当应用以多种意外方式行为时,测试可能会错误地通过:

  • 应用删除了整个Todo列表而不是插入第3个Todo
  • 应用删除了一个Todo而不是添加新Todo
  • 应用添加了空白Todo
cy.get('[data-testid="todos"]').should('have.length', 2)
cy.get('[data-testid="new-todo"]').type('Write tests{enter}')

// 使用否定断言检查不是某个数量的项目
cy.get('[data-testid="todos"]').should('not.have.length', 2)

Should回调

如果内置断言不够用,您可以编写自己的断言函数并将其作为回调传递给.should()命令。Cypress会自动重试回调函数直到通过或命令超时。参见.should()文档。

<div class="main-abc123 heading-xyz987">Introduction</div>
cy.get('div').should(($div) => {
expect($div).to.have.length(1)

const className = $div[0].className

// className将是类似"main-abc123 heading-xyz987"的字符串
expect(className).to.match(/heading-/)
})

多重断言

您可以在同一命令上附加多个断言。

<a
data-testid="assertions-link"
class="active"
href="https://on.cypress.io"
target="_blank"
>
Cypress Docs
</a>
cy.get('[data-testid="assertions-link"]')
.should('have.class', 'active')
.and('have.attr', 'href')
.and('include', 'cypress.io')

注意所有链式断言都使用对原始主题的相同引用。例如,如果要测试一个先出现后消失的加载元素,以下方法不适用,因为同一元素不能同时可见和不可见:

// ⛔️ 不适用
cy.get('[data-testid="loading"]').should('be.visible').and('not.be.visible')

正确的做法是拆分断言并重新查询元素:

// ✅ 正确方法
cy.get('[data-testid="loading"]').should('be.visible')
cy.get('[data-testid="loading"]').should('not.be.visible')

另请参阅