Skip to content

Vue3 E2E测试2024:前端开发者端到端测试完整指南

📊 SEO元描述:2024年最新Vue3 E2E测试教程,详解Cypress、Playwright端到端测试方法。包含完整代码示例,适合前端开发者快速掌握Vue3应用E2E测试技巧。

核心关键词:Vue3 E2E测试2024、Cypress测试、Playwright测试、端到端测试、Vue3集成测试、前端自动化测试

长尾关键词:Vue3 E2E测试怎么写、Cypress配置Vue3项目、端到端测试最佳实践、Vue3自动化测试、E2E测试工具选择


📚 Vue3 E2E测试学习目标与核心收获

通过本节Vue3 E2E测试,你将系统性掌握:

  • E2E测试概念:深入理解端到端测试的价值和应用场景
  • Cypress框架使用:掌握Cypress在Vue3项目中的配置和使用
  • Playwright工具应用:学会使用Playwright进行跨浏览器测试
  • 用户流程测试:编写完整的用户交互流程测试用例
  • API集成测试:处理前后端集成的端到端测试场景
  • CI/CD集成:将E2E测试集成到持续集成流程中

🎯 适合人群

  • Vue3开发者的端到端测试技能提升和质量保障需求
  • QA工程师的自动化测试工具学习和实践应用需求
  • DevOps工程师的CI/CD流程优化和质量门禁设置需求
  • 项目负责人的产品质量管控和用户体验验证需求

🌟 Vue3 E2E测试是什么?为什么是质量保障的最后防线?

Vue3 E2E测试是什么?这是确保应用质量的最高级别测试。Vue3 E2E测试是端到端的自动化测试,模拟真实用户在真实浏览器环境中的完整操作流程,验证整个应用的功能正确性和用户体验。

Vue3 E2E测试的核心价值

  • 🎯 真实环境验证:在真实浏览器环境中测试应用的完整功能
  • 🔧 用户体验保障:从用户角度验证应用的可用性和流畅性
  • 💡 集成问题发现:发现单元测试和组件测试无法覆盖的集成问题
  • 📚 回归测试自动化:自动化验证新功能不会破坏现有功能
  • 🚀 部署信心提升:为生产环境部署提供最后的质量保障

💡 测试金字塔原则:E2E测试虽然执行时间长、维护成本高,但能够提供最高的信心保障,应该覆盖关键用户路径

Cypress框架使用:现代E2E测试的首选工具

Cypress环境配置

javascript
// 🎉 cypress.config.js - Cypress配置文件
import { defineConfig } from 'cypress'

export default defineConfig({
  e2e: {
    // 基础URL配置
    baseUrl: 'http://localhost:3000',
    
    // 视口配置
    viewportWidth: 1280,
    viewportHeight: 720,
    
    // 测试文件配置
    specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}',
    supportFile: 'cypress/support/e2e.js',
    
    // 截图和视频配置
    screenshotOnRunFailure: true,
    video: true,
    videosFolder: 'cypress/videos',
    screenshotsFolder: 'cypress/screenshots',
    
    // 超时配置
    defaultCommandTimeout: 10000,
    requestTimeout: 10000,
    responseTimeout: 10000,
    
    // 环境变量
    env: {
      apiUrl: 'http://localhost:8080/api',
      testUser: {
        email: 'test@example.com',
        password: 'password123'
      }
    },
    
    setupNodeEvents(on, config) {
      // 插件配置
      on('task', {
        // 自定义任务
        log(message) {
          console.log(message)
          return null
        },
        
        // 数据库操作任务
        seedDatabase() {
          // 数据库种子数据
          return null
        }
      })
      
      return config
    }
  }
})

Cypress基础测试用例

javascript
// 🎉 cypress/e2e/user-authentication.cy.js
describe('用户认证流程E2E测试', () => {
  beforeEach(() => {
    // 访问登录页面
    cy.visit('/login')
  })
  
  it('用户应该能够成功登录', () => {
    // 输入用户凭据
    cy.get('[data-testid="email-input"]')
      .type(Cypress.env('testUser').email)
    
    cy.get('[data-testid="password-input"]')
      .type(Cypress.env('testUser').password)
    
    // 点击登录按钮
    cy.get('[data-testid="login-button"]').click()
    
    // 验证登录成功
    cy.url().should('include', '/dashboard')
    cy.get('[data-testid="user-menu"]').should('be.visible')
    cy.get('[data-testid="welcome-message"]')
      .should('contain', '欢迎回来')
  })
  
  it('错误的凭据应该显示错误信息', () => {
    cy.get('[data-testid="email-input"]').type('wrong@example.com')
    cy.get('[data-testid="password-input"]').type('wrongpassword')
    cy.get('[data-testid="login-button"]').click()
    
    // 验证错误信息
    cy.get('[data-testid="error-message"]')
      .should('be.visible')
      .and('contain', '用户名或密码错误')
    
    // 确保仍在登录页面
    cy.url().should('include', '/login')
  })
  
  it('表单验证应该正确工作', () => {
    // 提交空表单
    cy.get('[data-testid="login-button"]').click()
    
    // 验证验证错误
    cy.get('[data-testid="email-error"]')
      .should('contain', '邮箱不能为空')
    cy.get('[data-testid="password-error"]')
      .should('contain', '密码不能为空')
    
    // 输入无效邮箱
    cy.get('[data-testid="email-input"]').type('invalid-email')
    cy.get('[data-testid="login-button"]').click()
    
    cy.get('[data-testid="email-error"]')
      .should('contain', '邮箱格式不正确')
  })
})

复杂用户流程测试

javascript
// 🎉 cypress/e2e/user-workflow.cy.js
describe('用户完整工作流程E2E测试', () => {
  beforeEach(() => {
    // 登录用户
    cy.login(Cypress.env('testUser').email, Cypress.env('testUser').password)
  })
  
  it('用户应该能够创建、编辑和删除项目', () => {
    // 导航到项目页面
    cy.get('[data-testid="projects-nav"]').click()
    cy.url().should('include', '/projects')
    
    // 创建新项目
    cy.get('[data-testid="create-project-button"]').click()
    cy.get('[data-testid="project-name-input"]').type('测试项目')
    cy.get('[data-testid="project-description-textarea"]')
      .type('这是一个测试项目的描述')
    cy.get('[data-testid="save-project-button"]').click()
    
    // 验证项目创建成功
    cy.get('[data-testid="success-message"]')
      .should('contain', '项目创建成功')
    cy.get('[data-testid="project-list"]')
      .should('contain', '测试项目')
    
    // 编辑项目
    cy.get('[data-testid="project-item"]').first().within(() => {
      cy.get('[data-testid="edit-button"]').click()
    })
    
    cy.get('[data-testid="project-name-input"]')
      .clear()
      .type('更新的测试项目')
    cy.get('[data-testid="save-project-button"]').click()
    
    // 验证项目更新成功
    cy.get('[data-testid="project-list"]')
      .should('contain', '更新的测试项目')
    
    // 删除项目
    cy.get('[data-testid="project-item"]').first().within(() => {
      cy.get('[data-testid="delete-button"]').click()
    })
    
    cy.get('[data-testid="confirm-delete-button"]').click()
    
    // 验证项目删除成功
    cy.get('[data-testid="project-list"]')
      .should('not.contain', '更新的测试项目')
  })
  
  it('用户应该能够搜索和筛选项目', () => {
    cy.visit('/projects')
    
    // 搜索功能测试
    cy.get('[data-testid="search-input"]').type('Vue')
    cy.get('[data-testid="search-button"]').click()
    
    // 验证搜索结果
    cy.get('[data-testid="project-item"]').each(($el) => {
      cy.wrap($el).should('contain.text', 'Vue')
    })
    
    // 清除搜索
    cy.get('[data-testid="clear-search-button"]').click()
    cy.get('[data-testid="search-input"]').should('have.value', '')
    
    // 筛选功能测试
    cy.get('[data-testid="filter-select"]').select('active')
    cy.get('[data-testid="project-item"]').each(($el) => {
      cy.wrap($el).find('[data-testid="status-badge"]')
        .should('contain', '进行中')
    })
  })
})

Playwright工具应用:跨浏览器测试的强大选择

Playwright配置和基础使用

javascript
// 🎉 playwright.config.js - Playwright配置
import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
  // 测试目录
  testDir: './tests/e2e',
  
  // 全局设置
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  
  // 报告配置
  reporter: [
    ['html'],
    ['json', { outputFile: 'test-results.json' }],
    ['junit', { outputFile: 'test-results.xml' }]
  ],
  
  // 全局配置
  use: {
    baseURL: 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure'
  },
  
  // 浏览器项目配置
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] }
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] }
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] }
    },
    {
      name: 'Mobile Chrome',
      use: { ...devices['Pixel 5'] }
    },
    {
      name: 'Mobile Safari',
      use: { ...devices['iPhone 12'] }
    }
  ],
  
  // 开发服务器配置
  webServer: {
    command: 'npm run dev',
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI
  }
})

Playwright测试用例

javascript
// 🎉 tests/e2e/user-journey.spec.js
import { test, expect } from '@playwright/test'

test.describe('用户旅程E2E测试', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/')
  })
  
  test('新用户注册流程', async ({ page }) => {
    // 点击注册链接
    await page.click('[data-testid="register-link"]')
    await expect(page).toHaveURL(/.*register/)
    
    // 填写注册表单
    await page.fill('[data-testid="username-input"]', 'newuser')
    await page.fill('[data-testid="email-input"]', 'newuser@example.com')
    await page.fill('[data-testid="password-input"]', 'password123')
    await page.fill('[data-testid="confirm-password-input"]', 'password123')
    
    // 同意条款
    await page.check('[data-testid="terms-checkbox"]')
    
    // 提交注册
    await page.click('[data-testid="register-button"]')
    
    // 验证注册成功
    await expect(page.locator('[data-testid="success-message"]'))
      .toContainText('注册成功')
    await expect(page).toHaveURL(/.*login/)
  })
  
  test('购物车完整流程', async ({ page }) => {
    // 登录
    await page.goto('/login')
    await page.fill('[data-testid="email-input"]', 'test@example.com')
    await page.fill('[data-testid="password-input"]', 'password123')
    await page.click('[data-testid="login-button"]')
    
    // 浏览商品
    await page.goto('/products')
    
    // 添加商品到购物车
    await page.click('[data-testid="product-item"]:first-child [data-testid="add-to-cart"]')
    
    // 验证购物车图标更新
    await expect(page.locator('[data-testid="cart-count"]')).toContainText('1')
    
    // 查看购物车
    await page.click('[data-testid="cart-icon"]')
    await expect(page).toHaveURL(/.*cart/)
    
    // 验证商品在购物车中
    await expect(page.locator('[data-testid="cart-item"]')).toHaveCount(1)
    
    // 更新商品数量
    await page.fill('[data-testid="quantity-input"]', '2')
    await page.click('[data-testid="update-quantity"]')
    
    // 验证总价更新
    await expect(page.locator('[data-testid="total-price"]')).toContainText('¥200')
    
    // 结账
    await page.click('[data-testid="checkout-button"]')
    await expect(page).toHaveURL(/.*checkout/)
    
    // 填写配送信息
    await page.fill('[data-testid="address-input"]', '北京市朝阳区测试地址')
    await page.fill('[data-testid="phone-input"]', '13800138000')
    
    // 选择支付方式
    await page.check('[data-testid="payment-method-alipay"]')
    
    // 提交订单
    await page.click('[data-testid="place-order-button"]')
    
    // 验证订单成功
    await expect(page.locator('[data-testid="order-success"]'))
      .toContainText('订单提交成功')
  })
  
  test('响应式设计测试', async ({ page }) => {
    // 桌面视图测试
    await page.setViewportSize({ width: 1280, height: 720 })
    await page.goto('/')
    
    await expect(page.locator('[data-testid="desktop-nav"]')).toBeVisible()
    await expect(page.locator('[data-testid="mobile-nav"]')).toBeHidden()
    
    // 移动端视图测试
    await page.setViewportSize({ width: 375, height: 667 })
    
    await expect(page.locator('[data-testid="desktop-nav"]')).toBeHidden()
    await expect(page.locator('[data-testid="mobile-nav"]')).toBeVisible()
    
    // 测试移动端菜单
    await page.click('[data-testid="mobile-menu-button"]')
    await expect(page.locator('[data-testid="mobile-menu"]')).toBeVisible()
  })
})

### API集成测试:前后端协作验证

#### API拦截和Mock
```javascript
// 🎉 Cypress API拦截测试
describe('API集成测试', () => {
  beforeEach(() => {
    // 拦截API请求
    cy.intercept('GET', '/api/users', { fixture: 'users.json' }).as('getUsers')
    cy.intercept('POST', '/api/users', {
      statusCode: 201,
      body: { id: 123, name: '新用户', email: 'new@example.com' }
    }).as('createUser')

    cy.visit('/users')
  })

  it('应该正确加载用户列表', () => {
    // 等待API请求完成
    cy.wait('@getUsers')

    // 验证用户列表显示
    cy.get('[data-testid="user-list"]').should('be.visible')
    cy.get('[data-testid="user-item"]').should('have.length.greaterThan', 0)
  })

  it('应该能够创建新用户', () => {
    cy.get('[data-testid="add-user-button"]').click()
    cy.get('[data-testid="name-input"]').type('新用户')
    cy.get('[data-testid="email-input"]').type('new@example.com')
    cy.get('[data-testid="save-button"]').click()

    // 验证API调用
    cy.wait('@createUser').then((interception) => {
      expect(interception.request.body).to.deep.include({
        name: '新用户',
        email: 'new@example.com'
      })
    })

    // 验证UI更新
    cy.get('[data-testid="success-message"]').should('contain', '用户创建成功')
  })

  it('应该处理API错误', () => {
    // 模拟API错误
    cy.intercept('POST', '/api/users', {
      statusCode: 400,
      body: { error: '邮箱已存在' }
    }).as('createUserError')

    cy.get('[data-testid="add-user-button"]').click()
    cy.get('[data-testid="name-input"]').type('测试用户')
    cy.get('[data-testid="email-input"]').type('existing@example.com')
    cy.get('[data-testid="save-button"]').click()

    cy.wait('@createUserError')
    cy.get('[data-testid="error-message"]').should('contain', '邮箱已存在')
  })
})

Playwright API测试

javascript
// 🎉 Playwright API集成测试
import { test, expect } from '@playwright/test'

test.describe('API集成测试', () => {
  test('用户数据CRUD操作', async ({ page, request }) => {
    // 创建用户
    const createResponse = await request.post('/api/users', {
      data: {
        name: '测试用户',
        email: 'test@example.com',
        password: 'password123'
      }
    })

    expect(createResponse.ok()).toBeTruthy()
    const user = await createResponse.json()
    expect(user.id).toBeDefined()

    // 在页面中验证用户创建
    await page.goto('/users')
    await expect(page.locator(`[data-user-id="${user.id}"]`)).toBeVisible()

    // 更新用户
    const updateResponse = await request.put(`/api/users/${user.id}`, {
      data: {
        name: '更新的用户名'
      }
    })

    expect(updateResponse.ok()).toBeTruthy()

    // 刷新页面验证更新
    await page.reload()
    await expect(page.locator(`[data-user-id="${user.id}"]`))
      .toContainText('更新的用户名')

    // 删除用户
    const deleteResponse = await request.delete(`/api/users/${user.id}`)
    expect(deleteResponse.ok()).toBeTruthy()

    // 验证用户已删除
    await page.reload()
    await expect(page.locator(`[data-user-id="${user.id}"]`)).toHaveCount(0)
  })

  test('文件上传功能', async ({ page }) => {
    await page.goto('/upload')

    // 准备测试文件
    const fileChooserPromise = page.waitForEvent('filechooser')
    await page.click('[data-testid="file-upload-button"]')
    const fileChooser = await fileChooserPromise

    await fileChooser.setFiles({
      name: 'test.txt',
      mimeType: 'text/plain',
      buffer: Buffer.from('这是测试文件内容')
    })

    // 验证文件上传
    await expect(page.locator('[data-testid="file-name"]')).toContainText('test.txt')

    // 提交上传
    await page.click('[data-testid="upload-submit"]')

    // 验证上传成功
    await expect(page.locator('[data-testid="upload-success"]'))
      .toContainText('文件上传成功')
  })
})

CI/CD集成:自动化测试流程

GitHub Actions集成

yaml
# 🎉 .github/workflows/e2e-tests.yml
name: E2E Tests

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  cypress-tests:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        browser: [chrome, firefox, edge]

    steps:
    - name: Checkout
      uses: actions/checkout@v3

    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'

    - name: Install dependencies
      run: npm ci

    - name: Build application
      run: npm run build

    - name: Start application
      run: npm run preview &

    - name: Wait for application
      run: npx wait-on http://localhost:4173

    - name: Run Cypress tests
      uses: cypress-io/github-action@v5
      with:
        browser: ${{ matrix.browser }}
        record: true
        parallel: true
      env:
        CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

    - name: Upload screenshots
      uses: actions/upload-artifact@v3
      if: failure()
      with:
        name: cypress-screenshots-${{ matrix.browser }}
        path: cypress/screenshots

    - name: Upload videos
      uses: actions/upload-artifact@v3
      if: failure()
      with:
        name: cypress-videos-${{ matrix.browser }}
        path: cypress/videos

  playwright-tests:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v3

    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'
        cache: 'npm'

    - name: Install dependencies
      run: npm ci

    - name: Install Playwright browsers
      run: npx playwright install --with-deps

    - name: Build application
      run: npm run build

    - name: Run Playwright tests
      run: npx playwright test

    - name: Upload test results
      uses: actions/upload-artifact@v3
      if: always()
      with:
        name: playwright-report
        path: playwright-report/
        retention-days: 30

Docker环境测试

dockerfile
# 🎉 Dockerfile.e2e - E2E测试Docker环境
FROM mcr.microsoft.com/playwright:v1.40.0-focal

WORKDIR /app

# 复制依赖文件
COPY package*.json ./

# 安装依赖
RUN npm ci

# 复制源代码
COPY . .

# 构建应用
RUN npm run build

# 安装浏览器
RUN npx playwright install

# 运行测试
CMD ["npm", "run", "test:e2e"]

E2E测试最佳实践:提升测试效率和稳定性

页面对象模式

javascript
// 🎉 cypress/support/pages/LoginPage.js
class LoginPage {
  constructor() {
    this.emailInput = '[data-testid="email-input"]'
    this.passwordInput = '[data-testid="password-input"]'
    this.loginButton = '[data-testid="login-button"]'
    this.errorMessage = '[data-testid="error-message"]'
  }

  visit() {
    cy.visit('/login')
    return this
  }

  fillEmail(email) {
    cy.get(this.emailInput).type(email)
    return this
  }

  fillPassword(password) {
    cy.get(this.passwordInput).type(password)
    return this
  }

  submit() {
    cy.get(this.loginButton).click()
    return this
  }

  login(email, password) {
    return this.fillEmail(email)
      .fillPassword(password)
      .submit()
  }

  shouldShowError(message) {
    cy.get(this.errorMessage).should('contain', message)
    return this
  }
}

export default LoginPage

// 使用页面对象
import LoginPage from '../support/pages/LoginPage'

describe('登录功能测试', () => {
  const loginPage = new LoginPage()

  it('应该能够成功登录', () => {
    loginPage
      .visit()
      .login('test@example.com', 'password123')

    cy.url().should('include', '/dashboard')
  })
})

自定义命令

javascript
// 🎉 cypress/support/commands.js
// 登录命令
Cypress.Commands.add('login', (email, password) => {
  cy.session([email, password], () => {
    cy.visit('/login')
    cy.get('[data-testid="email-input"]').type(email)
    cy.get('[data-testid="password-input"]').type(password)
    cy.get('[data-testid="login-button"]').click()
    cy.url().should('include', '/dashboard')
  })
})

// 数据库种子命令
Cypress.Commands.add('seedDatabase', () => {
  cy.task('seedDatabase')
})

// 等待加载命令
Cypress.Commands.add('waitForPageLoad', () => {
  cy.get('[data-testid="loading"]').should('not.exist')
  cy.get('[data-testid="content"]').should('be.visible')
})

// 截图命令
Cypress.Commands.add('takeScreenshot', (name) => {
  cy.screenshot(name, { capture: 'fullPage' })
})

// 类型声明
declare global {
  namespace Cypress {
    interface Chainable {
      login(email: string, password: string): Chainable<void>
      seedDatabase(): Chainable<void>
      waitForPageLoad(): Chainable<void>
      takeScreenshot(name: string): Chainable<void>
    }
  }
}

📚 Vue3 E2E测试学习总结与下一步规划

✅ 本节核心收获回顾

通过本节Vue3 E2E测试的学习,你已经掌握:

  1. E2E测试概念:深入理解端到端测试的价值和在测试金字塔中的位置
  2. Cypress框架使用:能够配置和使用Cypress进行Vue3应用的E2E测试
  3. Playwright工具应用:掌握跨浏览器测试和现代E2E测试技术
  4. API集成测试:学会测试前后端集成和API交互场景
  5. CI/CD集成:能够将E2E测试集成到持续集成流程中

🎯 Vue3 E2E测试下一步

  1. 性能测试集成:学习在E2E测试中集成性能监控和分析
  2. 视觉回归测试:掌握UI变化检测和视觉测试技术
  3. 测试数据管理:建立完善的测试数据管理和环境隔离策略
  4. 测试报告优化:创建详细的测试报告和质量度量体系

🔗 相关学习资源

💪 E2E测试实践建议

  1. 关注关键路径:优先测试核心用户流程和业务场景
  2. 保持测试稳定:编写可靠、可重复的测试用例
  3. 合理使用资源:平衡测试覆盖率和执行时间
  4. 持续优化改进:定期回顾和优化测试策略

🔍 常见问题FAQ

Q1: E2E测试和集成测试有什么区别?

A: E2E测试在真实浏览器环境中测试完整用户流程,包括前端、后端、数据库等所有组件;集成测试主要验证组件间的接口和协作,通常不涉及UI层面。

Q2: Cypress和Playwright应该选择哪个?

A: Cypress更适合快速开始和简单场景,有优秀的开发体验;Playwright支持更多浏览器,性能更好,适合复杂的跨浏览器测试需求。

Q3: E2E测试执行时间太长怎么办?

A: 可以通过并行执行、选择性运行、优化测试用例、使用无头浏览器等方式提升执行效率。关键是平衡覆盖率和执行时间。

Q4: 如何处理E2E测试的不稳定性?

A: 使用显式等待而非固定延时、合理设置超时时间、隔离测试数据、重试机制、详细的错误日志等方法提升测试稳定性。

Q5: E2E测试应该覆盖多少功能?

A: 建议覆盖核心用户路径和关键业务流程,通常占总功能的20-30%即可。重点关注高价值、高风险的功能点。


🛠️ E2E测试故障排除指南

常见问题解决方案

测试不稳定问题

javascript
// 问题:元素查找失败
// 解决:使用显式等待

// ❌ 错误写法
cy.get('[data-testid="button"]').click()

// ✅ 正确写法
cy.get('[data-testid="button"]', { timeout: 10000 })
  .should('be.visible')
  .and('not.be.disabled')
  .click()

异步操作处理

javascript
// 问题:异步加载内容测试失败
// 解决:等待特定状态

// ❌ 错误写法
cy.visit('/page')
cy.get('[data-testid="content"]').should('contain', 'data')

// ✅ 正确写法
cy.visit('/page')
cy.get('[data-testid="loading"]').should('not.exist')
cy.get('[data-testid="content"]').should('be.visible')
cy.get('[data-testid="content"]').should('contain', 'data')

"掌握Vue3 E2E测试,为你的应用质量提供最后的保障。E2E测试是用户体验和产品质量的守护者!"