Эта статья — перевод оригинальной статьи Ignacio Falk "Testing with Vitest"

Также я веду телеграм канал “Frontend по-флотски”, где рассказываю про интересные вещи из мира разработки интерфейсов.

Вступление

Vitest — это новая среда тестирования на базе Vite. Он все еще находится в разработке, и некоторые функции могут быть еще не готовы, но это хорошая альтернатива, которую можно попробовать и изучить.

Настройка

Давайте создадим новый проект Vite!

Примечание. Для работы Vitest требуется Vite >= v2.7.10 и Node >= v14.

npm init vite@latest

✔ Project name: · try-vitest
✔ Select a framework: · svelte
✔ Select a variant: · svelte-ts

cd try-vitest
npm install //use the package manager you prefer
npm run dev

Теперь, когда наш проект создан, нам нужно установить все зависимости, необходимые для работы Vitest.

npm i -D vitest jsdom

Я добавил jsdom, чтобы иметь возможность мокать DOM API. По умолчанию Vitest будет использовать конфигурацию из vite.config.ts. Я добавлю в неё один плагин для svelte. Отключение hot module replacement при выполнении тестов.

Это должно выглядеть следующим образом:

import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'

export default defineConfig({
  plugins: [
    svelte({ hot: !process.env.VITEST }),
  ],
})

Я использую переменную env VITEST, чтобы разделить окружение тестов и разработки, но если ваша конфигурация слишком отличается, вы можете использовать другой файл конфигурации для тестов. Есть несколько вариантов сделать это.

  • Создайте файл конфигурации с именем vitest.config.ts: он будет иметь приоритет при запуске тестов.

  • Использование флага --config: используйте его как npx vitest --config <path_to_file>

Написание тестов

Давайте напишем несколько тестов для компонента Counter, созданного по умолчанию в нашем проекте.


<script lang="ts">
  let count: number = 0
  const increment = () => {
    count += 1
  }
</script>

<button on:click={increment}>
  Clicks: {count}
</button>

<style>
  button {
    font-family: inherit;
    font-size: inherit;
    padding: 1em 2em;
    color: #ff3e00;
    background-color: rgba(255, 62, 0, 0.1);
    border-radius: 2em;
    border: 2px solid rgba(255, 62, 0, 0);
    outline: none;
    width: 200px;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
  }

  button:focus {
    border: 2px solid #ff3e00;
  }

  button:active {
    background-color: rgba(255, 62, 0, 0.2);
  }
</style>

Чтобы написать наш первый набор тестов, давайте создадим файл с именем Counter.spec.ts рядом с нашим компонентом.


// @vitest-environment jsdom
import { tick } from 'svelte';
import { describe, expect, it } from 'vitest';
import Counter from './Counter.svelte';

describe('Counter component', function () {
  it('creates an instance', function () {
    const host = document.createElement('div');
    document.body.appendChild(host);
    const instance = new Counter({ target: host });
    expect(instance).toBeTruthy();
  });

  it('renders', function () {
    const host = document.createElement('div');
    document.body.appendChild(host);
    new Counter({ target: host });
    expect(host.innerHTML).toContain('Clicks: 0');
  });

  it('updates count when clicking a button', async function () {
    const host = document.createElement('div');
    document.body.appendChild(host);
    new Counter({ target: host });
    expect(host.innerHTML).toContain('Clicks: 0');
    const btn = host.getElementsByTagName('button')[0];
    btn.click();
    await tick();
    expect(host.innerHTML).toContain('Clicks: 1');
  });
});

Добавление строки комментария @vitest-environment jsdom вверху файла позволит нам мокать DOM API для всех тестов в файле. Этого можно избежать в каждом файле с помощью файла конфигурации. Мы также можем удостовериться, что мы импортируем describeitexpect глобально. Делаем это тоже через конфигурационный файл. Также нам нужно сделать типы доступными, добавив типы vitest/globals в ваш файл tsconfig.json (вы можете пропустить это, если не используете TypeScript).

import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'

export default defineConfig({
  plugins: [svelte({ hot: !process.env.VITEST })],
  test: {
    globals: true,
    environment: 'jsdom',
  },
});

{
  "extends": "@tsconfig/svelte/tsconfig.json",
  "compilerOptions": {
    "target": "esnext",
    "useDefineForClassFields": true,
    "module": "esnext",
    "resolveJsonModule": true,
    "baseUrl": ".",
    "allowJs": true,
    "checkJs": true,
	/**
     *Add the next line if using globals
     */
    "types": ["vitest/globals"]
  },
  "include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
}

Теперь нашим тестовым файлам не нужно импортировать глобальные переменные, и мы можем удалить настройку среды jsdom.

import { tick } from 'svelte';
import Counter from './Counter.svelte';

describe('Counter component', function () {
  // tests are the same
});

Команды

Есть четыре команды для запуска из cli:

  • dev: запустить vitest в режиме разработки

  • related: запускает тесты для списка исходных файлов

  • run: запустить тесты один раз

  • watch: режим по умолчанию, такой же, как при запуске vitest. Наблюдает за изменениями, а затем повторно запускает тесты.

Модификаторы тестов

Существуют модификаторы для тестов, которые изменят способ выполнения ваших тестов.

  • .only сосредоточится на одном или нескольких тестах, пропустив остальные

  • .skip пропустит указанный тест

  • .todo пометит тест, которq будtn реализован позже

  • .concurrently будет запускать непрерывные тесты, помеченные как concurrent параллельно. Этот модификатор можно комбинировать с предыдущими. Например: it.concurrently.todo("сделать что-то асинхронное")

Assertions

Vitest поставляется с assertions, совместимыми с chai и jest

expect(true).toBeTruthy() //ok
expect(1).toBe(Math.sqrt(4)) // false

Список доступных assertions см. в документации по API.

Покрытие

Для отчетов о покрытии нам нужно будет установить c8 и запустить тесты с флагом --coverage.

npm i -D c8

npx vitest --coverage

Это даст нам хороший отчет о покрытии.

Папка coverage будет создана в корне проекта. Вы можете указать желаемый тип вывода в файле конфигурации.

import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [svelte({ hot: !process.env.VITEST })],
  test: {
    globals: true,
    environment: 'jsdom',
    coverage:{
      reporter:['text', 'json', 'html'] // change this property to the desired output
    }
  },
});

UI

Вы также можете запустить vitest с помощью пользовательского интерфейса, который поможет вам визуализировать выполняемые тесты и их результаты. Давайте установим необходимый пакет и запустим его с флагом --ui.

npm i -D @vitest/ui

npx vitest --ui

Мне нравится этот интерфейс. Он даже позволяет вам прочитать код тестов и открыть его в редакторе.

Больше возможностей

Vitest поставляется со многими другими функциями, такими как тестирование снепшотами, мокинг, фальшивые таймеры и многое другое, что вы можете знать из других библиотек тестирования.

Переход на Vitest (из проекта Vite с использованием jest)

Если вы работаете над небольшим проектом или только начинаете его, вам может потребоваться адаптировать файл конфигурации, и на этом все. Если вы используете мокинг функции, Vitest использует TinySpy, а для фальшивых таймеров — @sinonjs/fake-timers. Проверьте совместимость. Кроме того, не забудьте импортировать {vi} из vitest, если вы будете его использовать. Еще одна вещь, которую вам может понадобиться настроить, — это установочный файл. Например, чтобы использовать сопоставители jest-dom, мы можем создать установочный файл.

import '@testing-library/jest-dom'

и объявим его в нашем конфигурационном файле.

export default defineConfig(({ mode }) => ({
    // ...
	test: {
		globals: true,
		environment: 'jsdom',
		setupFiles: ['<PATH_TO_SETUP_FILE>']
	}
}))

Вот пример миграции VitePress на Vitest. (Есть некоторые изменения в ts-config, но вы можете увидеть, где добавлен vitest, и файл vitest.config.ts)

Последние мысли

Несмотря на то, что Vitest все еще находится в разработке, он выглядит очень многообещающе, и тот факт, что они сохранили API, очень похожий на Jest, делает миграцию очень плавной. Он также поставляется с поддержкой TypeScript (без пакета внешних типов). Использование одного и того же файла конфигурации (по умолчанию) позволяет очень быстро сосредоточиться на написании тестов. Я с нетерпением жду v1.0.0

Комментарии (3)


  1. Ustas4
    05.05.2022 04:59

    Жаль, что переводная статья. Я читаю код и не понимаю главного: вижу текст кода, а название и тип файла где? Его нет. Файловая структура какая?

    Я получил задание внедрить автотесты UI. Ищу подходящий материал. А тут не могу разобраться в принципе , чтоб сделать proof of concept.

    С селениум 4 такого не происходит.


    1. VanishMax
      05.05.2022 09:38

      Vitest – инструмент для unit-тестирования. Если вы рассматриваете end-to-end тестирование, то можете посмотреть в сторону Playwright или Cypress


    1. qmzik Автор
      05.05.2022 10:04

      Компоненты приведены на примере Sveltejs, соответственно расширение .svelte. Для тест файлов .test.js или .test.ts (кому-то ещё нравится .spec вместо .test) в зависимости есть ли у вас в проекте typescript. Структура unit тестов обычно зависит от структуры самого проекта, часто бывает, что полностью повторяет её