Один assert на тест или группировка

В Vue Test Utils тестирование компонентов строится вокруг рендеринга компонентов в изолированной среде и проверки их поведения. Ключевой принцип — один assert на тест или аккуратная группировка проверок, что обеспечивает ясность и поддержку тестов в долгосрочной перспективе.


Один assert на тест

Практика написания одного утверждения (assert) на тест помогает:

  • Быстро локализовать причину падения теста.
  • Поддерживать тесты простыми и читаемыми.
  • Облегчить понимание, какую конкретную функциональность проверяет тест.

Пример компонента Counter.vue:

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>

Тест с одним assert:

import { mount } from '@vue/test-utils'
import Counter from './Counter.vue'

test('счетчик увеличивается при клике', async () => {
  const wrapper = mount(Counter)
  const button = wrapper.find('button')

  await button.trigger('click')
  
  expect(wrapper.text()).toContain('1')
})

Здесь тест проверяет только одно поведение — увеличение счетчика. Если тест провалится, причина сразу ясна.


Группировка проверок в тестах

Когда проверок много, их объединяют в логические блоки с помощью describe и нескольких it или test. Это сохраняет принцип одного assert на тест, но позволяет структурировать проверку более сложного поведения компонента.

Пример с компонентом формы LoginForm.vue:

<template>
  <form @submit.prevent="submit">
    <input v-model="username" placeholder="Username" />
    <input type="password" v-model="password" placeholder="Password" />
    <button type="submit">Login</button>
    <p v-if="error">{{ error }}</p>
  </form>
</template>

<script>
export default {
  data() {
    return {
      username: '',
      password: '',
      error: ''
    }
  },
  methods: {
    submit() {
      if (!this.username || !this.password) {
        this.error = 'Все поля обязательны'
      } else {
        this.error = ''
      }
    }
  }
}
</script>

Группировка тестов:

import { mount } from '@vue/test-utils'
import LoginForm from './LoginForm.vue'

describe('LoginForm', () => {
  let wrapper

  beforeEach(() => {
    wrapper = mount(LoginForm)
  })

  test('показывает ошибку, если username пуст', async () => {
    await wrapper.find('button').trigger('click')
    expect(wrapper.text()).toContain('Все поля обязательны')
  })

  test('показывает ошибку, если password пуст', async () => {
    await wrapper.setData({ username: 'user' })
    await wrapper.find('button').trigger('click')
    expect(wrapper.text()).toContain('Все поля обязательны')
  })

  test('не показывает ошибку при заполнении всех полей', async () => {
    await wrapper.setData({ username: 'user', password: '1234' })
    await wrapper.find('button').trigger('click')
    expect(wrapper.text()).not.toContain('Все поля обязательны')
  })
})

Каждый test содержит один assert, а describe объединяет их по смыслу. Такая структура позволяет:

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

Практические рекомендации по использованию Vue Test Utils

  1. Избегать нескольких assert в одном тесте, если они проверяют разные аспекты поведения.
  2. Использовать beforeEach для общих настроек компонентов.
  3. Группировать тесты через describe по функциональным блокам или сценариям.
  4. Точно выбирать селекторы: лучше использовать data-testid вместо общих тегов или классов для устойчивости тестов.
  5. Асинхронные действия всегда оборачивать в await с trigger, setData или nextTick, чтобы тест проверял фактическое состояние компонента.

Проверка событий и реактивных данных

Vue Test Utils позволяет проверять события через emitted(), а данные компонента — через vm или text(). Принцип один assert сохраняется:

test('emit login событие с правильными данными', async () => {
  const wrapper = mount(LoginForm)
  await wrapper.setData({ username: 'user', password: '1234' })
  await wrapper.find('button').trigger('click')

  expect(wrapper.emitted().login[0]).toEqual([{ username: 'user', password: '1234' }])
})

Тест проверяет только факт генерации события с правильными аргументами.


Итоговое правило организации тестов

  • Один assert на тест обеспечивает простоту и понятность.
  • Группировка через describe позволяет структурировать тесты крупных компонентов.
  • Любая дополнительная проверка должна попадать в отдельный test.

Следование этим принципам делает тесты во Vue Test Utils надежными, поддерживаемыми и легко расширяемыми, что критично при работе с большими фронтенд-проектами.