Создание приложения на Vue.js по TDD — обширное руководство для людей, у которых есть время — часть 1

Spread the love

Оригинальная статья: Daniel Kuroski Working an application in Vue.js with TDD — An extensive guide for people who have time — part 1

Это первая статья в серии статей:

Хранилище с финальным кодом: kuroski/article-tdd-vue

Во время семинаров VueJS Summit 2018 я лично поговорил с Эддом Йербургом и показал ему, как я тестирую свои приложения, и после некоторой поддержки я решил написать эту статью 😅

Я надеюсь показать, как легко тестировать приложения на Vue.js!


Что мы будем делать

Проект будет очень простым. Мы собираемся создать приложение, которое будет искать пользователя на Github через его API.

Этот проект может показаться простым, но его будет достаточно для демонстрации всех видов тестов, которые обычно используются в разработке приложений на Vue.js. Здесь мы собираемся тестировать:

  • Компоненты
  • Vuex
  • Сервисы

В этом проекте мы собираемся применить концепцию TDD!

TDD

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

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

TDD & Traditional Approach

Вначале может показаться сложным следовать концепции TDD, потому что это требует изменения мышления, так как с начало мы пишем тесты перед написанием производственного кода.

Я хочу напомнить, что мы не собираемся строго следовать всему, что предлагается, но для этого проекта я хочу показать, что это может быть чем-то простым, что можно сделать.


Моделирование приложения

Прежде всего, давайте спланируем, то что мы собираемся сделать.

Application components

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

  • UserView — «Умный» компонент, отвечающий за связь с хранилищем store и загрузку наших презентационных компонентов
  • VUserSearchForm — «Тупые» компоненты, отвечающие за визуализацию формы, а также за передачу сообщения родительскому компоненту с условием поиска
  • VUserProfile — «Тупой» компонент, отвечающий за предоставление информации о наших найденных пользователях

Мы также собираемся искать пользователей через Github API.

Github API — https://developer.github.com/v3/users/

Скачайте vue-cli и давайте создадим наш проект:

npm i -g @vue/cli
vue create tdd-app

Ниже показаны функции, которые я выбрал через CLI. Выберите наиболее удобные для вас.

Vue CLI v3.0.5
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, PWA, Router, Vuex, Linter, Unit
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a linter / formatter config: Standard
? Pick additional lint features: Lint on save
? Pick a unit testing solution: Jest
? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? (y/N) N

Давайте предложим задачу: мы будем запускать npm run serve только тогда, когда закончим наше приложение.

Очистим проект от лишнего

Генерация проекта через Vue CLI заканчивает тем, что туда добавляются некоторые начальные файлы. Давайте удалим все, что нам не нужно в этот первый момент.

Удалите:

  • src/store.js
  • src/assets/logo.png
  • src/components/HelloWorld.vue
  • src/views/About.vue
  • src/views/Home.vue
  • tests/unit/HelloWorld.spec.js

Очистите от лишнего App.vue

<template>
  <div id="app">
    <router-view/>
  </div>
</template><style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}
</style>

Очистите router.js

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: []
})

Создайте каталог store, а в нем файл для размещения в нашем store/index.js:

// src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import state from '@/store/state'
import mutations from '@/store/mutations'
import actions from '@/store/actions'

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  mutations,
  actions,
})

Далее создайте файлы: src/store/state.js src/store/actions.js src/store/mutations.js и все они имеют один и тот же кусок кода:

export default {}

Добавим скрипт npm для наблюдения за нашими тестами в package.json

...
"test:unit": "vue-cli-service test:unit",
"test:unit:watch": "vue-cli-service test:unit --watchAll"

И, наконец, мы собираемся установить некоторые зависимости, которые мы будем использовать в проекте

npm i -d axios
npm i -D flush-promises nock

Создание нашего первого теста

Теперь мы можем начать создавать наши тесты. Давайте начнем с нашего первого компонента, UserView, помня, что мы используем jest в качестве основы наших тестов.

Это будет интеллектуальный компонент, и он будет связываться с store для поиска пользователей Github.

Давайте создадим тогда файл: tests/unit/UserView.spec.js

describe('UserView', () => {
  it('works', () => {})
})

И мы можем запустить тесты: npm run test:unit

Our first test

Но что именно мы тестируем в наших компонентах?

Мы можем проверять многие вещи, но мы должны опираться на следующие идеи:

Если компонент что то рендерит

Мы должны гарантировать, что по крайней мере компонент выполняет рендеринг правильно.

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

Если мы гарантируем, что компонент выполняет рендеринг, мы можем проверить, правильно ли он выполняет рендеринг. У нашего компонента есть кнопка и элемент input, или он может содержит кнопку и комментарий, говорящий «Input will be implemented in v2».

Его биндинги

Мы можем проверить все его возможные связи. Передает ли наш компонент правильные props своим дочерним компонентам?

События

При нажатии кнопки или получении определенного события наш компонент ведет себя ожидаемым образом?

Крайние случаи

В случае наличия списков нам нужно гарантировать, как он будет себя вести, с пустым списком, списком из 5 или 100 элементов.

Продолжим создания нашего компонента

Зная это, нам нужно сделать напишем наш тест. Давайте реализуем первый случай, все ли правильно рендериться в UserView:

import { shallowMount } from '@vue/test-utils'
import UserView from '@/views/UserView'

describe('UserView', () => {
  it('renders the component', () => {
    // arrange
    const wrapper = shallowMount(UserView)

    // assert
    expect(wrapper.html()).toMatchSnapshot()
  })
})

Мы используем jest в качестве нашей тестовой среды и vue-test-utils, чтобы помочь в работе с нашими компонентами.

В строке 7 мы используем shallowMount для создания экземпляра нашего компонента. Это означает:

Эта функция создает только первый уровень его зависимостей

Shallow mount renders only our component’s first level

Vue-test-utils также предоставляет другой метод mount, который создает полное дерево зависимостей.

Here we fully render the whole components’ tree from the rendering component

Но для нашего случая уже достаточно shallowMount.

В строке 10 мы берем wrapper, который является представлением нашего компонента, созданного vue-test-utils, а затем мы «фотографируем» html нашего компонента (создаем snapshot). Этот HTML существует благодаря vue-test-utils.

Запустив npm run test:unit, мы получили первую ошибку

RED

Компонента по-прежнему не существует. Теперь мы находимся в фазе TDD, где мы наконец можем создать наш файл.

//views/UserView.vue
<script>
export default {
  name: 'UserView'
}
</script>

<template>
  <div>
    UserView
  </div>
</template>

Здесь мы создали минимальное представление нашего компонента, и благодаря этому у нас есть первое прохождение теста, которое выполняется после запуска команды: npm run test:unit

GREEN

Но что такое этот snapshot?

По сути, jest сделал «снимок» html нашего компонента, и если мы проверим каталог, внутри tests то там будет создан файл __snapshots__/UserView.spec.js.snap с содержим html компонента.

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

Внести изменения в UserView.vue и убедитесь, что тест не будет проходить

<script>
export default {
  name: 'UserView'
}
</script>

<template>
  <div>
    Hello World
  </div>
</template>
Our test failing due to the html alteration

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

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

Snapshot testing

Для этого вам нужно после запуска команды npm run test:unit:watch всего лишь нажать u на терминале, и snapshot будет обновлен автоматически 😊. Чтобы увидеть больше опций, вам нужно нажать w в терминале, чтобы открыть список опций шутки.

Updated snapshot, GREEN

НИКОГДА не изменяйте файл snapshot вручную.


Заключение

В этой первой статье мы сделали следующее:

  • Описали в то, что мы будем делать
  • Спланировали наше приложение
  • Осуществили начальную настройку нашего проекта. Оставили только минимально необходимые файлы для работы
  • Объяснили что необходимо тестировать
  • Создали наш первый тест

Вторая часть
Твиттер автора оригинальной статьи : @DKuroski

Была ли вам полезна эта статья?
[4 / 5]

Spread the love
Подписаться
Уведомление о
guest
0 Комментарий
Oldest
Newest Most Voted
Inline Feedbacks
View all comments