Пост

Тестирование веб-приложений: от unit-тестов до E2E

Полное руководство по тестированию веб-приложений. Изучите различные уровни тестирования, инструменты и методологии. Практические примеры с Jest, Cypress и другими популярными инструментами.

Тестирование веб-приложений: от unit-тестов до E2E

Введение

Тестирование - важная часть разработки веб-приложений. В этой статье мы рассмотрим различные подходы к тестированию, инструменты и лучшие практики.

Почему тестирование важно?

  • Качество: Раннее обнаружение ошибок
  • Безопасность: Предотвращение регрессий
  • Документация: Тесты как живая документация
  • Рефакторинг: Уверенность при изменении кода

Уровни тестирования

1. Unit-тесты

Что такое unit-тесты?

  • Тестирование отдельных компонентов
  • Изоляция от внешних зависимостей
  • Быстрое выполнение
  • Легкое поддержание

Примеры unit-тестов

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// math.js
export function add(a, b) {
  return a + b;
}

// math.test.js
import { add } from './math';

describe('Math functions', () => {
  test('adds two numbers correctly', () => {
    expect(add(1, 2)).toBe(3);
    expect(add(-1, 1)).toBe(0);
  });
});

2. Интеграционные тесты

Что такое интеграционные тесты?

  • Тестирование взаимодействия компонентов
  • Проверка API endpoints
  • Работа с базой данных
  • Тестирование middleware

Примеры интеграционных тестов

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const request = require('supertest');
const app = require('./app');

describe('User API', () => {
  test('creates a new user', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({
        name: 'John',
        email: 'john@example.com'
      });
    
    expect(response.status).toBe(201);
    expect(response.body).toHaveProperty('id');
  });
});

3. End-to-End тесты

Что такое E2E тесты?

  • Тестирование всего приложения
  • Имитация действий пользователя
  • Проверка реальных сценариев
  • Тестирование UI/UX

Примеры E2E тестов

1
2
3
4
5
6
7
8
9
10
11
describe('User Registration', () => {
  it('registers a new user', () => {
    cy.visit('/register');
    cy.get('input[name="name"]').type('John');
    cy.get('input[name="email"]').type('john@example.com');
    cy.get('button[type="submit"]').click();
    
    cy.url().should('include', '/dashboard');
    cy.contains('Welcome, John');
  });
});

Инструменты тестирования

1. Jest

Установка и настройка

1
npm install --save-dev jest
1
2
3
4
5
6
7
{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  }
}

Конфигурация Jest

1
2
3
4
5
6
7
8
// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  moduleNameMapper: {
    '\\.(css|less|scss|sass)$': 'identity-obj-proxy'
  }
};

2. React Testing Library

Установка

1
npm install --save-dev @testing-library/react @testing-library/jest-dom

Примеры тестов компонентов

1
2
3
4
5
6
7
8
9
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';

test('increments counter when button is clicked', () => {
  render(<Counter />);
  const button = screen.getByText('Increment');
  fireEvent.click(button);
  expect(screen.getByText('1')).toBeInTheDocument();
});

3. Cypress

Установка

1
npm install --save-dev cypress

Настройка Cypress

1
2
3
4
5
6
7
8
// cypress.config.js
module.exports = {
  e2e: {
    baseUrl: 'http://localhost:3000',
    supportFile: 'cypress/support/e2e.js',
    specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}'
  }
};

Методологии тестирования

1. Test-Driven Development (TDD)

Цикл TDD

  1. Написание теста
  2. Запуск теста (ожидается ошибка)
  3. Написание кода
  4. Запуск теста (ожидается успех)
  5. Рефакторинг

Пример TDD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// calculator.test.js
describe('Calculator', () => {
  test('multiplies two numbers', () => {
    const calculator = new Calculator();
    expect(calculator.multiply(2, 3)).toBe(6);
  });
});

// calculator.js
class Calculator {
  multiply(a, b) {
    return a * b;
  }
}

2. Behavior-Driven Development (BDD)

Синтаксис BDD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
describe('User Registration', () => {
  it('should create a new user account', () => {
    // Given
    const user = {
      name: 'John',
      email: 'john@example.com'
    };

    // When
    const result = registerUser(user);

    // Then
    expect(result.success).toBe(true);
    expect(result.user).toHaveProperty('id');
  });
});

Тестирование API

1. REST API

Тестирование endpoints

1
2
3
4
5
6
7
8
9
10
describe('User API', () => {
  test('GET /api/users returns list of users', async () => {
    const response = await request(app)
      .get('/api/users')
      .set('Authorization', `Bearer ${token}`);
    
    expect(response.status).toBe(200);
    expect(Array.isArray(response.body)).toBe(true);
  });
});

2. GraphQL API

Тестирование запросов

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
describe('User Queries', () => {
  test('fetches user by ID', async () => {
    const response = await graphqlRequest(`
      query GetUser($id: ID!) {
        user(id: $id) {
          name
          email
        }
      }
    `, { id: '1' });

    expect(response.data.user).toHaveProperty('name');
    expect(response.data.user).toHaveProperty('email');
  });
});

Тестирование производительности

1. Нагрузочное тестирование

Пример с k6

1
2
3
4
5
6
7
8
9
10
11
import http from 'k6/http';
import { check, sleep } from 'k6';

export default function() {
  const response = http.get('http://test.k6.io');
  check(response, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500
  });
  sleep(1);
}

2. Тестирование производительности браузера

Lighthouse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// lighthouse.config.js
module.exports = {
  ci: {
    collect: {
      startServerCommand: 'npm run start',
      url: ['http://localhost:3000']
    },
    assert: {
      assertions: {
        'categories:performance': ['error', { minScore: 0.9 }]
      }
    }
  }
};

Автоматизация тестирования

1. CI/CD интеграция

GitHub Actions

1
2
3
4
5
6
7
8
9
10
11
12
13
name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: '14'
      - run: npm install
      - run: npm test
      - run: npm run test:e2e

2. Отчеты о тестировании

Настройка отчетов

1
2
3
4
5
6
7
8
9
10
// jest.config.js
module.exports = {
  reporters: [
    'default',
    ['jest-junit', {
      outputDirectory: 'reports',
      outputName: 'junit.xml'
    }]
  ]
};

Заключение

Тестирование - важная часть разработки веб-приложений. Используйте различные уровни тестирования и инструменты для обеспечения качества вашего кода. Помните, что хорошие тесты помогают не только находить ошибки, но и документировать поведение приложения.

Полезные ресурсы

Авторский пост защищен лицензией CC BY 4.0 .

© Oleg Dobrynin. Некоторые права защищены.