html текст
All interests
  • All interests
  • Design
  • Food
  • Gadgets
  • Humor
  • News
  • Photo
  • Travel
  • Video
Click to see the next recommended page
Like it
Don't like
Add to Favorites

Использование Selenium WebDriver для автоматического тестирования веб-интерфейса Яндекс.Почты tutorial

Без качественного тестирования невозможно разрабатывать и поддерживать крупный веб-сервис. На ранних этапах его развития часто можно обходиться только ручным тестированием по заданному тест-плану, но с появлением новых фич и увеличением количества тест-кейсов довольствоваться только им становится все сложнее и сложнее. В этой статье мы расскажем о том, как автоматизируем функциональное тестирование веб-интерфейса Яндекс.Почты с помощью Selenium WebDriver и Node.js.

Selenium

Помимо Selenium WebDriver существует ещё несколько решений для автоматического тестирования веб-интерфейсов, среди которых Watir, Zombie.js, PhantomJS. Но именно он стал практически стандартом. Во-первых, он имеет хорошую функциональность. А во-вторых, для него есть драйверы подо все распространённые браузеры — в том числе и мобильные — и платформы, чего не скажешь о headless-инструментах (Zombie.js, PhantomJS).

А почему именно Node.js? Потому что все фронтенд-разработчики Яндекс.Почты знают JavaScript, а именно они разрабатывают интерфейс и понимают, где и что в нём меняется от релиза к релизу.

Установка и настройка


Для установки и настройки Selenium WebDriver на локальной машине понадобятся:

  1. Java (http://www.java.com/en/download).
  2. Selenium server (скачать standalone версию можно тут — https://code.google.com/p/selenium/downloads/lis).
  3. Node.js + npm (http://nodejs.org/download).
  4. ChromeDriver (для тестирования в Google Chrome). Качается отсюда: code.google.com/p/chromedriver/downloads/list.

После установки всех зависимостей нужно:

  1. Установить selenium-webdriver для Node.js:
    npm install selenium-webdriver -g

  2. Запустить selenium server:
    java -jar selenium-server-standalone-{VERSION}.jar


Первый тест


Для примера, напишем простой тест (test.js):

var wd = require('selenium-webdriver');
var assert = require('assert');

var SELENIUM_HOST = 'http://localhost:4444/wd/hub';
var URL = 'http://www.yandex.ru';

var client = new wd.Builder()
   .usingServer(SELENIUM_HOST)
   .withCapabilities({ browserName: 'firefox' })
   .build();

client.get(URL).then(function() {
    client.findElement({ name: 'text' }).sendKeys('test');
    client.findElement({ css: '.b-form-button__input' }).click();

    client.getTitle().then(function(title) {
        assert.ok(title.indexOf('test — Яндекс: нашлось') > -1, 'Ничего не нашлось :(');
    });

    client.quit();
});


По коду все довольно просто:

  1. Подключаем selenium-webdriver;
  2. Инициализируем клиент с указанием нужного браузера и передачей хоста, на котором у нас висит selenium-server;
  3. Открываем www.yandex.ru;
  4. После загрузки вводим в поисковой строке (<input name=”text”>) посимвольно “test” и кликаем на кнопку (она будет найдена по CSS-селектору ‘.b-form-button__input');
  5. Получаем тайтл страницы результатов поиска и ищем в нем подстроку 'test — Яндекс: нашлось'.

Если при запуске (node test.js) никаких ошибок не произошло, то тест пройден успешно.

Все методы работы со страницей асинхронные. И, как видно на примере, каждый метод у объекта client возвращает promise-объект, на который можно навесить обработчики в случае успешного выполнения операции или возникновения ошибки (объект ошибки будет передан первым параметром).

Помимо использования промисов, для упрощения работы с API WebDriver.js имеет возможность работы в псевдосинхронном стиле, когда все вызовы методов ставятся в очередь и выполняются один за другим. Это реализовано через объект wd.promise.controlFlow(). Для контроля ошибок в таком случае используется событие “uncaughtException”:

wd.promise.controlFlow().on('uncaughtException', function(e) {
    console.log('Произошла ошибка: ', e);
});

Используя такой подход, код нашего теста можно переписать так:

wd.promise.controlFlow().on('uncaughtException', function(e) {
    console.log('Произошла ошибка: ', e);
});

client.get(URL);
client.findElement({ name: 'text' }).sendKeys('test');
client.findElement({ css: '.b-form-button__input' }).click();
client.getTitle().then(function(title) {
        assert.ok(title.indexOf('test — Яндекс: нашлось') > -1, 'Ничего не нашлось :(');
});
client.quit();

Возможные проблемы и ошибки


Но даже при выполнении такого, казалось бы, простого теста могут возникнуть разные ошибки. Мы расскажем о наиболее частых из тех, с которыми у вас есть вероятность столкнуться, и о том, как их предотвращать.

Разные версии клиента и сервера

Чтобы этого избежать, перед запуском теста необходимо убедиться, что ваша версия WebDriver.js совпадает с версией selenium-server. Иначе вы не сможете полноценно использовать все новые функции, а в некоторых случаях новые браузеры могут вообще не запуститься.

Используемый в тесте элемент не находится через findElement

У этого может быть несколько причин. Во-первых, нужно убедиться, что элемент действительно присутствует на странице, и, если вы используете css-селекторы, селектор матчится на элемент. Это вроде бы очевидно, но в условиях частого обновления верстки — как, например, в Яндекс.Почте — нужно следить за актуальностью селекторов в тестах. Чтобы немного упростить этот процесс, можно для всех ключевых элементов теста давать дополнительный класс (например, t-login-button) и договориться никогда не трогать такие классы при внесении изменений в верстку.

Во-вторых, элемент может появиться на странице динамически — во время выполнения каких-либо действий. Например, при смене значения свойства display. До этого момента, при попытке работы с такими элементами будет выведена ошибка: «Element is not currently visible and so may not be interacted with». Элемент считается невидимым, если для него выполняется хотя бы одно из перечисленных условий:

  • значение свойства display равно none;
  • значение свойства visibility равно hidden;
  • значение свойства opacity равно 0 (кроме операции клика);
  • значение атрибута type равно hidden (если это input);
    offsetWidth и offsetHeight равны нулю.

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

client.sleep(<количество миллисекунд>) 

Второй способ лучше — можно установить дефолтный таймаут на поиск элементов через:

client.manage().timeouts().implicitlyWait(<количество миллисекунд>);

Третий и самый хороший, на наш взгляд, вариант решения этой проблемы — поиск элемента с поллингом раз в 50 миллисекунд через метод isElementPresent:

var locator = { css: '.b-button' };
client.isElementPresent(locator).then(function(found) {
    if (found) {
       client.findElement(locator).click();
    }
});

Есть и четвёртый вариант. Можно выполнять любой код по наступлению некоторого условия через метод wait:

client.wait(function() {
     return client.findElement({ css: '.b-button' });
}, <таймаут в миллисекундах>).then(function() {
     // Нашли кнопку
    // ...
});

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

Чаще всего это связано с особенностью работы метода get. WebDriver считает, что страница загрузилась только тогда, когда произошло событие load, а оно, как известно, наступает после загрузки всех ресурсов страницы. То есть на время выполнения теста могут влиять разные сторонние ресурсы, которые используются на странице (например, счетчики, социальные кнопки и другие виджеты). К сожалению, поменять событие, которого ждет get в текущей версии WebDriver невозможно, но можно установить таймаут на время общей загрузки страницы:

client.manage().timeouts().pageLoadTimeout(<время в миллисекундах>);

Еще можно задать общий таймаут на ожидание команды сервером:

java -jar selenium-server-standalone-{VERSION}.jar -browserTimeout=<время в секундах>

Более сложные примеры


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

Работа с алертами и фреймами:

// получение текста алерта
	client.switchTo().alert().getText();
	// ввод текста в prompt
	client.switchTo().alert().sendKeys('name');
	// отмена
	client.switchTo().alert().dismiss();
	
	// переключение на определенный фрейм
	client.switchTo().frame('frame-id')
client.findElement({ css: '.b-another-button' }).click();

Выполнение произвольного JS-кода:

client.executeAsyncScript(function() {
		var cb = arguments[arguments.length - 1];
		$.getJSON(‘/suggest.json’, { query: 'pa' }, function(data) {
			cb(data);
		});
	}).then(function(data) {
		var contacts = JSON.parse(data).contacts;
		console.log(contacts[0]);
	});

В том числе и синхронно:

client.executeScript('window.scrollTo(0, 500)');

Эмуляция драг-н-дропа и нажатие нескольких клавиш одновременно:

var message = client.findElment('message');
var anotherMessage = client.findElement('another-message');
var dropZone = client.findElement(‘drop’);
	var action = wd.ActionSequence(client)
			 .keyDown(wd.Key.SHIFT)
			 .click(message)
			 .click(anotherMessage)
			 .dragAndDrop(dropZone)
			 .keyUp(wd.Key.SHIFT);

	// …
	// вызов экшена
	action.perform(); 

Подробнее про возможности WebDriver можно почитать на docs.seleniumhq.org/docs/03_webdriver.jsp, а также в исходниках WebDriver.js.

Запуск нескольких тестов с помощью Mocha


До этого мы рассматривали запуск и проверку только одного теста в одном браузере, что, согласитесь, не очень удобно. Для того чтобы автоматизировать запуск нескольких тест-кейсов и упростить написание тестов под Node.js, есть несколько библиотек. Нам пришлась по душе библиотека Mocha от TJ Holowaychuk. Почему именно она? Потому что не навязывает какой-то конкретный стиль написания тестов и позволяет использовать любую библиотеку для ассертов. Также она имеет большое число репортеров в разных представлениях и форматах.

Простейщий тест-кейс с использованием Mocha и Chai (библиотека для ассертов) выглядит так:

var assert = require('chai').assert;
var webdriver = require('selenium-webdriver');
var config = require('../config');

var client = new webdriver.Builder()
    .usingServer('http://' + config.selenium.host + ':' + config.selenium.port + '/wd/hub')
    .withCapabilities({
        'browserName': config.browsers[0]
    }).build();

client.manage().timeouts().implicitlyWait(config.WAIT_TIMEOUT);

suite('Общее');

test('Загружаем http://www.yandex.ru', function(done) {
    client.get(‘http://www.yandex.ru’);

    client.isElementPresent({ css: '.b-morda-search-decor' }).then(function(result) {
        assert.isTrue(result);
        done();
    }, done);
});

test('Загружаем http://yandex.ru/yandsearch?text=test', function(done) {
    client.get(‘http://yandex.ru/yandsearch?text=test');

    client.isElementPresent({ css: '.b-serp-list’ }).then(function(result) {
        assert.isTrue(result);
        done();
    }, done);
});

after(function(done) {
    client.quit().then(done);
});

Так как все операции асинхронные, то и тесты должны быть асинхронными. В Mocha работа с ними сделана очень удобно. Чтобы указать, что тест асинхронный, нужно просто передать в функцию теста первым параметром колбек (в нашем случае done) и вызвать его, когда тест завершился. В случае неудачного завершения, в колбек передается объект ошибки.

Запускается тест-кейс так:

mocha --reporter spec --ui qunit --timeout 1200000 --slow 10000 test.js

Обратите внимание, что, помимо репортера и интерфейса для написания тестов, мы указали таймаут в 20 минут и порог, по которому Mocha определяет, что тест выполнился медленно. Таймаут нужно увеличить потому, что в общем случае Mocha используется для синхронных юнит-тестов и дефолтный таймаут равен двум секундам. В случае же с нашими тестами этого времени часто не хватает даже на открытие браузера. Чтобы не писать каждый раз такой длинный список флагов, можно сохранить их в файл mocha.opts (каждый флаг со значением на новой строке) и положить в папку с тестами (mocha по умолчанию выполняет все .js файлы из папки test). Тогда запуcкать выполнение тестов можно просто по mocha.

Параллельное выполнение тестов


С ростом количества тестов и увеличением их сложности время выполнения всего тестового плана даже в одном браузере может достигать нескольких минут. Одной из затратных операций является открытие браузера, поэтому для ускорения имеет смысл выполнять серию тестов в рамках одной сессии. Этого можно добиться с помощью создания нового потока выполнения — на самом деле, нового окна браузера — через метод wd.promise.createFlow. Перепишем наш первый тест на несколько параллельных запросов к поиску с ожиданием результатов:

var wd = require('selenium-webdriver');
var assert = require('assert');

var SELENIUM_HOST = 'http://localhost:4444/wd/hub';
var URL = 'http://www.yandex.ru';
var WAIT_TIMEOUT = 500;
var queries = ['test', 'webdriver', 'node.js'];

var flows = queries.map(function(query) {
    return wd.promise.createFlow(function() {
        var client = new wd.Builder()
            .usingServer(SELENIUM_HOST)
            .withCapabilities({ browserName: 'firefox' })
            .build();

        client.manage().timeouts().implicitlyWait(WAIT_TIMEOUT);

        client.get(URL);
        client.findElement({ name: 'text' }).sendKeys(query);
        client.findElement({ css: '.b-form-button__input' }).click();

        client.getTitle().then(function(title) {
            assert.ok(title.indexOf(query + ' — Яндекс: нашлось') > -1, 'Ничего не нашлось :(');
        });

        client.quit();
    });
});

wd.promise.fullyResolved(flows).then(function() {
    console.log('Все ок!');
});

Аналогичным способом можно открывать несколько браузеров сразу, меняя browserName при инициализации нового клиента. Но для более эффективного запуска тестов в разных браузерах и на разном количестве машин есть отдельный инструмент, который называется Selenium Grid. Он позволяет поднимать несколько selenium-server’ов на разных портах или разных машинах и управлять их конфигурацией. Таким образом, можно коннектиться к общему хабу серверов, и тесты сами будут выполняться на всех свободных серверах параллельно. Для настройки хаба нужно выполнить:

java -jar selenium-server-standalone-{VERSION}.jar -role hub

После этого по адресу localhost:4444/grid/ будет доступна консоль управления, где можно смотреть зарегистрированные сервера и управлять ими. Для регистрации сервера в хабе, выполните:

java -jar selenium-server-standalone-{VERSION}.jar -role webdriver -hub http://localhost:4444/grid/register -port <port>

Полезные ссылки

docs.seleniumhq.org — документация по Selenium.
code.google.com/p/selenium/wiki/WebDriverJs — документация по WebDriver.js.
dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html — черновик спецификации WebDriver API.
Читать дальше
Twitter
Одноклассники
Мой Мир

материал с habrahabr.ru

0

      Add

      You can create thematic collections and keep, for instance, all recipes in one place so you will never lose them.

      No images found
      Previous Next 0 / 0
      500
      • Advertisement
      • Animals
      • Architecture
      • Art
      • Auto
      • Aviation
      • Books
      • Cartoons
      • Celebrities
      • Children
      • Culture
      • Design
      • Economics
      • Education
      • Entertainment
      • Fashion
      • Fitness
      • Food
      • Gadgets
      • Games
      • Health
      • History
      • Hobby
      • Humor
      • Interior
      • Moto
      • Movies
      • Music
      • Nature
      • News
      • Photo
      • Pictures
      • Politics
      • Psychology
      • Science
      • Society
      • Sport
      • Technology
      • Travel
      • Video
      • Weapons
      • Web
      • Work
        Submit
        Valid formats are JPG, PNG, GIF.
        Not more than 5 Мb, please.
        30
        surfingbird.ru/site/
        RSS format guidelines
        500
        • Advertisement
        • Animals
        • Architecture
        • Art
        • Auto
        • Aviation
        • Books
        • Cartoons
        • Celebrities
        • Children
        • Culture
        • Design
        • Economics
        • Education
        • Entertainment
        • Fashion
        • Fitness
        • Food
        • Gadgets
        • Games
        • Health
        • History
        • Hobby
        • Humor
        • Interior
        • Moto
        • Movies
        • Music
        • Nature
        • News
        • Photo
        • Pictures
        • Politics
        • Psychology
        • Science
        • Society
        • Sport
        • Technology
        • Travel
        • Video
        • Weapons
        • Web
        • Work

          Submit

          Thank you! Wait for moderation.

          Тебе это не нравится?

          You can block the domain, tag, user or channel, and we'll stop recommend it to you. You can always unblock them in your settings.

          • habrahabr.ru
          • домен habrahabr.ru

          Get a link

          Спасибо, твоя жалоба принята.

          Log on to Surfingbird

          Recover
          Sign up

          or

          Welcome to Surfingbird.com!

          You'll find thousands of interesting pages, photos, and videos inside.
          Join!

          • Personal
            recommendations

          • Stash
            interesting and useful stuff

          • Anywhere,
            anytime

          Do we already know you? Login or restore the password.

          Close

          Add to collection

             

            Facebook

            Ваш профиль на рассмотрении, обновите страницу через несколько секунд

            Facebook

            К сожалению, вы не попадаете под условия акции