Cypress测试自动化的最佳实践

1,105 阅读24分钟

Cypress是一个了不起的框架,用于测试你的前端应用程序。然而,你可能会犯一些错误,导致你的开发和测试过程变慢。 你可能会面临难以超越的挑战,比如处理认证和处理网络服务器,或者更糟糕的是,处理第三方认证供应商等。

在这篇关于Cypress最佳实践的博客中,我将向你展示Cypress的最佳实践以及人们在进行Cypress E2E测试时编写测试代码的常见错误。我还将告诉你如何避免这些错误,使你的开发过程更快,没有错误,并不断重构代码。

以下是博客中的关键学习点。

  • 如何根据真实的应用来建立你的测试模型?
  • 如何在隔离状态下编写有效的测试?
  • 将自己从页面对象中解放出来
  • 选择一个有效的测试策略来登录你的应用程序
  • 如何利用对其状态的直接访问?

Cypress总体上是令人惊奇的,但如果你没有用正确的方法,没有遵循Cypress的最佳实践,你的测试性能将急剧下降,你将引入不必要的错误,你的测试代码将是不可靠的和飘忽的。

例如,你的代码可能今天还能工作,而明天在第三方供应商给他们的网站添加了一些变化后就会崩溃。

本博客将教你赛普拉斯的最佳实践,让你在进行赛普拉斯测试时,永远不犯这样的错误,写出可靠、高质量的测试代码。

什么是Cypress?

Cypress也被广泛用于E2E(端到端)测试,因此你不必将你的测试分开,分别编写你的前端、后端和数据库测试。相反,你可以复制真实的用户场景,使用Cypress进行端到端测试

根据JS 2021的状态调查,Cypress是第三大最受欢迎的测试框架,表明许多开发人员和QA工程师正在转向Cypress测试自动化框架

What is Cypress?

资料来源

虽然Cypress是一个相对较新的孩子;但它已经迅速确立了其作为JavaScript应用程序首选测试自动化框架的地位,这一点从GitHub上该项目的Forks(2.4K)和Stars(38.9K)的数量中可以看出。Cypress的流行可以归功于一些方便的功能,如运行时检查器、时间旅行调试、并行运行测试的能力和插件。

Cypress不断增加的下载量充分说明了这个开源测试自动化框架的受欢迎程度。

ever-increasing downloads for Cypress

此外,Cypress社区是一个繁荣的环境,具有大量的学习机会。它管理良好,为你提供了接触软件测试和网络开发领域顶尖人才的机会。除了活跃的Cypress社区外,还有Cypress大使,你可以向他们学习。Cypress大使是经验丰富的开发人员和工程师,他们使用Cypress创建了令人惊叹的应用程序。

Selenium等其他测试框架相比,Cypress正在加快步伐。如果你来自Selenium背景,并想了解更多关于Cypress自动化工具的信息,你可以查看Selenium与Cypress的比较。

Cypress CTA.

Cypress测试的最佳实践

自动化测试中,有一件事被深情地提及:"再多的坏代码也不能用自动化来解决。"这实质上意味着,如果我们遵循用于编写自动化测试的框架的最佳实践,就能从测试自动化中获得最大的投资回报

根据我在Cypress UI测试方面的经验,这里有一些Cypres的最佳实践,以避免Cypress中的反模式,应该利用这些反模式来提出一流的自动化测试。

以编程方式登录

反模式。使用用户界面登录
最佳实践。以编程方式登录应用程序并广泛使用应用程序的状态

在测试需要认证的网页时,人们倾向于做的一件非常普遍的事情是通过用户界面登录,然后重定向到需要测试的页面。

比如说。

describe("Testing protected routes", () => {
 beforeEach(() => {
   cy.visit("/login")
   cy.get("data-cy=email").type("jon@example.com")
   cy.get("data-cy=password").type("password")
   cy.get("data-cy=submit").click()
 })
})

这样就可以完成你的工作。但这样做的问题是,这使用了你的应用程序UI进行认证,认证完成后,重定向到你想要的页面。
这也意味着登录页面必须在你想要测试的任何其他特定页面之前工作。这种写测试的风格不是孤立的,这不属于Cypess最佳实践。

比方说,你想测试设置页面。要做到这一点,你要登录并引入登录页面,这意味着你孤立地测试失败。如果你有数百个页面,这也将是非常耗时和适得其反的。如果登录和重定向到所需页面的过程需要1.0秒,如果你有一百个页面,它将增加100秒的测试时间。

对此,更好的方法是以编程方式登录。这意味着什么呢?

首先让我们看看当有人登录时,认证是如何发生的。大多数情况下,用户通过POST请求向后端服务器发送电子邮件和密码,服务器会将用户数据和一个令牌发回给客户端。

客户端将该令牌保存在浏览器的本地存储中,并在每次向后端发送另一个请求时在授权头中发送该令牌。这样,后端就可以识别哪个用户发送了请求。

为了以编程方式登录,我们需要使用另一个名为Cypress request**cy.request()**的命令。该方法在浏览器的约束之外发出HTTP请求。它不受 CORS 或任何其他安全限制的约束。

让我来谈谈什么是CORS?CORS是跨源资源共享的意思。它是一种基于HTTP头的机制,帮助服务器指明浏览器发送请求的来源。大多数服务器只允许来自特定的可信来源的请求。

回到cy**.request()**,**cy.request()**的厉害之处在于它使用了浏览器的用户代理和cookie存储,所以它的行为与请求确实来自浏览器完全一样,但它不受限制。

简而言之,**cy.request()cy.visit()**的区别在于,**cy.visit()重定向并使用浏览器来访问指定的URL,这意味着当你用cy.visit()**访问一个URL时,它将在浏览器中打开并下载页面的所有资产,运行所有的JavaScript代码。

另一方面,**cy.request()**只向一个URL发送HTTP请求;你不能直观地看到它,它也不会下载任何网站资产或运行任何JavaScript代码。

我们现在可以做的是使用**cy.request()**向我们的后台服务器发送一个POST请求,并在请求体中加入电子邮件和密码,在我们得到响应后,我们会将令牌保存在浏览器的本地存储中。

describe("Testing protected routes", () => {
  beforeEach(() => {
    cy.request("http://localhost:3000/api/auth/login", {
      email: "jon@example.com",
      password: "password",
    }).then((r) => {
      window.localStorage.setItem("token", r.body.token)
    })
  })
})

这在每次测试特定页面时都会发送一个请求。

Cypress的最佳实践之一是为我们的登录代码建立一个自定义命令。

Cypress.Commands.add("login", (email, password) => {
  cy.request("http://localhost:3000/api/auth/login", {
    email,
    password,
  }).then((r) => {
    window.localStorage.setItem("token", r.body.token)
  })
})

并在我们的测试代码中重新使用我们的自定义命令。

describe("Testing protected routes", () => {
  beforeEach(() => {
    cy.login("jon@example.com", "password")
  })
})

现在,这是一个更好的做法,比使用用户界面登录要快得多。它为你节省了大量的时间;由于我们不依赖于可能在开发过程中发生变化的选择器,所以它更具有可维护性和可靠性。

选择最适合的定位器

反模式。使用可能改变的选择器
最佳实践。使用data-*属性来提供上下文,以表明哪些选择器是用于测试的

编写测试时最常见的错误之一是使用过于脆弱和易变的选择器。这包括与CSS样式和JavaScript事件监听器耦合的选择器。

例如,让我们使用LambdaTest的eCommerce Playground来运行一个使用脆性选择器的测试(不建议)。

describe("Testing home page", () => {
  it("should go to product page", () => {
    cy.visit("<https://ecommerce-playground.lambdatest.io/>")
    cy.get("#mz-product-listing-image-97213254-0-1").click()
  })
})

在这个例子中,我们想点击 "每日优惠 "滑块的第一个元素,上面的代码的问题是,它使用元素ID来点击它。

这将工作(目前),但更多的产品将在未来被添加到滑块。它不能保证这个产品会出现在那里,所以Cypress不能找到一个具有给定id的元素,测试就会失败。要了解更多关于在Cypress中寻找元素的信息,你可以阅读这个关于使用Cypress定位器寻找HTML元素的博客。

你可以使用Cypress的最佳实践,像**data-cy="first-slider-item "作为元素属性,并在Cypress中使用cy.get('[data-cy="first-slider-item"]')**来获得对该元素的访问。

现在可以保证,无论id是否改变或元素的样式是否改变,代码都会一直运行。

你应该把你的代码重构成这样。

describe("Testing home page", () => {
  it("should go to product page", () => {
    cy.visit("https://ecommerce-playground.lambdatest.io/")
 
    cy.get('[data-cy="first-slider-item"]').click()
  })
})

使用过于通用的选择器的问题是,一开始可能会起作用,因为你没有非常复杂的UI,但是当你在应用程序中扩展和添加更多的功能时,你很容易破坏你的选择器。

这样一来,你可能会引入破碎的选择器和不必要的失败测试,而这些测试实际上应该被认为是通过的。

例如,假设你想选择一个元素按钮 并点击它。下面是一些你可以做到的方法,以及为什么你应该使用它们或不使用它们。

  • **cy.get('button').click()。**这太通用了,页面中可能有不止一个按钮,或者你将来可能会添加更多的按钮。
  • **cy.get('.btn.btn-primary').click()。**这个也是可以改变的。你可能想在将来改变类来更新按钮的样式。
  • **cy.get('#main').click()。**更好,但不是最好的,因为它与CSS样式和JavaScript事件监听器有关。
  • **cy.get('[name=submission]').click()。**与名称属性相联系,它具有HTML语义。
  • **cy.contains('submit').click()。**这要好得多,但这是与HTML元素的内容相联系的。这取决于你是否真的希望按钮包含 "提交"。如果你想让测试在不包含 "Submit "时失败,那么你应该使用它。
  • **cy.get('[data-cy=submit]').click()。**最佳实践,不受任何可能的改变。
推荐使用不推荐
✅ data-cy❌
✅ 数据测试❌ id
✅ data-testid❌ 名称

使用data-cy、data-test或data-testid可以让大家清楚地看到这个元素是直接被测试代码使用的。

代码示例。

// cypress/support/commands.ts
Cypress.Commands.add("getBySelector", (selector, ...args) => {
 return cy.get(`[data-test=${selector}]`, ...args)
})
Cypress.Commands.add("getBySelectorLike", (selector, ...args) => {
 return cy.get(`[data-test*=${selector}]`, ...args)
})
  • getBySelector产生具有data-test属性的元素,与指定的选择器相匹配。
  • getBySelectorLike产生具有data-test属性的元素,它包含一个指定的选择器。

编写TypeScript的类型。

declare global {
  namespace Cypress {
    interface Chainable {
      getBySel: (
        selector: string,
        ...args: Partial<Loggable & Timeoutable & Withinable & Shadow>[]
      ) => Chainable<any>
 
      getBySelLike: (
        selector: string,
        ...args: Partial<Loggable & Timeoutable & Withinable & Shadow>[]
      ) => Chainable<any>
    }
  }
}

在3000多个环境中运行Cypress脚本。现在就试试LambdaTest!

指派返回值

反模式。指定Cypress命令的返回值
最佳实践。使用闭包和别名

Cypress 不同步运行,这意味着你永远不能分配任何 Cypress 命令的返回值。

不要分配或处理任何Cypress命令的返回值;命令被排队,并以异步方式运行。如果依赖返回值,不能保证测试的行为是相同的。

这是人们大多犯的一个常见错误。

// this won't work 
// do not write your code like this
const button = cy.get('button')
const form = cy.get('form')
button.click()

由于命令被排队并异步运行,这段代码不起作用。千万不要给Cypress命令分配任何值。

但是不要担心...

先别慌,Cypress还为我们提供了一些其他的技术,我们可以用这些技术来获取任何选定元素的值。

如何访问任何Cypress命令的返回值?

有一些方法,你可以访问任何Cypress命令的返回值。

  • 使用闭包
  • 使用变量
  • 使用别名

闭包

如果你使用过足够多的JavaScript,你肯定对JavaScript承诺和如何使用它们相当熟悉。

你可以使用**.then()**命令访问每个Cypress命令所产生的值。

例子。

让我们使用这个简单的表单演示来运行一个使用闭包的简单测试。基本上,我们想从DOM的一个随机元素中抓取一个文本,并在一个输入中输入该元素,该元素也将在一个不同的div元素中显示该文本。

这只是在我们的代码中使用闭包的一个简单例子。

it("should be equal", () => {
  cy.visit("https://www.lambdatest.com/selenium-playground/simple-form-demo")
 
  cy.get("h2").then(($h2) => {
    const text = $h2.text() // gets the text in the p tag
 
    cy.get("input").first().type(text)
 
    // click the button that will show the input
    cy.get("#showInput").click()
 
    // get the div which contains the message
    // using another closure to get the text
    cy.get("#message").then((message) => {
      expect(message.text()).to.equal(text)
    })
  })
})

正如你所看到的,我们在获得元素h2后使用了.then(),我们还使用了**.text()**,它只能在返回的元素上访问,在这个例子中是$h2。这将返回DOM中第一个h2元素中的文本值。

在我们得到第一个h2元素的文本后,我们要在第一个输入元素中输入该文本,并点击按钮将其显示在其他div上。我们还断言,消息中的文本应该与第一个h2中的文本相等。

message should be equal to the text in the first h2

正如你所看到的,两个文本是相等的,这就是为什么我们的测试通过了。

both texts are equal

变量
在你的大部分代码中,你不会使用变量,或者你几乎不会使用它们,但变量也有自己的用例,有时也是很重要的。

如果你的应用程序的状态在运行测试代码的过程中发生了变化,那么你可能想使用变量来比较你之前的状态值和下一个状态值。

例子。

让我们写一段简单的HTML代码,其中包含一个保持一个计数器状态值的跨度和一个增加计数器的按钮。

<button>increment</button>
counter = <span id="num">0</span>
<script>
 var counter = 0
 document.getElementById("num").innerHTML = counter
 document
   .getElementsByTagName("button")[0]
   .addEventListener("click", function () {
     counter++
     document.getElementById("num").innerHTML = counter
   })
</script>

我们想用Cypress来比较上一个状态和下一个状态,并作出断言,以确保每次点击按钮时数值都会增加。

cy.get("#num").then(($span) => {
 // the current number
 const num1 = parseInt($span.text())
 cy.get("button")
   .click()
   .then(() => {
     // the number after the state updates
     const num2 = parseInt($span.text())
     expect(num2).to.eq(num1 + 1)
 })
})

正如你所看到的,我们首先用**.text()**获取span中的值,然后点击按钮使其递增,最后比较新值与旧值+1。

别名

有时你想重新使用你在钩子里面运行的Cypress命令的返回值,比如beforebeforeEach

使用**.then()**当然无济于事。

beforeEach(() => {
  cy.get("button").then(($btn) => {
    const text = $btn.text()
  })
})
 
it("does not have access to text", () => {
  // how do we get access to text ?
  // .then() is not useful in this scenario.
})

你可以通过使用**.as()**命令共享你想要的任何值的上下文。

**.as()**命令让你指定一个别名供以后使用;它产生的主题与前一个命令给它的主题相同。

然后你可以用**this.alias或cy.get('@alias')**访问该别名,并在开头加一个@。

beforeEach(() => {
 // alias the $btn.text() as 'text'
 cy.get("button").invoke("text").as("text")
})
 
it("has access to text", function () {
 expect(this.text).to.equal("Click me")
})

拥有测试隔离

反模式。让测试相互依赖
最佳实践。测试应该总是能够独立于任何其他测试而运行。

假设你想测试一个特定的输入是否存在,填入文本输入,然后提交表单。

这包括三个测试。下面是大多数人的做法,这不是Cypress的最佳实践,你应该避免这样做。

describe("Texts should be equal", () => {
 it("should visit lambdatest form demo", () => {
cy.visit("https://www.lambdatest.com/selenium-playground/simple-form-demo")
 })
 it("should find the input", () => {
   cy.get("#user-message").should("exist")
 })
 it("should type in the input", () => {
   cy.get("#user-message").type("Hello World")
 })
 it("should click the button", () => {
   cy.get("#showInput").click()
 })
 
 it("should show the message", () => {
   cy.get("#message").should("contain", "Hello World")
 })
})

这段代码有什么问题?为什么这是个坏主意?

这种测试你的代码的方法取决于应用程序的前一个状态,例如,**.should("contain", "Hello World")**这一步取决于点击按钮的前一个步骤,这也取决于输入的前一个状态。

这些步骤显然是相互依赖的,而且完全孤立地失败,这在编写测试时是至关重要的。

相反,你应该将所有这些步骤合并到一个测试中。

下面是推荐的方法。

describe("Texts should be equal", () => {
  it("should fill in the form and show the message", () => {
    cy.visit("https://www.lambdatest.com/selenium-playground/simple-form-demo")
 
    cy.get("#user-message").should("exist")
 
    cy.get("#user-message").type("Hello World")
 
    cy.get("#showInput").click()
 
    cy.get("#message").should("contain", "Hello World")
  })
})

如果你认为你需要以不同的方式运行其他一些测试,通过使用beforeEach共享一些代码是一个好主意。

beforeEach 钩子在运行任何测试之前运行里面的代码。

describe("Testing login page ...", () => {
  beforeEach(() => {
    cy.visit("/login")
  })
  it("test 1", () => {
    // ...
  })
 
  it("test 2", () => {
    // ...
  })
})

例如,在这段代码中,Cypress将在运行it块内的任何代码之前访问登录页面。

用一个断言创建小型测试

反模式。对同一元素用一个断言创建不同的测试
最佳实践。在同一个测试中添加多个断言

Cypress与运行单元测试不同,也不一样,它运行一系列异步生命周期事件,在测试之间重置状态。

这意味着在一个测试中编写单个断言会使你的测试运行得很慢,并导致真正的糟糕性能。

添加多个断言要比创建不同的测试快得多;因此,不要害怕在一个测试中添加多个断言。

如果你想知道哪些测试失败了呢?难道你不需要为每个测试写不同的标题吗?答案是NO。因为你将能够 "直观地 "看到哪些测试失败了,你不需要在不同的测试中写每一个断言,你可以很容易地在一个测试中创建多个断言。

按照赛普拉斯的说法,他们认为在赛普拉斯测试中出现30个以上的命令是相当普遍和正常的。

这是一个简单的例子,说明编写多个断言的正确用法。

describe("Testing login page", () => {
  it("should type and validate inputs", () => {
    cy.visit(
   "https://ecommerce-playground.lambdatest.io/index.php?route=account/login"
    )
 
    cy.get("#input-email")
      .type("jon@doe.com")
      .should("have.value", "jon@doe.com")
      .and("include.value", "@")
      .and("not.have.value", "jon@doe")
  })
})

不必要的等待

反模式。等待一个任意的时间段
最佳实践。不在你的大部分测试中使用 cy.wait()

在Cypress中,你几乎不需要为任何事情使用**cy.wait()**一个任意的数字。Cypress 用户似乎经常这样做,但幸运的是,有更好的方法可以做到这一点。

你永远不应该在以下任何命令中使用cy.wait()

  • cy.request()
  • cy.visit()
  • cy.get()

命令**cy.request()**在收到服务器的响应之前不会解析,所以增加一个任意的等待时间是完全没有必要的。

这是Cypress的一个伟大功能,也是Cypress的最佳实践之一。如果你为一个请求设置一个2秒的任意数字,而请求需要0.1秒,你将使测试过程慢20倍。

另外,如果你等待1秒,而现在请求需要2秒,你会得到一个错误,因为请求还没有解决。所以Cypress让这一点变得非常简单,你可以使用**cy.request()**而不用担心等待它的解决。

**cy.visit()**的情况也是如此。它只有在每一个资产都被加载时才会解析,包括JS和CSS文件。

网络服务器

反模式。使用 Cypress 命令 cy.exec() 和 cy.task() 启动 Web 服务器
最佳做法。在运行Cypress之前启动Web服务器

每次运行cy.exec()cy.task() 时,该进程最终必须退出。如果没有,Cypress 将不会继续执行任何其他命令。

如果你用Cypress启动一个服务器,你会引入很多问题,因为。

  • 你必须对进程进行后台管理
  • 不能通过终端访问
  • 没有对日志的有效访问
  • 每次你的测试运行时,你都要围绕启动一个已经运行的网络服务器来解决复杂的问题
  • 端口冲突

使用**after()钩子可以解决你的问题并关闭服务器,但 after() 钩子只在测试完成后运行。而且,也不能保证after()**钩子每次都能运行。

因为你总是可以在Cypress中重新启动/刷新,那么after钩子中的代码就不会总是运行。

你应该怎么做呢?

你应该在运行Cypress测试之前启动你的服务器,并在测试结束时关闭它。

我们推荐使用**[wait-on](www.npmjs.com/package/wai…

npm start & wait-on http://localhost:8080

http://localhost:8080

然后运行你的测试。

Then run your tests:

使用全局 baseUrl

反模式。使用 cy.visit() 而不使用 baseUrl
最佳做法。在你的 cypress.json 中设置一个 baseUrl

设置一个全局的baseUrl可以使你在每次使用Cypress命令cy.request()和cy.visit()的时候不用硬编码URL。

此外,如果你不设置一个全局baseUrl,Cypress将自动转到**https://localhost**\+ 一个随机端口,这将显示一个错误。通过配置一个baseUrl,你可以避免在Cypress第一次打开时看到这个错误。

如果你没有在你的cypress.json中配置一个baseUrl,这里是你应该重新编写的代码。

比方说,你已经访问了登录页面。

cy.visit("http://localhost:3000/login")
Change it to:
cy.visit("/login")
cypress.json
{
	...
	"baseUrl": "http://localhost:3000"
	...
}

访问外部网站

反模式。访问第三方网站
最佳实践。只测试你控制的东西,并在第三方API中使用cy.request()

你应该始终避免使用**cy.visit()**来访问任何外部网站,并尽量避免与用户界面进行交互。

为什么你永远不应该使用**cy.visit()**和用户界面与第三方网站和服务器进行交互?

  • 它需要大量的时间并拖慢你的速度。
  • 网站可能会在你不知道的情况下发生变化。
  • 他们的网站可能有问题。
  • 他们可能因为自动化而想阻止你。
  • 他们可能正在进行A/B活动。

测试用OAuth登录

这里有一些方法可以代替使用cy.visit来处理第三方供应商的登录问题。

  • 使用存根

你可以把OAuth提供商存根出来。使用这个的原因是欺骗你的应用程序,使其相信OAuth提供者已经将令牌送回给应用程序。

如果你必须得到一个真正的令牌,如果提供者的API变化较少,建议使用cy.request;即使有变化,你也会得到通知。

使用after或afterEach钩子

反模式。使用 after 或 afterEach 钩子来清理状态
最佳实践。在运行测试前清理状态

我们看到人们在测试结束后立即写下他们的状态清理。这可能会导致多种问题,包括引入不必要的失败测试和减慢你的测试性能。

下面是一个大多数初学者倾向于这样做的例子,不建议这样做。

afterEach(()=> {
  cy.cleanDb()
})

it('should add a new user', ...)
it("should some other stuff", ...)
it('should do something else', ...)

虽然这段代码看起来很好,但实际上不是,因为它不能保证afterEach钩子里面的任何代码都会运行。这可能会给你的应用程序留下一个不想要的状态。

为什么afterEach不能保证运行?

假设你在测试过程中刷新了你的浏览器,这将瞬间重启你的测试,而不运行afterEach钩子里面的任何代码,给你留下一个不想要的状态。

所以,当你下次开始你的测试过程时,你会遇到许多错误和失败的测试,因为在你刷新/关闭测试时,之前的测试产生的旧状态。

让我们来看看另一个大多数人倾向于写的代码例子,这也是不推荐的

beforeEach(() => {
 cy.login()
})
 
afterEach(() => {
 cy.logout()
})
 
it('some', ...)
it('more', ...)
it('tests', ...)

这段代码将为每个测试登录和注销用户,这有时是不必要的。

Cypress留给你的是前一个测试留下的相同状态。这是Cypress的最佳实践之一,总是利用这种状态,并在此基础上编写你的测试。

我们不必担心以后的调试问题,因为Cypress的调试与其他任何测试库都不同。它具有无可比拟的可调试性,可以帮助你以这种风格编写你的测试。

你应该怎么做,而不是?

尽可能地避免使用afterEachafter。这样你就可以利用以前的测试状态,运行你的测试要快得多,性能也高得多。

如果你想清理状态,请在开始测试前进行,也就是说,把它放在beforeEach块中。这样,你将始终确保你的测试是在一个干净和未触及的状态下开始的。

额外的提示 使用云Cypress网格来进行大规模的测试

赛普拉斯的一个缺点是,你不能使用赛普拉斯同时驱动两个浏览器。这就是云Cypress网格可以带来巨大好处的地方,因为它可以帮助你运行并行测试,进行大规模的测试。

像LambdaTest这样的Cypress云网格允许你大规模地进行Cypress测试。LambdaTest允许你在一个由40多个浏览器和操作系统组成的在线浏览器场上执行自动化的跨浏览器测试,以可扩展的方式加快测试执行。此外,它还增加了测试覆盖率,提高了产品质量。

你也可以订阅LambdaTest的YouTube频道,随时了解围绕自动化浏览器测试Selenium测试、CI/CD等的最新教程。

在这个例子中,我将向你展示如何使用LambdaTest运行平行的Cypress浏览器。我们将使用LambdaTest的eCommerce Playground来访问注册页面并创建一个断言。

// /cypress.json
{
  "baseUrl": "https://ecommerce-playground.lambdatest.io"
}
// /cypress/integration/test.spec.ts
describe("Testing register page", () => {
  it("should find a text", () => {
    cy.visit("/index.php?route=account/register")
 
    cy.contains("Register Account").should("exist")
  })
})

在LambdaTest上运行你的Cypress测试,请使用以下命令安装LambdaTest Cypress CLI。

lambdatest-cypress-cli

然后,运行以下命令。

npm install -g lambdatest-cypress-cli

Run your Cypress test on LambdaTest

设置你想运行测试的配置- 一旦你安装了lambdatest-cypress CLI,现在你需要设置配置。你可以用下面的命令来做。

lambdatest-cypress init

这将把配置放在lambdatest-config.json里面**。**

如果你使用TypeScript,别忘了在npm依赖中添加指定版本的typecript。

"npm_dependencies": {
         "cypress": "9.0.0",
         "typescript": "^4.7.2" // don't forget to add this line
      }

这是一个LambdaTest配置文件的例子。不要忘记用有效的证书更新用户名和访问密钥。你可以在登录LambdaTest后在LambdaTest配置文件部分找到同样的内容。

如浏览器数组所示,我们已经指定了两个具有指定操作系统的浏览器。

我们还指定了并行的值为5,这意味着LambdaTest将在不同的浏览器中自动运行这些测试,最多可有5个并行测试。

现在是在LambdaTest中运行Cypress UI自动化测试的时候了。为此,请使用以下命令。

lambdatest-cypress run

这将自动把你的测试上传到安全的LambdaTest Cypress网格,并帮助你进行Cypress并行测试

访问自动化仪表板,查看测试的状态。

Automation Dashboard

你将能够在那里看到你的测试,并看到测试期间记录的日志和视频。

你可以通过这个关于Cypress的网络研讨会进一步深化你的知识,它将帮助你用Cypress进行可扩展的、可靠的跨浏览器测试

如果你是一名开发人员或测试人员,并希望将你的Cypress专业知识提高到新的水平,你可以参加这个Cypress 101认证,并保持领先一步。

下面是LambdaTest公司对Cypress 101认证的简短介绍。

结论

如果使用得当,遵循最佳实践,Cypress是一个伟大的测试框架。

遵循Cypress的一些最佳实践可能是令人恼火的,或者有些难以实施。但从长远来看,它们肯定会得到回报,并在执行Cypress E2E测试时为你节省大量时间。否则,你会引入错误和失败的测试,并减慢进程。

遵循这些Cypress最佳实践将使你的测试更加有效,给你一个无缝的测试体验,而不会在未来引入错误或失败。

如果你想探索Cypress提供的大量功能,请查看详细的Cypress测试教程

常见问题(FAQ)

如何在Cypress中写一个好的测试?

在Cypress中编写测试时,有几件事需要记住。首先,用Cypress编写的测试可以使用与用JavaScript编写的测试相同的功能。这意味着你可以在你用TypeScript编写的测试中使用任何Cypress命令和断言。第二,只写的API是在Cypress中编写测试的最简单方式。如果你需要,只读API仍然可用,但不建议大多数测试人员和开发人员使用。

你把Cypress测试放在哪里?

默认情况下,测试文件位于 cypress/e2e 中。然而,这可以被配置为一个不同的目录。

Cypress是一个BDD吗?

Cypress是一个基于Node.js的BDD/TDD网络应用程序框架,用于测试API、网站、网络应用程序和一般的软件。它直接从浏览器为你的测试提供有价值的数据,如屏幕截图、日志和位置。