Написание тестов принятия и компонентных тестов для кода Ember в Discourse

Автоматизированные тесты — отличный способ защитить ваш код от будущих регрессий. Многие знакомы с тем, как это делается в нашей кодовой базе Rails с помощью rspec, но сторона Javascript может быть для некоторых загадкой.

К счастью, в наши дни довольно легко добавить базовые тесты к вашему коду Ember!

Тесты компонентов

В предыдущем уроке этой серии мы добавили компонент под названием fancy-snack для отображения нашего снека с эффектом затухания фона. Давайте напишем для него тест. Создайте следующий файл:

test/javascripts/components/snack-test.js

import componentTest from "helpers/component-test";

moduleForComponent("fancy-snack", { integration: true });

componentTest("test the rendering", {
  template: "{{fancy-snack snack=testSnack}}",

  setup() {
    this.set("testSnack", {
      name: "Potato Chips",
      description: "Now with extra trans fat!",
    });
  },

  test(assert) {
    assert.equal(this.$(".fancy-snack-title h1").text(), "Potato Chips");
    assert.equal(
      this.$(".fancy-snack-description p").text(),
      "Now with extra trans fat!"
    );
  },
});

Чтобы запустить тест, откройте в браузере ваш сервер разработки по адресу /qunit?module=component%3Afancy-snack. Браузер выполнит тесты компонентов и выведет что-то вроде «2 утверждения из 2 пройдены, 0 не пройдены».

Обратите внимание, что на странице /qunit можно запускать и другие тесты. Вы можете просто выбрать новый тест в раскрывающемся списке Module в верхней части экрана.

Давайте разберёмся, как работает этот тест.

Строка template сообщает Ember, как мы хотим вставить наш компонент. Это точно такая же разметка, которую вы использовали бы для размещения компонента в шаблоне Handlebars, поэтому она должна быть вам знакома:

template: '{{fancy-snack snack=testSnack}}',

Обратите внимание, что здесь передаётся testSnack в качестве параметра snack. Это определяется в методе setup():

setup() {
  this.set('testSnack', {
    name: 'Potato Chips',
    description: 'Now with extra trans fat!'
  });
},

Я просто вставил некоторые тестовые данные. Это всё, что нужно сделать, чтобы Ember отрендерил компонент. Наконец, в методе test() у нас есть несколько утверждений:

test(assert) {
  assert.equal(this.$('.fancy-snack-title h1').text(), 'Potato Chips');
  assert.equal(this.$('.fancy-snack-description p').text(), 'Now with extra trans fat!');
}

Если вы используете this.$(), вы получаете доступ к селектору jQuery в вашем шаблоне. Утверждения здесь используют этот селектор, чтобы получить значение заголовка снека и его описания и сравнить их с ожидаемыми. Если значения совпадают, утверждения пройдут, и наш тест будет работать корректно.

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

Интеграционные тесты

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

Вот как мы можем написать интеграционный тест, который посетит маршрут /admin/snack и подтвердит, что снек был отрендерен:

test/javascripts/acceptance/snack-test.js

import { acceptance } from "helpers/qunit-helpers";
acceptance("Snack");

test("Visit Page", function (assert) {
  visit("/admin/snack");
  andThen(() => {
    assert.ok(exists(".fancy-snack-title"), "the snack title is present");
  });
});

В данном случае test() почти читается как английский! Первая команда говорит посетить URL /admin/snack. После этого есть метод andThen(). Этот метод необходим, чтобы убедиться, что все фоновые операции завершены, прежде чем тесты продолжатся. Поскольку код Javascript и Ember асинхронный, нам нужно убедиться, что Ember завершил все необходимые действия, прежде чем будут выполнены наши утверждения. Наконец, проверяется наличие элемента .fancy-snack-title.

Однако, если вы запустите этот тест, посетив /qunit?module=Acceptance%3A%20Snack, вы обнаружите, что тест не пройдет из-за ошибки AJAX.

Если вы помните, наш код включает в себя как сторону Rails, так и сторону Javascript, которая выполняет AJAX-запрос для получения данных. Интеграционный тест запустил сторону Javascript, но не знал, как получить данные от Rails.

Чтобы исправить это, нам нужно добавить фейковый ответ, используя отличную библиотеку pretender. Откройте файл test/javascripts/helpers/create-pretender.js и найдите строку:

this.get("/admin/plugins", () => response({ plugins: [] }));

Сразу под ней добавьте строку для возврата фейкового объекта снека, с которым будет работать наш интеграционный тест:

this.get("/admin/snack.json", () => {
  return response({ name: "snack name", description: "snack description" });
});

Вы можете прочитать этот код как: «для любого запроса к /admin/snack.json ответьте следующим response».

Если вы обновите страницу по адресу /qunit?module=Acceptance%3A%20Snack, ваш интеграционный тест должен получить данные через pretender, и тесты должны пройти.

Куда двигаться дальше

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


Этот документ находится под контролем версий — предлагайте изменения на GitHub.

15 лайков

My prior experience in writing qunit tests was based on the other howto

i.e. “acceptance” was simply

acceptance("Purple Tentacle", { loggedIn: true });

I have seen in other plugins where the test code contained “fake” JSON to test against. I wasn’t sure if that would be as good as testing against “real” data, so I wanted to avoid doing it that way.

The six tests passed, but I got a couple of rather angry looking “unhandled request” errors.

After finding an example of some “setup” code, I tried it and it solved the errors.

It works, but I’m not really sure why, nor why it’s needed for some but not for the majority of plugins I’ve seen that have qunit tests.

3 лайка

For a long time we weren’t great with testing plugins, so not many people followed our example and added tests.

You are right to add a response using pretender to handle AJAX calls.

4 лайка

Просто заметка: путь /qunit устарел. Теперь это /tests. Мне потребовалось некоторое время, чтобы разобраться :slight_smile:

1 лайк