乐闻世界logo
搜索文章和话题

How does Puppeteer integrate with testing frameworks? What are the best practices for E2E testing and CI/CD integration?

2月19日 19:55

Puppeteer can be integrated with various testing frameworks to implement end-to-end testing, unit testing, and integration testing. Here are common integration methods and best practices.

1. Integration with Jest

Install Dependencies:

bash
npm install --save-dev puppeteer jest jest-puppeteer @types/puppeteer

Configure Jest:

javascript
// jest.config.js module.exports = { preset: 'jest-puppeteer', testMatch: ['**/*.test.js'], setupFilesAfterEnv: ['./jest.setup.js'] };

Setup File:

javascript
// jest.setup.js beforeEach(async () => { await page.goto('http://localhost:3000'); });

Write Tests:

javascript
describe('Puppeteer with Jest', () => { test('page title', async () => { await expect(page.title()).resolves.toMatch('My App'); }); test('user can login', async () => { await page.type('#username', 'testuser'); await page.type('#password', 'password'); await page.click('#login-button'); await expect(page).toMatch('Welcome'); }); });

2. Integration with Mocha

Install Dependencies:

bash
npm install --save-dev puppeteer mocha chai

Configure Mocha:

javascript
// mocha.config.js module.exports = { timeout: 10000, require: ['mocha-setup.js'] };

Setup File:

javascript
// mocha-setup.js const puppeteer = require('puppeteer'); const { expect } = require('chai'); let browser; let page; before(async () => { browser = await puppeteer.launch(); page = await browser.newPage(); }); after(async () => { await browser.close(); }); beforeEach(async () => { await page.goto('http://localhost:3000'); }); global.page = page; global.expect = expect;

Write Tests:

javascript
describe('Puppeteer with Mocha', () => { it('should display correct title', async () => { const title = await page.title(); expect(title).to.equal('My App'); }); it('should allow user to login', async () => { await page.type('#username', 'testuser'); await page.type('#password', 'password'); await page.click('#login-button'); const welcomeText = await page.$eval('.welcome', el => el.textContent); expect(welcomeText).to.include('Welcome'); }); });

3. Integration with Playwright

Install Dependencies:

bash
npm install --save-dev @playwright/test

Configure Playwright:

javascript
// playwright.config.js module.exports = { testDir: './tests', use: { headless: true, screenshot: 'only-on-failure' } };

Write Tests:

javascript
const { test, expect } = require('@playwright/test'); test.describe('Puppeteer migration', () => { test('basic navigation', async ({ page }) => { await page.goto('http://localhost:3000'); await expect(page).toHaveTitle('My App'); }); test('form submission', async ({ page }) => { await page.goto('http://localhost:3000'); await page.fill('#username', 'testuser'); await page.fill('#password', 'password'); await page.click('#login-button'); await expect(page.locator('.welcome')).toBeVisible(); }); });

4. Integration with Cypress

Install Cypress:

bash
npm install --save-dev cypress

Configure Cypress:

javascript
// cypress.config.js const { defineConfig } = require('cypress'); module.exports = defineConfig({ e2e: { baseUrl: 'http://localhost:3000', setupNodeEvents(on, config) { on('task', { puppeteer({ url, action }) { return require('./puppeteer-task')(url, action); } }); } } });

Puppeteer Task:

javascript
// puppeteer-task.js const puppeteer = require('puppeteer'); module.exports = async (url, action) => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto(url); let result; if (action === 'screenshot') { result = await page.screenshot({ encoding: 'base64' }); } else if (action === 'pdf') { result = await page.pdf({ encoding: 'base64' }); } await browser.close(); return result; };

Cypress Tests:

javascript
// cypress/e2e/puppeteer.spec.js describe('Puppeteer integration', () => { it('should take screenshot with Puppeteer', () => { cy.task('puppeteer', { url: 'http://localhost:3000', action: 'screenshot' }).then(screenshot => { cy.log('Screenshot taken'); }); }); });

5. End-to-End Testing Best Practices

Test Structure:

javascript
describe('User Flow', () => { before(async () => { // Setup test environment await setupTestDatabase(); }); after(async () => { // Cleanup test environment await cleanupTestDatabase(); }); beforeEach(async () => { // Preparation before each test await page.goto('http://localhost:3000'); }); test('complete user registration flow', async () => { // Test steps await page.click('#register-button'); await page.type('#username', 'newuser'); await page.type('#email', 'newuser@example.com'); await page.type('#password', 'password123'); await page.click('#submit-button'); // Verify results await expect(page).toMatch('Registration successful'); }); });

Test Data Management:

javascript
// test-data.js module.exports = { validUser: { username: 'testuser', email: 'test@example.com', password: 'password123' }, invalidUser: { username: '', email: 'invalid-email', password: '123' } }; // Use test data const { validUser } = require('./test-data'); test('register with valid data', async () => { await page.type('#username', validUser.username); await page.type('#email', validUser.email); await page.type('#password', validUser.password); await page.click('#submit-button'); await expect(page).toMatch('Success'); });

6. Visual Regression Testing

Using Percy:

bash
npm install --save-dev @percy/puppeteer
javascript
const percy = require('@percy/puppeteer'); describe('Visual regression tests', () => { beforeAll(async () => { await percy.start(); }); afterAll(async () => { await percy.stop(); }); test('homepage visual', async () => { await page.goto('http://localhost:3000'); await percy.snapshot(page, 'Homepage'); }); });

Using BackstopJS:

javascript
// backstop.config.js module.exports = { scenarios: [ { label: 'Homepage', url: 'http://localhost:3000', selectors: ['#header', '#main', '#footer'] } ], paths: { bitmaps_reference: 'backstop_data/bitmaps_reference', bitmaps_test: 'backstop_data/bitmaps_test', html_report: 'backstop_data/html_report' } };

7. Performance Testing

Using Lighthouse:

bash
npm install --save-dev lighthouse puppeteer
javascript
const lighthouse = require('lighthouse'); const puppeteer = require('puppeteer'); describe('Performance tests', () => { test('page performance score', async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); const runnerResult = await lighthouse('http://localhost:3000', { port: new URL(browser.wsEndpoint()).port, output: 'json' }); const score = runnerResult.lhr.categories.performance.score * 100; expect(score).toBeGreaterThan(80); await browser.close(); }); });

8. Test Reporting

Using Allure:

bash
npm install --save-dev allure-commandline
javascript
const allure = require('allure-js-commons'); describe('Allure reporting', () => { test('with Allure steps', async () => { const epic = new allure.Epic('User Management'); const feature = new allure.Feature('Registration'); const story = new allure.Story('User can register'); epic.addFeature(feature); feature.addStory(story); await page.click('#register-button'); await page.type('#username', 'testuser'); await page.click('#submit-button'); story.addStep('Click register button'); story.addStep('Enter username'); story.addStep('Submit form'); }); });

9. CI/CD Integration

GitHub Actions Configuration:

yaml
name: Puppeteer Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Setup Node.js uses: actions/setup-node@v2 with: node-version: '16' - name: Install dependencies run: npm ci - name: Install Chrome run: | sudo apt-get update sudo apt-get install -y chromium-browser - name: Run tests run: npm test env: CI: true

Docker Configuration:

dockerfile
FROM node:16-alpine # Install Chrome RUN apk add --no-cache chromium # Install dependencies COPY package*.json ./ RUN npm ci # Copy test files COPY . . # Run tests CMD ["npm", "test"]

10. Best Practices Summary

1. Test Isolation:

javascript
// Use separate context for each test beforeEach(async () => { const context = await browser.createIncognitoBrowserContext(); page = await context.newPage(); }); afterEach(async () => { await context.close(); });

2. Wait Strategies:

javascript
// Use explicit waits await page.waitForSelector('.element', { visible: true }); // Avoid hardcoded delays // await page.waitForTimeout(5000); // Not recommended

3. Error Handling:

javascript
test('with error handling', async () => { try { await page.click('#button'); } catch (error) { // Save failure screenshot await page.screenshot({ path: 'failure.png' }); throw error; } });

4. Test Data Cleanup:

javascript
afterEach(async () => { // Cleanup test data await cleanupTestData(); });

5. Parallel Testing:

javascript
// Jest configuration module.exports = { maxWorkers: 4, // Run tests in parallel preset: 'jest-puppeteer' };
标签:Puppeteer