Мар 192016
 

BrowserMob Proxy + Selenium: автоматизация сбора данных о производительности

BrowserMob Proxy это бесплатная утилита, которая позволяет собирать данные о производительности веб-сайтов и браузеров. Обычно используется вместе с инструментальными средствами автоматизации, такими как Selenium или Watir. Proxy позволяет управлять поведением браузера, имитировать сетевой трафик и задержки, изменять HTTP запросы и ответы. К востребованной функциональности также можно отнести использование «черного» и «белого» списков, что позволяет, например, блокировать загрузку сторонних сайтов, не относящихся к тестированию Вашего веб-приложения, и тем самым повысить скорость тестов.
Также довольно часто BrowserMob используется для автоматизации доступа к сайтам, требующим Basic авторизацию.

Управлять прокси-сервером можно напрямую через Java интерфейс или через REST API. В этой статье мы будем рассматривать только интересующий нас Java интерфейс. Для того, чтобы подключить BrowserMob в проект нужно скачать библиотеку и добавить ее в проект, либо добавить соответствующую зависимость в pom.xml, если вы используете Maven:


			net.lightbody.bmp
			browsermob-proxy
			2.0-beta-8
			test
		

Если у Вас уже имеется подключенная в проект библиотека Selenium, то лучше исключить использование Selenium API, встроенного в BrowserMob. Но при этом учитывайте возможные несоответствия версий Selemium и BrowserMob:


			net.lightbody.bmp
			browsermob-proxy
			2.0-beta-8
			test
			
				
					org.seleniumhq.selenium
					selenium-api
				
			
		

Java реализация BrowserMob представлена в виде набора классов, основной из которых net.lightbody.bmp.proxy.ProxyServer.
Пример запуска прокси-сервера с FirefoxDriver и сбора данных загрузки страницы:

import net.lightbody.bmp.core.har.Har;
import net.lightbody.bmp.proxy.ProxyServer;

import org.junit.Test;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;

public class ProxyServerTest {

    @Test
    public void testMobProxyServer() throws Exception {
        // запуск прокси сервера
        ProxyServer server = new ProxyServer(4444);
        server.start();

        // получение Selenium proxy
        Proxy proxy = server.seleniumProxy();

        // конфигурация FirefoxDriver для использования прокси
        DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setCapability(CapabilityType.PROXY, proxy);

        WebDriver driver = new FirefoxDriver(capabilities);

        // создание HAR с меткой "yandex.ru"
        server.newHar("yandex.ru");

        // открытие страницы
        driver.get("http://yandex.ru");

        // получение данных HAR
        Har har = server.getHar();

        // здесь будет обработка полученных данных

        driver.quit();
        server.stop();
    }
}

Вывод в консоль свидетельствует об успешном запуске и остановке прокси-сервера на 4444 порту:

HttpSer~ - Version Jetty/5.1.x
Contain~ - Started HttpContext[/,/]
SocketL~ - Started SocketListener on 0.0.0.0:4444
Contain~ - Started net.lightbody.bmp.proxy.jetty.jetty.Server@133321d6
Threade~ - Stopping Acceptor ServerSocket[addr=0.0.0.0/0.0.0.0,port=0,localport=4444]
SocketL~ - Stopped SocketListener on 0.0.0.0:4444
Contain~ - Stopped HttpContext[/,/]
Contain~ - Stopped net.lightbody.bmp.proxy.jetty.jetty.Server@133321d6

HAR

BrowserMob Proxy предоставляет полученную им информацию в HAR (HTTP Archive) формате. Этот формат используется инструментами мониторинга HTTP для выгрузки собранных данных. Фактически HAR формат представляет из себя JSON структуру данных в кодировке UTF-8, которую можно просматривать и анализировать использую различные HAR визуализаторы.

После того как в тесте мы получили данные HAR их можно сохранить физически в виде файла:

// получение данных HAR
        Har har = server.getHar();

        // обработка полученных данных
        try {
            File file = new File("results\\Test.har");
            if (!file.exists()) {
                file.createNewFile();
            }
            FileOutputStream fos = new FileOutputStream(file);
            try {
                har.writeTo(fos);
            }
            finally {
                fos.close();
            }
        }
        catch (IOException e) {
            // обработка ошибки
            e.printStackTrace();
        }
        finally {
            driver.quit();
            server.stop();
        }

Обновив проект Вы увидите сохраненный файл Test.har в папке results (папка была предварительно создана перед запуском).

Полученный HAR файл содержит JSON данные, которые можно использовать как текстовую информацию, либо просмотреть в удобном представлении, используя один из существующих онлайн инструментов, например, HTTP Archive Viewer 2.0.15:

Получить интересующую Вас информацию и поместить ее в собственный отчет можно и без сохранения HAR файла. Java класс net.lightbody.bmp.core.har.HarLog предоставляет набор методов для выборочного получения нужной информации:

Har har = server.getHar();

        // получить информацию о браузере
        System.out.println(har.getLog().getBrowser().getName());
        System.out.println(har.getLog().getBrowser().getVersion());

        // список всех обработанных запросов
        for (HarEntry entry : har.getLog().getEntries()) {

            System.out.println(entry.getRequest().getUrl());
            // время ожидания ответа от сервера в миллисекундах
            System.out.println(entry.getTimings().getWait());
            // время чтения ответа от сервера в миллисекундах
            System.out.println(entry.getTimings().getReceive());
        }

Помимо сбора информации net.lightbody.bmp.proxy.ProxyServer позволяет манипулировать запросами и полученными ответами с помощью добавления интерсепторов запросов и ответов соответственно:

public void addRequestInterceptor(RequestInterceptor interceptor)
public void addResponseInterceptor(ResponseInterceptor interceptor)

Пример интеграции BrowserMob с драйверами ChromeDriver и InternetExplorerDriver:

public class ProxyServerTest {

    private static ProxyServer server;
    private static Proxy proxy;

    @BeforeClass
    public static void setUpProxy() throws Exception {
        server = new ProxyServer(4444);
        server.start();
        proxy = server.seleniumProxy();
    }

    @Before
    public void createNewHar() {
        server.newHar("yandex.ru");
    }

    /**
     * ChromeDriver
     */
    @Test
    public void testChromeDriver() throws Exception {
        ChromeOptions option = new ChromeOptions();
        option.addArguments("--proxy-server=localhost:" 
                                                + server.getPort());
        WebDriver driver = new ChromeDriver(option);

        driver.get("http://yandex.ru");

        driver.quit();
    }

    /**
     * InternetExplorerDriver
     */
    @Test
    public void testIEDriver() throws Exception {
        DesiredCapabilities capabilities = 
                                DesiredCapabilities.internetExplorer();
        capabilities.setCapability(CapabilityType.PROXY, proxy);
        capabilities.setCapability("ie.setProxyByServer", true);
        WebDriver driver = new InternetExplorerDriver(capabilities);

        driver.get("http://yandex.ru");

        driver.quit();
    }

    @After
    public void saveHAR() throws Exception {
        Har har = server.getHar();
        File file = new File("results\\" 
                         + har.getLog().getBrowser().getName() + ".har");
        if (!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream fos = new FileOutputStream(file);
        try {
            har.writeTo(fos);
        }
        finally {
            fos.close();
        }
    }

    @AfterClass
    public static void stopProxyServer() throws Exception {
        server.stop();
    }
}
Мар 192016
 

Selenium WebDriver: basic авторизация

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

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

Deprecated Одним из способов избежать появления диалогового окна может быть 
использование специального URL формата http://username:password@example.com/ 
для передачи учетных данных. Эта конструкция все еще поддерживается некоторыми 
браузерами, однако такое решение является официально устаревшим и иногда может 
отрицательно влиять на корректное выполнение дальнейших запросов на странице. 
Поэтому я настоятельно рекомендую не использовать такой подход для тестирования.

BrowserMob Proxy

При использовании basic аутентификации имя пользователя и пароль включаются в состав веб-запроса в стандартный HTTP заголовок «Authorization». Из этого можно заключить, что для успешного прохождения авторизации достаточно изменить HTTP заголовок перед отправкой на сервер. Сам Selenium не умеет манипулировать отправляемыми запросами, но для этих целей отлично подойдет прокси-сервер, в частности BrowserMob Proxy в силу простоты его подключения. Об интеграции и использовании BrowserMob с Selenium WebDriver можно почитать в предыдущей статье. В случае basic авторизации net.lightbody.bmp.proxy.ProxyServer предоставляет метод для ее автоматического выполнения:

server.autoBasicAuthorization("example.com", "username", "password"); 

Важно то, что первый аргумент autoBasicAuthorization это не URL, а именно доменное имя. Он не должен содержать http:// или других частей URL. Для того, чтобы использовать учетные данные для любого домена нужно оставить первый параметр пустым.

server.autoBasicAuthorization("", "username", "password"); 

Пример автоматической basic-аутентификации с BrowserMob Proxy:

import net.lightbody.bmp.proxy.ProxyServer;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;

public class ProxyServerBasicAutorizationTest {

    private ProxyServer server;
    private WebDriver driver;

    @Before
    public void startProxy() throws Exception {
        server = new ProxyServer(4444);
        server.start();
        server.autoBasicAuthorization("", "username", "password");
        Proxy proxy = server.seleniumProxy();

        DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setCapability(CapabilityType.PROXY, proxy);

        driver = new FirefoxDriver(capabilities);
    }

    @Test
    public void testAccessProtectedSite() throws Exception {
        driver.get("https://example.com");
        driver.findElement(By.className("sign-out"));
    }

    @After
    public void stopProxyServer() throws Exception {
        driver.quit();
        server.stop();
    }
}

И напоследок ложка дегтя, BrowserMob Proxy работает только с basic-аутентификацией и не предоставляет методов для работы с другими протоколами (Kerberos, NTLM и др.).

AutoIt

Для автоматизации прохождения basic-авторизации можно также использовать AutoIt: нужно перехватить диалоговое окно, ввести учетные данные и подтвердить ввод. Казалось бы ничего сложного, но каждый браузер для авторизации формирует окна разных классов: Firefox использует свои диалоговые окна, IE — класс диалоговых окон Windows, а Chrome и вовсе не создает отдельного окна для ввода. В этом можно убедиться используя утилиту AutoIt Window Info. В единичном случае, например, для Firefox скрипт AutoIt может выглядеть следующим образом:

Local $classForBasicAuthWindow = "[CLASS:MozillaDialogClass]";

;ожидание появления окна 10 секунд
WinWait($classForBasicAuthWindow, "", 10)

If WinExists($classForBasicAuthWindow) Then
	WinActivate($classForBasicAuthWindow)
 
	;имя пользователя
	Send($CmdLine[1] & "{TAB}"); 

	;пароль
	Send($CmdLine[2] & "{ENTER}"); 

EndIf

Файл .au3 необходимо переконвертировать в .exe для дальнейшего использования в ходе выполнения теста. Сделать это можно с помощью Aut2exe:

В случае с авторизацией процесс AutoIt нужно запускать до открытия страницы драйвером, чтобы появившееся окно не заблокировало выполнение теста:

public class AutoItBasicAutorizationTest {

    private final String username = "username";
    private final String password = "password";

    @Test
    public void testBasicAuthenticationFirefox() throws Exception {
        WebDriver driver = new FirefoxDriver();
        File autoIt = new File("src/test/resources/auth.exe");

        // запуск exe с передачей учетных данных
        // в качестве параметра
        Process p = Runtime.getRuntime().exec(
                autoIt.getAbsolutePath() + " " 
                                  + username + " " + password);

        driver.get("https://example.com");
        driver.findElement(By.className("sign-out"));
        driver.quit();
    }
}

Если есть необходимость тестировать во всех браузерах используя один скрипт AutoIt, то его можно расширить требуемыми условиями:

$timeoutSec = 10;
$lookingSec = 0;
While $lookingSec < $timeoutSec
	;Firefox or InternetExplorer or Chrome
    If WinExists("[CLASS:MozillaDialogClass]") _
		Or WinExists("[TITLE:Windows Security; CLASS:#32770]") _
			Or WinExists("[CLASS:Chrome_WidgetWin_1]", _
									"Authentication Required") Then
				;вводим учетные данные
				;имя пользователя
				Send($CmdLine[1] & "{TAB}"); 
				;пароль
				Send($CmdLine[2] & "{ENTER}"); 
				Exit;
    EndIf
    sleep(1000)
	$lookingSec += 1;
WEnd

Приведенные выше заголовки и классы окон могут отличаться в зависимости от версий браузеров и их настроек, проверить и получить Ваши текущие данные можно используя AutoIt Window Info.