JavaScript QA — делаем все правильно с самого начала

Содержание:

JavaScript — это широчайшая функциональность, незаменимый user experience, и ты всегда на волне последних трендов веб-дизайна. Есть и минусы, за удобство и красоту надо платить: большой объем кода в проекте, и да — все написанное на JS приходится очень много тестировать. Выход — в автоматизации, так быстрее, проще, надежнее.

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

JavaScript все еще первый и главный язык тестировщика, а Python пока не очень серьезный конкурент. Чтобы оценить мощь JS, надо овладеть лучшими практиками JavaScript для QA; далее — попытка систематизировать эти практики.

Правильное поведенческое тестирование

Наверное, пункт первый это behavioral testing, то есть поведенческое тестирование. Старайся смотреть на код с точки зрения именно тестировщика, а не разработчика. Это сэкономит время, по крайней мере за счет пропуска неприоритетных тестов. Быстрый пример: можно, ничего не теряя, пропустить тесты внутренних переменных:

it('should login with valid credentials, () => {
  userManager.login('UserId', 'Password');
  expect(userManager._usersList[0].name).toBe('UserId');
  expect(userManager._usersList[0].password).toBe('Password');
});

Здесь пользователь пытается залогиниться в веб-приложение с логином/паролем, взятыми из соответствующего списка. Такой тест лучше переписать вот так:

it('should login with valid credentials', () => {
  userManager.login('UserId', 'Password');
  expect(userManager.login('John', 'Password')).toBe(true);
});

Мы не проверяли значения внутренних переменных — и все равно получили нужный результат.

Такая практика улучшает расширяемость тестов, а также позволяет команде лучше понимать свои тест-кейсы. Она считается, вероятно, одной из лучших, помогающих избежать “рандомно падающих”, то есть нестабильных flaky-тестов на JS.

Когда нужно юнит-тестирование

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

Юнит-тесты не касаются внешних систем, они находятся на уровне “юнитов”. Если тест “красный” при запуске в системе неправильной конфигурации, такой тест не считается юнит-тестом.

В случае необходимости тестирования сложного высокоуровневого функционала рекомендуется писать end-to-end-тесты, функциональные тесты, и интеграционные. Это позволяет протестировать функции и их интеграцию. Обычно здесь применяют Selenium.

Сдвиг влево

Концепция “Test-driven development”, как гласит классическое определение, разработана для улучшения качества кода и ускорения сдвига влево. Эта практика позволяет раньше находить дефекты.

Однако, написание тест-кейсов до написания кода — само по себе еще не гарантирует полное отсутствие багов, а просто максимизирует тестовое покрытие, и лучше удовлетворяет бизнес-требования.

Как внедряется TDD применительно к JavaScript? Например, мы пишем тест валидации:

  • Считываем значения из полей
  • Применяем правила обработки цифр/букв
  • Если что-то не так, возвращаем ошибку

В форме проверяем только поля имени и контакта. Скрипт:

it('should validate a form with all possible data types', function () {
    const name = form.querySelector('input[name="first-name"]');
    const contact = form.querySelector('input[name="contact"]');
    name.value = 'John';
    age.value = ‘9051513622’;
    const result = validateForm(form);
    expect(result.isValid).to.be.true;
    expect(result.errors.length).to.equal(0);
});

HTML-форма:

form class="test-form">
    <input name="first-name" type="text" data-validation="alphabetical" />
    <input name="contact" type="text" data-validation="numeric" />
</form>

Так как мы не написали код валидации формы, тест упадет. Чтобы он прошел, надо написать код, который будет валидировать форму.

Когда нужно остановиться

Упомянутый выше TDD-подход ориентирован на повышение тестового покрытия. Это любят проджект-менеджеры. А иногда нужно не перегнуть палку с покрытием, понимая разницу между покрытием кода и тестовым покрытием.

Если спросишь кого-то, а какое должно быть идеальное тестовое покрытие, скорее всего скажут — 100%, или как минимум 80%. Если такая цель дана команде, они будут “вытягивать покрытие”. И есть вероятность, что из-за спешки не протестируют некую важную функцию.

Что такое идеальное покрытие? Невозможно сказать точно. Если в проекте автоматизация в Selenium, бывает трудно добиться высокого покрытия — очень много времени уйдет на написание правильных тестов. Это скажется, когда в проекте кроссбраузерные и кроссплатформенные тесты. 

Работа с такими тестами бывает монотонной, и нужны специалисты в Selenium. Каждое изменение в коде должно пройти тестирование, то есть тогда лучше ограничиться тестированием ветвлений.

“Портативный” фреймворк автоматизации

Тестировщики тратят много времени на конфигурацию. Это вредит на больших проектах. Скрипт, отлично работающий на машине, где фреймворк создавался, может плохо работать на других.

А если тесты запускаются на CI-сервере? Если фреймворк автоматизации не “портативный”, тестирование становится проблемным. 

Для начала, избавься от привычки хранить файлы автоматизации на локальной машине. Вместо этого отправляй их в фреймворк. Если файлы большие, можно подумать о хранилище на Amazon S3, например. Если работаешь в Selenium, можно избежать проблем с конфигурацией, используя облачную платформу. Можно применять POM-модель, отделяя веб-локаторы от логики, чтобы изменения в UI включали модификации в репозитории веб-локаторов (а не в коде теста).

Давай осмысленные имена тестам

Это одна из недооцененных практик тестирования на JavaScript. Правильное и понятное именование функций, переменных и тестов — хорошая практика.

Имена тестов должны быть понятными и “объявлять” их предназначение. Имена тестов должны:

  • Обозначать цель теста
  • Легко найти “место поломки” в функции
  • Улучшить реюзабельность тест-кейсов и сэкономить время

Так называть не рекомендуется:

[@Test](http://twitter.com/Test)
public void employeeSearchTest() {.....}

Плохо, потому что название ничего не говорит о сценарии. Лучше так:

[@Test](http://twitter.com/Test)
public void userShouldBeAbleToSearchEmployeeNameFromDatabase() {.....}

BDD-подход

Behavior-driven development, как и TDD-методика, направлена на повышение покрытия кода и тестового покрытия. BDD опирается на постоянную коммуникацию и общее понимание продукта тестировщиками и разработчиками. Это достигается созданием сценариев “желаемого поведения” продукта.

BDD особенно подходит для автоматизации UI. Например:

HomePage homePage = new HomePage();
homePage.open();
homePage.waitUntilPageLoaded();
Assert.assertEquals(homePage.getTitle(), "FlightBookingDemo");
Assert.assertEquals(homePage.getTitle(), "Welcome to the Flight Booking Demo Site!");
FlightsPage flightsPage = homePage.findFlights("Bangalore","Mumbai");
List<String> foundFlights = flightsPage.getFlights();
Assert.assertTrue(foundFlights.size() > 0);

Опытный тестировщик должен уметь делать такие скрипты. А для бизнес-менеджеров они “вне контекста”. Представим скрипт, написанный на специфическом языке типа Gherkin:

Given that a user opens the home page
The user should view a homepage with title "FlighBookingDemo" and heading "Welcome to the Flight Booking Demo Site!"/
When the user searches for flights from Bangalore to Mumbai
Then the user should find some flights

Так BDD-подход реализуется в Cucumber. Он позволяет снизить дублирование кода, улучшить читаемость, и упростить правки. Люди в команде, не владеющие ЯП, таким образом смогут участвовать в создании сценариев. Если можно, применяй BDD вместо TDD, чтобы привлекать к сценариям специалистов, не владеющих ЯП.

Не надо делать mock’и везде

Такая привычка есть у разработчиков, обычно они это делают, когда пишут свои юнит-тесты. Далее им приходится тестировать if…else. Такие тесты не имеют большой ценности, потому что тесты if…else можно писать на любом ЯП.

Делать моки есть смысл только для самых низкоуровневых зависимостей, или других I/O-операций (API, вызовы из базы данных и подобное). Тестируется корректность имплементации private-методов.

Например, нужно написать код на страницы магазине, чтобы пользователь мог вычислить цену товара с НДС. В следующей функции мы берем цену продукта, вычисляя НДС. Функция вызывает внутренний метод calculateVATAddition, который вызывает API getVATRate. Здесь не надо делать мок функции calculateVATAddition. Нужен только мок функции getVATRate, потому что у тебя нет доступа к результату вызова этого API.

class ProductService {
    // Internal method
    calculateVATAddition(priceWithoutVAT) {
        const vatPercentage = getVATRate(); // external API call -> Mock
        const finalprice = priceWithoutVAT * vatPercentage;
        return finalprice;
    }
    //public method
    getPrice(productId) {
        const desiredProduct = DB.getProduct(productId);
        finalPrice = this.calculateVATAddition(desiredProduct.price); // Don't mock this method
        return finalPrice;
    }
}

Избегай повторения

Понятно, что дублирование кода усложняет читабельность и обслуживание. Плохой пример:

if($.browser.chrome) { 
console.log(“Browser is Chrome”);
} else if ($.browser.mozilla) {
console.log(“Browser is Firefox”);
} else if ($.browser.msie) {
console.log(“Browser is IE”);
} else {
console.log(“Browser is unknown”);
}

Цель была:

  1. Проверить браузер, в котором открыта страница
  2. Вывести имя браузера в Консоль
  3. Если имени браузера нет в списке, выдать сообщение “Неизвестный браузер”

Примерно так:

escribe("Browser", function(){
    it("should print Browser is Chrome", function(){
      expect($.browser.chrome) is true;
    });
    it("should print Browser is Firefox", function(){
      expect($.browser.mozilla) is true;
    });
 it("should print Browser is IE", function(){
      expect($.browser.msie) is true;
    });
it("should print Browser is Unknown", function(){
      expect($.browser.chrome) is false;
    });

it("should print Browser is Unknown", function(){
      expect($.browser.mozilla) is false;
    });

it("should print Browser is Unknown", function(){
      expect($.browser.msie) is false;
    });
});

Много продублированного кода. Код — фактически копия имплементации, и нет правильной логики.

Например, если ($browser.chrome) равно false, но ($browser.msie) равно true, тест все равно выдаст ложный негативный результат.

А у нас все будет хорошо, если просто напишем:

it("should return the correct browser", function(){
var browsers= 'chrome,mozilla,msie';
expect($.browser.browsers).toEqual(true);
});

Эффективное кроссбраузерное тестирование

Понятно, что в 2022 году сайт должен идеально выглядеть и быть доступным с любых устройств и браузеров. Это делает кроссбраузерное тестирование обязательным, по крайней мере что касается пользовательских продуктов. Хорошее кроссбраузерное тестирование это одна из лучших практик в JS QA.

Чтобы правильно протестировать все варианты браузеров и ОС, надо следующее:

  • Присваивай категории багам “по браузерам”, поскольку некоторые баги являются “браузерно-специфическими”. Таким образом, необязательно тестировать все (не)возможные сочетания ОС/браузер, это уйма времени. Надо бы создать матрицу, где будут хорошо видно приоритеты.
  • Надо хорошо знать требования насчет браузеров и устройств от своих клиентов. Далее определиться, нужно ли тестировать сайт на старых версиях браузеров. Например IE почти мертв, но все-таки его надо протестить, если предполагаемые клиенты все еще “сидят в експлорере”. В таком случае есть смысл сделать отдельный stylesheet для старых браузеров.
  • Вместо долгой настройки локальной инфраструктуры лучше сэкономить время, деньги, и усилия, и тестировать на облаке. 

Сделай сьют управления тестами

Если работаешь в большом проекте, бывает большой объем тестовых данных и скриптов. Тогда понадобится специальный инструмент управления ими, который:

  • Уменьшает затраты времени на написание и выполнение скриптов. В некоторых инструментах AI-мозг подскажет, как писать эти скрипты. 
  • Упорядочивает всю информацию в едином дашборде, куда имеют (настраиваемый) доступ члены команды. Лид проверяет прогресс по отдельным тестировщикам, мониторит эффективность команды.
  • Расширяемое окружение — а значит тесты выполняются быстрее
  • Экономит время, уменьшая лишние действия присвоения статуса бага и обновления процесса
  • Предполагает интеграцию с Jira, Github, Slack

Соблюдай законы о личных данных

Эти законы зависят от места жительства заказчиков и клиентов. Соответствующий закон Евросоюза — GDPR, в США это CCPA. 

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

Во избежание, нужно часто проводить аудит тестовых данных, пользоваться комплайенс-сьютом для тестовых данных, или маскировать данные, или работать с “синтетическими” тестовыми данными.

Слишком много хелперов и хуков

Библиотеки-хелперы упрощают работу, но иногда вредны. Особенно когда у разработчиков/тестировщиков не хватает компетенции разобраться с тест-сьютом. Например нужно много времени на разбирательства, что случилось с хуком beforeAll, или beforeEach.

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

Если коротко

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

И еще: нужно избегать сложных сетапов и многоуровневой абстракции. Также, неплохо бы освоить паттерн ААА (Arrange, Act, Assert), и трехуровневую архитектуру

***

100 (да, сто) бесплатных советов по Java-инструментам QA

Какой была ваша первая зарплата в QA и как вы искали первую работу?

Мега обсуждение в нашем телеграм-канале о поиске первой работы. Обмен опытом и мнения.

Подписаться
Уведомить о
guest

0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии

Мы в Telegram

Наш официальный канал
Полезные материалы и тесты
Готовимся к собеседованию
Project- и Product-менеджмент

🔥 Популярное

💬 Telegram-обсуждения

Наши подписчики обсуждают, как искали первую работу в QA. Некоторые ищут ее прямо сейчас.
Наши подписчики рассказывают о том, как не бояться задавать тупые вопросы и чувствовать себя уверенно в новой команде.
Обсуждаем, куда лучше податься - в менеджмент или по технической ветке?
Говорим о конфликтных ситуациях в команде и о том, как их избежать
$1100*
медианная зарплата в QA в июне 2023

*по результатам опроса QA-инженеров в нашем телеграм-канале

Собеседование

19%*
IT-специалистов переехало или приняло решение о переезде из России по состоянию на конец марта 2022

*по результатам опроса в нашем телеграм-канале

live

Обсуждают сейчас