Vitest - это очень быстрый фреймворк модульных тестов, построенный на основе Vite. С ним можно быстро начать работу и легко писать тесты для своих компонентов. Vitest не предназначен в первую очередь для тестирования фронтенд-компонентов, но с добавлением некоторых замечательных пакетов мы можем использовать его для тестирования наших компонентов Solid.
Теперь, когда мы установили необходимые пакеты, необходимо настроить vitest для работы с Solid. Создадим файл vitest.config.ts в корне нашего проекта.
1 2 3 4 5 6 7 8 9101112131415161718192021
/// <reference types="vitest" />/// <reference types="vite/client" />// 👆 do not forget to add the references aboveimport{defineConfig}from'vite';importsolidPluginfrom'vite-plugin-solid';exportdefaultdefineConfig({plugins:[solidPlugin()],server:{port:3000,},build:{target:'esnext',},test:{environment:'jsdom',globals:true,transformMode:{web:[/\.[jt]sx?$/]},},});
Примечание: Не забудьте добавить типы reference в верхней части файла. Если вы не добавите их, то при попытке запустить тесты вы получите ошибку типа.
После создания файла конфигурации нам необходимо добавить скрипт в наш файл package.json.
Теперь, когда мы добавили скрипт, мы можем запустить наши тесты, выполнив команду npm run test или yarn test. Однако тестов для запуска нет, поэтому нам нужно их создать.
В Typescript/Javascript файлы тестов могут быть созданы с расширением .test.ts или .spec.ts. Тесты обычно хранятся в отдельной папке в корне проекта, большинство разработчиков предпочитают называть эту папку tests или __tests__. Однако в некоторых случаях тесты лучше хранить в той же папке, что и тестируемый компонент. Изучите различные стили и найдите тот, который подходит именно вам 🙂 .
В Solid мы рекомендуем создать папку test, в которой будут храниться все тестовые файлы. Давайте создадим папку test в корне нашего проекта и создадим в ней файл App.test.ts.
12
mkdirtest
touchtest/App.test.ts
Теперь, когда мы создали тестовый файл, давайте добавим в него несколько тестов.
1 2 3 4 5 6 7 8 910111213
import{render}from'@solidjs/testing-library';importAppfrom'../src/App';import{describe,expect,it}from'vitest';import'@testing-library/jest-dom';// 👈 this is imported in order to use the jest-dom matchersdescribe('App',()=>{it('should render the app',()=>{const{getByText}=render(()=><App/>);expect(getByText('Learn Solid')).toBeInTheDocument();});});
Приведенный выше тест проверяет, находится ли фраза Learn Solid в компоненте App после его рендеринга. Если да, то тест будет пройден. Если нет, то тест будет провален. Приведенный выше фрагмент является примером теста компонента.
Здесь мы используем библиотеку solid-testing-library. Наиболее важными помощниками являются render для управляемого рендеринга компонента в DOM, fireEvent для диспетчеризации событий, напоминающих реальные события пользователя, и screen для предоставления глобальных селекторов. Мы также используем полезные утверждения, добавленные в expect, предоставленные @testing-library/jest-dom.
Давайте создадим тестовый файл для нашего компонента Counter:
// counter.test.tsximport{Counter}from'../src/counter';import{cleanup,fireEvent,render,screen,afterEach,}from'solid-testing-library';import{describe,expect,it}from'vitest';import'@testing-library/jest-dom';describe('Counter',()=>{afterEach(cleanup);it('it starts with zero',()=>{render(()=><Counter/>);constbutton=screen.getByRole('button');expect(button).toBeInTheDocument();expect(button).toHaveTextContent('Count: 0');});it('it increases its value on click',async()=>{render(()=><Counter/>);constbutton=screen.getByRole('button');fireEvent.click(button);// the event loop takes one Promise to resolve to be finishedawaitPromise.resolve();expect(button).toHaveTextContent('Count: 1');fireEvent.click(button);awaitPromise.resolve();expect(button).toHaveTextContent('Count: 2');});});
Приведенный выше тест проверяет компонент counter на корректность отображения текста и увеличение счета при нажатии на кнопку мыши.
Для удобства обслуживания или поддержки нескольких представлений вы можете захотеть хранить часть состояния отдельно от компонентов. В этом случае интерфейсом, на котором проводится тестирование, является само состояние. Следует помнить, что вне реактивного корня (например, createRoot) состояние не отслеживается, и его обновление не будет вызывать эффекты и заметки.
В качестве примера создадим и протестируем функцию createLocalStorage, которая будет создавать реактивное состояние, сохраняемое в локальном хранилище браузера.
import{createRoot,createEffect}from'solid-js';import{createLocalStorage}from'../src/createLocalStorage';// 👈 our functionimport{describe,expect,it,beforeEach}from'vitest';describe('createLocalStorage',()=>{beforeEach(()=>{localStorage.clear();});constinitialState={todos:[],newTitle:'',};it('reads pre-existing state from localStorage',()=>{createRoot((dispose)=>{constsavedState={todos:[{title:'Learn Solid'}],newTitle:'Learn Solid',};localStorage.setItem('state',JSON.stringify(savedState));const[state]=createLocalStorage('state',initialState);expect(state).toEqual(savedState);dispose();});});it('stores new state to localStorage',()=>{createRoot((dispose)=>{const[state,setState]=createLocalStorage('state',initialState);setState('newTitle','updated');returnnewPromise((resolve)=>createEffect(()=>{expect(JSON.parse(localStorage.getItem('state')||'')).toEqual({todos:[],newTitle:'updated',});dispose();resolve();}));});});it('updates state multiple times',async()=>{const{dispose,setState}=createRoot((dispose)=>{const[state,setState]=createLocalStore('state',initialState);return{dispose,setState};});setState('newTitle','first');// wait a tick to resolve all effectsawaitnewPromise((done)=>setTimeout(done,0));expect(JSON.parse(localStorage.getItem('state')||'')).toEqual({todos:[],newTitle:'first',});setState('newTitle','second');// wait a tick to resolve all effectsawaitnewPromise((done)=>setTimeout(done,0));expect(JSON.parse(localStorage.getItem('state')||'')).toEqual({todos:[],newTitle:'second',});dispose();});});