Один браузер — хорошо, а три…Лучше?

cross

Один из аспектов тестирования веб-приложений это кросс-браузерность, то есть способность приложения адекватно и одинаково хорошо работать в различных браузерах. Запускать одни и те же тесты в разных браузерах, это же увеличение времени тестирования, так можно ли как то распараллелить кросс-браузерность, чтобы тестировать сразу в трех основных браузерах (Internet Explorer, Mozilla Firefox, Google Chrome) одновременно?

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

  1. Открываем наш файл настройки testng.xml и начинаем настройку с него
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Test class" parallel="tests" thread-count="3" > 

  <test verbose="1" name="testing-firefox" annotations="JDK">
    <parameter name="driver-name" value="firefox"/>
    <classes>
      <class name="ClassOne"></class> 
      <class name="ClassTwo"></class>
      <class name="ClassThree"></class>
    </classes>
 </test>

 <test verbose="1" name="testing-ie" annotations="JDK">
 <parameter name="driver-name" value="ie"/>

    <classes>
      <class name="ClassOne"></class> 
      <class name="ClassTwo"></class>
      <class name="ClassThree"></class>
    </classes>

</test>

 <test verbose="1" name="testing-chrome" annotations="JDK">
 <parameter name="driver-name" value="chrome"/>

   <classes>
      <class name="ClassOne"></class> 
      <class name="ClassTwo"></class>
      <class name="ClassThree"></class>
    </classes>

 </test>
</suite>

Все колдовство начинается вот тут:

<suite name="Test class" parallel="tests" thread-count="3" >

мы объявляем, что будем параллелить именно тесты (можно еще классы, методы) и использовать для этого нужно 3 потока. При этом важно понимать что тут под словом тест имеется в виду не маленький метод в вашем классе с аннотацией @Test, а структура в xml файле, заключенная в тегах <test>.

В следующих строках мы говорим название нашего теста и передаем в тест параметр с именем желаемого браузера

<test verbose="1" name="testing-firefox" annotations="JDK">
    <parameter name="driver-name" value="firefox"/>

затем в тегах <class> у нас перечислены одни и те же 3 класса, которые мы будем гонять в разных браузерах.

2. Нам нужен метод, который возвратит драйвер нужного браузера, по его имени и мы можем набросать что-то простенькое вроде

public WebDriver getDriverByName(String browserType) {
    WebDriver driver=null;
    if (browserType.equals("ie")) {
        System.setProperty("webdriver.ie.driver", "C://Vendors/IEDriverServer.exe");
        driver = new InternetExplorerDriver();
    }
    if (browserType.equals("chrome")) {
        System.setProperty("webdriver.chrome.driver", "C://Vendors/chromedriver.exe");
        driver=new ChromeDriver();
    }
    if (browserType.equals("firefox")) {
        driver = new FirefoxDriver();
    }
    return driver;
}

по адресу C://Vendors/ (можно и  в папке проекта) у вас должны лежать заблаговременно скачанные диструбутивы ИнтернетЭксплорер Сервер и ХромДрайвер.

3. Ну и перед началом Теста, у нас должен быть параметризованный метод, который примет имя браузера и создаст его

@Parameters("driver-name")
@BeforeTest
public void startTest(String browserName) throws Exception { 
    driver = getDriverByName(browserName);
    driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
    driver.manage().window().maximize();    
}

4. Останется только верно настроить ваши тесты, чтобы они в пределах одного теста (<test>) использовали один и тот же драйвер.

В итоге я очень скоро наблюдал как одинаковые тесты одновременно гоняются в 3 основных браузерах, но радость от победы увы была не полной, теперь навалю вам дегтя в горшочек с медом:

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

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

Ну-ка, повтори! Перезапуск проваленного теста на TestNG

photo

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

Один из способов это узнать -перезапустить проваленный тест на исполнение еще раз, да не руками после прогона всех тестов, а автоматически и сразу. Упадет повторно? Что ж  — пора искать проблему, разбираться. Не упадет? Значит проблема действительно была случайной!

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

Для этого в нашем распоряжении есть интерфейс iRetryAnalyser, который как раз позволяет выполнить задуманное. Итак вот весь код класса, имплементирующего интерфейс:

import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;

public class RunTestAgain implements IRetryAnalyzer {
    private int nowCount=0;
    private int maxCount=1;


    @Override
    public boolean retry(ITestResult iTestResult) {
        if (nowCount<maxCount) {
            nowCount++;
            return true; //пока истина перезапускаем
        }
        System.out.println("ТЕСТ ПРОВАЛЕН ДВАЖДЫ!!! "); // пишем в лог или делаем скриншот
        nowCount=0;
        return false;
    }
}

Все должно быть понятно, но немного проговорим: переменная maxCount — это максимальное количество перезапусков теста, ведь можно и не дважды его прогнать, у меня стоит 1 так как я проверяю только 1 раз автоматически, дальше лучше разбираться самому, так как два раза упавший тест — повод для интереса. Переменная nowCount это текущий счетчик, который инкрементится в случае сработки и сбрасывается после того как мы перестаем перезапускать тест. Метод retry возвращает булево значение и если оно ИСТИНА то перезапускает тест.

Теперь все, что нужно сделать — это прописать наш новый класс в аннотации к тесту или классу, например

@Test(priority = 2, retryAnalyzer = RunTestAgain.class)
public class SimpleTest {

Сразу оговорю важные моменты:

— перезапускать упавшие тесты нужно с умом, а не гоняясь за позеленением выполненного сьюта, это не панацея от кривых тестов

— сам интерфейс сыроват, он экспериментальный и как я понимаю на данный момент не поддерживается, хотя и работает

— не всегда срабатывает! Да, сказывается его экспериментальность, как я заметил есть проблемы при сработке, если тест упал по необработанному исключению. То есть, если тест падает ожидаемо по Ассерту, то все должно работать, а вот если вы не обработали и не предусмотрели какое-нибудь исключение, то повтор не запускается.

Стремитесь писать правильные тесты и тогда данный интерфейс вам не понадобится. А вот как их писать будем разбираться вместе!

Software-Testing.Ru