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

Spread the love

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

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

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

Второй test

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

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

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

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

  it('renders main child components', () => {
    // arrange
    const wrapper = shallowMount(UserView)
    const userSearchForm = wrapper.find(VUserSearchForm)
    const userProfile = wrapper.find(VUserProfile)

    // assert
    expect(userSearchForm.exists()).toBe(true)
    expect(userProfile.exists()).toBe(true)
  })
})

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

  • В строке 17 мы создаем наш wrapper и в нем мы ищем наши основные дочерние элементы
  • В строке 18 и 19 с помощью метода find из vue-test-utils мы проверяем, существуют ли они

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

RED — VUserSearchForm andVUserProfile do not exist

Давайте исправим это … нам нужно только создать их и сослаться на них в нашем UserView

src/components/VUserProfile.vue

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

<template>
  <div>
    UserProfile
  </div>
</template>

src/components/VUserSearchForm.vue

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

<template>
  <div>
    UserSearchForm
  </div>
</template>

src/views/UserView.vue

<script>
import VUserSearchForm from '@/components/VUserSearchForm'
import VUserProfile from '@/components/VUserProfile'
export default {
  name: 'UserView',
  components: {
    VUserSearchForm,
    VUserProfile,
  }
}
</script>

<template>
  <div>
    <VUserSearchForm />
    <VUserProfile />
  </div>
</template>

Сейчас наш тест стал успешно проходить. Нам нужно только обновить наш Snapshot. Нажмите u на терминале.

Press “u” to update the snapshots
GREEN

Некоторые люди могут сказать, что этот тест в конечном итоге лишает смысла наличие первого, который мы сделали ранее, но здесь мы проверяем только наличие основных компонентов. Нам не нужно делать этот тест ВСЕМ нашим дочерним компонентам, если у нас есть h1 или p или любой другой элемент, который не является «жизненно важным» для работы нашего компонента. Snapshot будет достаточно, и он также гарантирует любое текстовое изменение, будь то изменение класса или свойства. Тогда оба теста полезны и важны.

Этап рефакторинга

Используя тот факт, что мы завершили ЗЕЛЕНЫЙ этап, мы можем перейти к следующему этапу, реорганизовать наши тесты. Если вы заметили, у нас есть несколько повторений. Теперь я собираюсь предложить изменение структуры, чтобы использовать форму, которую я применяю ко всем своим приложениям.

Изменения в UserView.spec.js

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

describe('UserView', () => {
  const build = () => {
    const wrapper = shallowMount(UserView)

    return {
      wrapper,
      userSearchForm: () => wrapper.find(VUserSearchForm),
      userProfile: () => wrapper.find(VUserProfile)
    }
  }

  it('renders the component', () => {
    // arrange
    const { wrapper } = build()
    // assert
    expect(wrapper.html()).toMatchSnapshot()
  })

  it('renders main child components', () => {
    // arrange
    const { userSearchForm, userProfile } = build()

    // assert
    expect(userSearchForm().exists()).toBe(true)
    expect(userProfile().exists()).toBe(true)
  })
})
  • Здесь в строке 7 мы создали функцию для построения структуры наших тестов.
  • В строке 10 я возвращаю объект, содержащий нашу оболочку и все селекторы / элементы / утилиты, которые можно использовать среди тестов

Стоит подчеркнуть, что эти селекторы являются функциями, и это важно, так как мы хотим контролировать момент поиска элемента.

Если мы не будем использовать функции, с момента вызова build будет немедленно вызван wrapper.find.

  • В строках 19 и 26 через деструктуризацию мы вызываем функцию build и получаем то, что собираемся использовать для теста
  • Помним, что теперь наши селекторы — это функции, которые нужно вызывать в строках 29 и 30.

В этот момент функция build может не иметь особого смысла, и некоторые люди скажут: «Почему вы не создаете эти структуры в beforeEach»! Но поверьте мне, по мере развития, в этом будет больше смысла.

Третий test

В третьем тесте мы можем проверить привязки компонента. Давайте посмотрим, передает ли оно свойство для компонента VUserProfile, содержащего объект. Этот объект будет иметь всю информацию исследуемого пользователя.

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

describe('UserView', () => {
  const build = () => {
    const wrapper = shallowMount(UserView, {
      data: () => ({
        user: {}
      })
    })

    return {
      wrapper,
      userSearchForm: () => wrapper.find(VUserSearchForm),
      userProfile: () => wrapper.find(VUserProfile)
    }
  }
  
  ...
  ...

  it('passes a binded user prop to user profile component', () => {
    // arrange
    const { wrapper, userProfile } = build()
    wrapper.setData({
      user: {
        name: 'Daniel'
      }
    })

    // assert
    expect(userProfile().vm.user).toBe(wrapper.vm.user)
  })
})

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

Для простоты теста, только в строке 9, я передал свойств data нашему компоненту, содержащему нашего пользователя Github.

В строке 27 я назначаю значение этого объекта {name: ‘Daniel’}, а затем проверяю, получил ли наш компонент VUserProfile того же пользователя в качестве props.

RED

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

Файл UserView.vue

<script>
import VUserSearchForm from '@/components/VUserSearchForm'
import VUserProfile from '@/components/VUserProfile'
export default {
  name: 'UserView',
  components: {
    VUserSearchForm,
    VUserProfile,
  },
  data() {
    return {
      user: { name: '' }
    }
  }
}
</script>

<template>
  <div>
    <VUserSearchForm />
    <VUserProfile :user="user" />
  </div>
</template>

Только вставки свойства не достаточно. Нам нужно поместить это объявление в наш VUserProfile.

<script>
export default {
  name: 'UserProfile',
  props: {
    user: {
      type: Object,
      required: true,
      default: () => ({})
    }
  }
}
</script>

<template>
  <div>
    UserProfile
  </div>
</template>
GREEN

Здесь мы гарантируем, что наш UserView.vue передает желаемое свойство для VUserProfile.vue.

Используя тот факт, что мы находимся на зеленом этапе, мы можем думать, что в этом случае было бы совершенно нормально сохранить состояние нашего приложения внутри UserView.vue. Но для демонстрационных проблем скажем, что этот пользователь Github, используется в других местах нашего приложения.

Далее мы собираемся отправить этого пользователя в наше хранилище и проверить, отправляем ли мы user в store вместо свойства data.


Заключение

В этой статье мы сделали:

  • Наш второй и третий тест
  • Рефакторинг всех тестов с использованием функции, отвечающей за монтирование нашего компонента, выставляя все селекторы дочерних элементов

Далее, мы завершим наш компонент с интеграцией с хранилищем.

Следующая часть
Твиттер автора оригинальной статьи : @DKuroski

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

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

При прогоне последнего теста

 expect(userProfile().vm.user).toBe(wrapper.vm.user)

Не вернет объект, чтобы тесты прошли нужно заменить:

 wrapper.setData({
      props: {
        name: 'Daniel'
      }
    })