🦉 How to test Components 🦉

Содержание

Введение

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

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

Юнинт тесты

Написание юнит тестов для Owl компонентов зависит от фреймворка тестирования который вы используете в проекте. Но обычно это зависит от нескольких шагов:

  • создание файла теста: например SomeComponent.test.js,
  • в этом файле импортируйте код из SomeComponent,
  • добавьте тестовый кейс:
    • создайте реальный DOM элемент чтобы использовать его как тестовую фикстуру
    • создайте тестовое окружение
    • создайте экземпляр SomeComponent, прмонтируйте его к фикстуре
    • взаимодействуйте с компонентом и установике проверки его свойств.

Чтобы помочь в этом, полезно иметь файл helper.js, который будет содержать в себе некоторые общие служебные функции:

export function makeTestFixture() {
  let fixture = document.createElement("div");
  document.body.appendChild(fixture);
  return fixture;
}

export function nextTick() {
  let requestAnimationFrame = owl.Component.scheduler.requestAnimationFrame;
  return new Promise(function(resolve) {
    setTimeout(() => requestAnimationFrame(() => resolve()));
  });
}

export function makeTestEnv() {
    // application specific. It needs a way to load actual templates
    const templates = ...;

    return {
        qweb: new QWeb(templates),
        ..., // each service can be mocked here
    };
}

С таким файлом типичный набор тестов для Jest будет выглядеть так:

// в SomeComponent.test.js
import { SomeComponent } from "../../src/ui/SomeComponent";
import { nextTick, makeTestFixture, makeTestEnv} from '../helpers';


//------------------------------------------------------------------------------
// Setup
//------------------------------------------------------------------------------
let fixture: HTMLElement;
let env: Env;

beforeEach(() => {
  fixture = makeTestFixture();
  env = makeTestEnv();
  // мы устанавливаем здесь окружение по умолчанию для каждого компонента, созданного в тесте
  Component.env = env;
});

afterEach(() => {
  fixture.remove();
});

//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------
describe("SomeComponent", () => {
  test("component behaves as expected", async () => {
    const props = {...}; // зависит от компонента
    const comp = await mount(SomeComponent, { target: fixture, props });

    // делаем проверки
    expect(...).toBe(...);

    fixture.querySelector('button').click();
    await nextTick();

    // далаем другие проверки
    expect(...).toBe(...);
  });
});

Обратите внимание что Owl ожидает следующий кадр анимации для того, чтобы обновить DOM. Вот почему необходимо явно включать ожидание с помощью nextTick (или других методов) чтобы убедиться что DOM обновился.

Иногда бывает полезно подождать, пока Owl полностью завершит обновление компонентов (в частности, если у нас высокопараллельный пользовательский интерфейс). Следующий помощник просто опрашивает каждые 20 мс внутреннюю очередь задач Owl и возвращает промис, который разрешается, когда он пуст:

function afterUpdates() {
  return new Promise((resolve, reject) => {
    let timer = setTimeout(poll, 20);
    let counter = 0;
    function poll() {
      counter++;
      if (owl.Component.scheduler.tasks.length) {
        if (counter > 10) {
          reject(new Error("timeout"));
        } else {
          timer = setTimeout(poll);
        }
      } else {
        resolve();
      }
    }
  });
}