单元测试与集成测试对比
iOS 测试策略的基石:是选择让单元隔离的单元测试,还是选择验证组件间交互的集成测试?如何正确搭建测试金字塔?
微软出品的多浏览器、多语言 E2E 测试引擎
运行在浏览器内、注重开发者体验的 JS/TS E2E 工具
没有绝对答案,但有清晰的判断框架。如果需要多浏览器覆盖、免费的并行能力、跨域/iframe 流程,或者需要 JS/TS 之外的语言支持,Playwright 是 2026 年的默认选择。 如果你已经拥有庞大且健康的 Cypress 测试套件并对 Cloud 有投入,仅凭“工具差异”并不足以支撑一次重写——在 component testing 方面 Cypress 目前更成熟。分阶段、优先迁移关键流程比“周末重写”更理性。
| 分类 | Playwright | Cypress |
|---|---|---|
| 性能 | 8/10 | 7/10 |
| 学习难易度 | 7/10 | 8/10 |
| 生态系统 | 8/10 | 7/10 |
| 社区 | 8/10 | 7/10 |
| 就业市场 | 7/10 | 7/10 |
| 面向未来 | 9/10 | 6/10 |
// Playwright Test - 登录流程与 API mock(TypeScript)
import { test, expect } from '@playwright/test';
test.describe('Giris akisi', () => {
test('gecerli kullanici ile giris yapar ve dashboard gorur', async ({ page }) => {
// 网络 mock - 无需额外配置
await page.route('**/api/login', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ token: 'test-token', user: { name: 'Ayse' } }),
});
});
await page.goto('/login');
await page.getByLabel('E-posta').fill('[email protected]');
await page.getByLabel('Sifre').fill('guclu-sifre-123');
await page.getByRole('button', { name: 'Giris Yap' }).click();
// Auto-wait:等待 locator 变为可见
await expect(page.getByText('Hos geldin, Ayse')).toBeVisible();
await expect(page).toHaveURL('/dashboard');
});
test('checkout iframe icindeki odeme butonunu bulur (cross-frame)', async ({ page }) => {
await page.goto('/checkout');
// v1.63.0:不带 selector 的 frameLocator 会在所有子 frame 树中查找
const payButton = page.frameLocator().getByRole('button', { name: 'Ode' });
await payButton.click();
await expect(page.getByText('Odeme basarili')).toBeVisible();
});
});
test('3 tarayicida paralel calisir (playwright.config.ts icinde tanimli)', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveTitle(/Ana Sayfa/);
});// Cypress - 登录流程与 API mock(JavaScript/TypeScript)
describe('Giris akisi', () => {
it('gecerli kullanici ile giris yapar ve dashboard gorur', () => {
// 使用 cy.intercept 进行网络 stub
cy.intercept('POST', '/api/login', {
statusCode: 200,
body: { token: 'test-token', user: { name: 'Ayse' } },
}).as('loginRequest');
cy.visit('/login');
cy.get('[data-cy=email]').type('[email protected]');
cy.get('[data-cy=password]').type('guclu-sifre-123');
cy.get('[data-cy=submit]').click();
cy.wait('@loginRequest');
// Cypress 自动重试:等待元素变为可见
cy.contains('Hos geldin, Ayse').should('be.visible');
cy.url().should('include', '/dashboard');
});
it('cross-origin odeme sayfasina yonlenir (cy.origin gerekli)', () => {
cy.visit('/checkout');
cy.get('[data-cy=pay-button]').click();
// 切换到不同源时必须使用 cy.origin()
cy.origin('https://pay.example.com', () => {
cy.get('[data-cy=confirm-payment]').click();
cy.contains('Odeme onaylandi').should('be.visible');
});
});
});
// cypress.config.js — 基础 E2E 配置
module.exports = {
e2e: {
baseUrl: 'http://localhost:3000',
experimentalRunAllSpecs: true,
setupNodeEvents(on, config) {
return config;
},
},
};没有绝对答案,但有清晰的判断框架。如果需要多浏览器覆盖、免费的并行能力、跨域/iframe 流程,或者需要 JS/TS 之外的语言支持,Playwright 是 2026 年的默认选择。 如果你已经拥有庞大且健康的 Cypress 测试套件并对 Cloud 有投入,仅凭“工具差异”并不足以支撑一次重写——在 component testing 方面 Cypress 目前更成熟。分阶段、优先迁移关键流程比“周末重写”更理性。
获取免费咨询如果你需要多浏览器(Chromium+Firefox+WebKit)、免费的内置并行能力,或者需要 TS/Python/Java/.NET 之间的语言灵活性,Playwright 是 2026 年的默认选择。如果你已经拥有庞大的现有 Cypress 套件、团队习惯交互式 runner,并且已经对 Cypress Cloud 有投入,仅凭“工具差异”并不足以支撑迁移——分阶段迁移更理性。