1..Oi8UoU0.7

Cегодня речь пойдёт о том, как тестировать фронт-енд отдельно от бек-енда. Зачем это нужно? На самом деле, пока я слабо это представляю, но возможность отдельного тестирования интерфейсов от серверной логики кажется мне заманчивой. Почему? Потому что заебался писать вебдрайвер тестики на всякое говно. Мною были написаны сотни тестов на API и UI по ручным тесткейсам. Часто сталкивался с тем, что те, кто писал эти сраные тесткейсы, не очень понимали, что данные и отображение этих данных – разные вещи. Задумайтесь об этом, когда в следующий раз будете писать очередной унылый тест на вебдрайвере, который проверяет фильтр для сортировки результатов поиска или отображение очередной ошибочки.

Суть того, что я предлагаю можно посмотреть ТУТ. Кратко — используя одни и те же контрактные сущности в тестах, можно отделить тестирование серверной логики, обрабатывающей данные, от их отображения на UI.

Сами моки можно организовать по-разному. Например, заменить бэк-енд на http мок сервер (Python: pretenders, Java: mock-http-server).  Можно в процессе тестирования трафик всего браузера пустить через управляемый прокси и отправлять от него тестовые ответы (fiddler). Но, на моё мнение, всё это технически сложно и добавляет лишние звенья в тестирование, которые могут стать причиной нестабильности. А почему бы не замокать весь AJAX на уровне самого браузера?  Таким образом мы будем уверены, что то, что было в  мужской бане тестируется в браузере останется только в браузере. Такие тесты можно запускать  параллельно хоть тысячами, они не будут мешать друг другу (если, конечно, вы в своём коде тестов не наделаете зависимостей).

Теперь к теории мокирования AJAX. Всё AJAX общение вашего приложения с сервером идёт двумя способами: через объект класса  XMLHttpRequest или через функцию fetch (новая модная хипстерская штука, про которую можно почитать ТУТ). И, как ни странно, для того и для другого уже есть готовые решения для моков:

Про существование такой штуки, как fetch, я узнал совсем недавно. Поэтому ничего толкового по теме пока сказать не могу. Поэтому поговорим о мокировании XMLHttpRequest. Для этих целей мною когда-то был найден xhook. Вообще, изначально, он был придуман автором не для моков, а для того, чтобы делать низкоуровневое кэширование AJAX запросов, их модификации и вот этого всего. Но возможность использовать его для тестирования была также заложена.

Итак, установить любой из моков можно просто, добавих их js файл к себе на страницу. Вот как-нибудь так добавим тег в DOM:

<script src="//jpillora.com/xhook/dist/xhook.js" type="text/javascript"></script>

 

Обратите внимание, что схема не указывается.  Браузер определяет её самостоятельно. Добавить тег со скриптом на страницу можно разными способами. Можно попросить девелоперов делать специальную тестовую сборку фронтенда, а можно воспользоваться методом вебдрайвера executeScript(…). Пример:

if (window.xhook === void 0) {
    head = document.getElementsByTagName('head').item(0);
    script = document.createElement('script');
 
    script.setAttribute('type', 'text/javascript');
    script.setAttribute('src', '//jpillora.com/xhook/dist/xhook.js');
 
    head.appendChild(script);
}

 

У Xhook есть всего четыре метода:

xhook.before(handler(request[, callback])[, index]) // Устанавливаем обработчик после выполнения реального запроса к бэк-енду.
xhook.after(handler(request, response[, callback]) [, index]) //Устанавливаем обработчик до выполнения реального запроса к бэк-енду.
xhook.enable() //Заменить реальный XmlHttpRequest на xhook и включает все обработчики.
xhook.disable() //Возвращает XmlHttpRequest на место и отключает все обработчики.

 

Работает он с двумя типами объектов:

request Object:
method (String) (open(method,url))
url (String) (open(method,url))
body (String) (send(body))
headers (Object) (Contains Name-Value pairs set with setRequestHeader(name,value))
timeout (Number) (timeout)
type (String) (responseType)
withCredentials (String) (withCredentials)
 
response Object:
status (Number) Required when for fake responses (status)
statusText (String) (statusText)
text (String) (responseText)
headers (Object) (Contains Name-Value pairs retrieved with getAllResponseHeaders())
xml (XML) (responseXML)
data (Varies) (response)

 

Все эти методы соответствуют  документации по JS API.

Теперь рассмотрим простой пример мока и то, как он выглядит в JS:

xhook.before(function(req) {
          if(req.url.match(/InputValidator/)){ //Отвечать только по урлу котороый содержит "InputValidator"
            var response = new Response(); //Создаём объект респонса.
            response.status = 200; //Ставим http статус нашего фейкового ответа.
            response.statusText = 'OK'; //Текстовое представление статуса.
            response.text = '{}'; //Якобы наш бэк-енд ответил нам пустым Json'ом.
 
            return response; //Возвращаем наш поддельный ответ вместо реального ответа от сервера.
            }
    });

 

В этом куске кода устанавливаем перехватчик ДО выполнения запроса к серверу и говорим, чтобы он отреагировал нашим поддельным ответом с пустым джсоном и http статусом 200. Наш перехватчик отработает только в том случае, если url аякс запроса содержал в себе “InputValidator”. Выполнить его на странице вашего приложения можно из вебдрайвера через executeScript(…).

Вот, собственно, и вся магия. Если вам влом с этим заморачиваться, то мы с Никитой сделали пару удобных обёрток для использования всех этих штук вместе с WebDriver. Называется всё это безобразие Bemo, а про использование Java версии я напишу отдельный пост. Есть реализация для Java (ТУТ) и Python (ТУТ). Их уже можно использовать, API самих обёрток менять пока не собираемся. Новый функционал было решено внедрять по требованию пользователей. Сейчас мы работаем над тем, чтобы всё это работало ещё и для fetch.

Всем спасибо за внимание.

Опубликовать в Google Plus
Опубликовать в LiveJournal
Опубликовать в Мой Мир
Опубликовать в Одноклассники
Опубликовать в Яндекс