Selenium Grid в нескольких браузерах: должен ли каждый тестовый случай иметь отдельный класс для каждого браузера?

Я пытаюсь собрать свою первую среду тестирования, управляемую данными, которая запускает тесты через Selenium Grid/WebDriver в нескольких браузерах. Прямо сейчас, у меня каждый тест в свой класс, и я параметризовать браузер, так он проходит каждый тест один раз с каждого браузера.

Это распространено на больших тестовых фреймворках? Или каждый тестовый случай должен быть скопирован и настроен на каждый браузер в своем собственном классе? Итак, если я тестирую chrome, firefox и IE, должны ли быть классы для каждый, например: "TestCase1Chrome", "TestCase1FireFox", "TestCase1IE"? Или просто "TestCase1" и параметризовать тест для запуска 3 раз с каждым браузером? Просто интересно, как другие это делают.

параметризация тестов в один класс для каждого тестового случая упрощает обслуживание кода, не относящегося к браузеру, а дублирование классов, по одному для каждого случая браузера, упрощает обслуживание кода, относящегося к браузеру. Когда я говорю конкретный код браузера, например, щелчок по элементу. На ChromeDriver, вы не можете нажать в середине некоторых элементов, где на FirefoxDriver, вы можете. Таким образом, вам потенциально нужны два разных блока кода, чтобы просто щелкнуть элемент (когда он не кликабелен посередине).

для тех из вас, кто работает инженерами QA, которые используют селен, что было бы лучшей практикой здесь?

4 ответов


в настоящее время я работаю над проектом, который ежедневно проходит тесты 75k - 90k. Мы передаем браузер в качестве параметра тестам. Причины:

  1. как вы упомянули в вашем вопросе, это помогает в обслуживании.
  2. мы не видим слишком много код для конкретного браузера. Если у вас слишком много кода браузера, я бы сказал, что есть проблема с самим webdriver. Потому что одним из преимуществ selenium / webdriver является запись кода один раз и запустите его против любого поддерживаемого браузера.

разница, которую я вижу между моей структурой кода и той, которую вы упомянули в вопросе, заключается в том, что у меня нет тестового класса для каждого тестового случая. Тесты разделены на основе функций, которые я тестирую, и каждая функция будет иметь класс. И этот класс будет проводить все тесты как методы. Я использую testNG, чтобы эти методы можно было вызывать параллельно. Возможно, это не будет соответствовать вашему AUT.


если вы сохраните структуру кода, которую вы упомянули в вопросе, рано или поздно поддержание его станет кошмаром. Попробуйте придерживаться правила: один и тот же тестовый код (написанный один раз) для всех браузеров (сред).

данное условие заставит вас решить два вопроса:

1) Как запустить тесты для всех выбранных браузеров

2) Как применять конкретные обходные пути браузера без загрязнения тестового кода

на самом деле, это кажется это будет твой вопрос.

вот как я решил первую проблему. Во-первых, я определил все среды, которые я собираюсь протестировать. Я называю "средой" все условия, при которых я хочу запускать свои тесты: имя браузера, номер версии, ОС и т. д. Итак, отдельно от тестового кода я создал перечисление, подобное этому:

public enum Environments {

    FF_18_WIN7("firefox", "18", Platform.WINDOWS),
    CHR_24_WIN7("chrome", "24", Platform.WINDOWS),
    IE_9_WIN7("internet explorer", "9", Platform.WINDOWS)
    ;

    private final DesiredCapabilities capabilities;
    private final String browserName;
    private final String version;
    private final Platform platform;

    Environments(final String browserName, final String version, final Platform platform) {
        this.browserName = browserName;
        this.version = version;
        this.platform = platform;
        capabilities = new DesiredCapabilities();
    }

    public DesiredCapabilities capabilities() {
        capabilities.setBrowserName(browserName);
        capabilities.setVersion(version);
        capabilities.setPlatform(platform);
        return this.capabilities;
    }

    public String browserName() {
        return browserName;
    }
}

легко доработать и добавить окружающие среды когда вам нужно. Как вы можете заметить, я использую это для создания и извлечения DesiredCapabilities, которые позже будет использоваться для создания определенного WebDriver.

чтобы выполнить тесты для всех определенных сред, я использовал JUnit (4.10 в моем случае)org.тесты JUnit.экспериментальный.теории!--29-->:

@RunWith(MyRunnerForSeleniumTests.class)
public class MyWebComponentTestClassIT {

    @Rule 
    public MySeleniumRule selenium = new MySeleniumRule();

    @DataPoints 
    public static Environments[] enviroments = Environments.values();

    @Theory
    public void sample_test(final Environments environment) {

        Page initialPage = LoginPage.login(selenium.driverFor(environment), selenium.getUserName(), selenium.getUserPassword());

        // your test code here

    }   
}

тесты, определяемых как @Theory (а не @Test, как в обычных тестах JUnit) и передаются параметр. Каждый тест будет выполняться для всех определенных значений этого параметра, которые должны быть массивом значений, аннотированных как @DataPoints. Кроме того, вы следует использовать runner, который простирается от org.junit.experimental.theories.Theories. Я использую org.junit.rules подготовить мои анализы, поставив там всю необходимую сантехнику. Как вы можете видеть, я также получаю драйвер конкретных возможностей Через правило. Хотя вы можете использовать следующий код прямо в своем тесте:

RemoteWebDriver driver = new RemoteWebDriver(new URL(some_url_string), environment.capabilities());

дело в том, что, имея его в правиле, вы пишете код один раз и используете его для всех своих тестов. Что касается класса Page, это класс, в котором я помещаю весь код, который использует функциональность драйвера (найти элемент, навигации и т. д.). Таким образом, тестовый код остается четким и ясным, и вы снова пишете его один раз и используете во всех тестах. Итак, это решение для первого вопроса. (Я знаю, что вы можете сделать аналогичную вещь с TestNG, но я не пробовал.)

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

вот как это работает для ошибки драйвера Chrome, которую вы упомянули. Я создаю метод clickOnButton в BrowserSpecific с общим кодом для затронутого поведения:

public abstract class BrowserSpecific {

    protected final RemoteWebDriver driver;

    protected BrowserSpecific(final RemoteWebDriver driver) {
        this.driver = driver;
    }

    public static BrowserSpecific aBrowserSpecificFor(final RemoteWebDriver driver) {
        BrowserSpecific browserSpecific = null;

        if (Environments.FF_18_WIN7.browserName().contains(driver.getCapabilities().getBrowserName())) {
            browserSpecific = new FireFoxSpecific(driver);
        }

        if (Environments.CHR_24_WIN7.browserName().contains(driver.getCapabilities().getBrowserName())) {
            browserSpecific = new ChromeSpecific(driver);
        }

        if (Environments.IE_9_WIN7.browserName().contains(driver.getCapabilities().getBrowserName())) {
            browserSpecific = new InternetExplorerSpecific(driver);
        }

        return browserSpecific;
    }

    public void clickOnButton(final WebElement button) {
            button.click();
    }
}

и затем я переопределяю этот метод в определенном классе, например ChromeSpecific, где я размещаю обходной путь код:

public class ChromeSpecific extends BrowserSpecific {

        ChromeSpecific(final RemoteWebDriver driver) {
            super(driver);
        }

        @Override
        public void clickOnButton(final WebElement button) {

        // This is the Chrome workaround

            String script = MessageFormat.format("window.scrollTo(0, {0});", button.getLocation().y);
            driver.executeScript(script);

        // Followed by common behaviour of all the browsers
            super.clickOnButton(button);
        }
}

когда я должен учитывать конкретное поведение некоторого браузера, я делаю следующее:

 aBrowserSpecificFor(driver).clickOnButton(logoutButton);

вместо:

 button.click();

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

последний слово о выполнении тестов. Поскольку вы собираетесь использовать Selenium Grid, вы захотите использовать возможность параллельного запуска тестов, поэтому не забудьте настроить эту функцию для тестов JUnit (доступно с версии 4.7).


мы используем testng в нашей организации, и мы используем параметр, который TestNG дает, чтобы указать среду, т. е. браузер для использования, машину для запуска и любую другую конфигурацию, необходимую для конфигурации env. Browsername отправляется через xml-файл, который управляет тем, что нужно запустить и где. Он устанавливается как глобальная переменная. То, что мы сделали в качестве дополнительного, у нас есть наши пользовательские аннотации, которые могут переопределять эти глобальные переменные, т. е. если тест очень конкретно должен быть запустите chrome и никакой другой браузер, затем мы указываем то же самое на пользовательской аннотации. Таким образом, независимо от того, даже если параметр say run on FF, если он аннотирован chrome, он всегда будет работать на chrome.

Я почему-то считаю, что создание одного класса для каждого браузера не является хорошей идеей. Представьте, что поток меняется или есть немного здесь и там, и у вас есть 3 класса для изменения вместо одного. А если количество браузеров увеличивается, то еще один класс.

Что бы я suggest должен иметь код, который является browserspecific для извлечения. Таким образом, если поведение щелчка зависит от браузера, переопределите его, чтобы выполнить соответствующие проверки или обработки сбоев на основе браузеров.


Я делаю это так, но имейте в виду, что это чистый WebDriver без сетки или RC в виду:

// Utility class snippet
// Test classes import this with:  import static utility.*;
public static WebDriver driver;
public static void initializeBrowser( String type ) {
  if ( type.equalsIgnoreCase( "firefox" ) ) {
    driver = new FirefoxDriver();
  } else if ( type.equalsIgnoreCase( "ie" ) ) {
    driver = new InternetExplorerDriver();
  }
  driver.manage().timeouts().implicitlyWait( 10000, TimeUnit.MILLISECONDS );
  driver.manage().window().setPosition(new Point(200, 10));
  driver.manage().window().setSize(new Dimension(1200, 800));
}

теперь, используя JUnit 4.11+, ваш файл параметров должен выглядеть примерно так:

firefox, test1, param1, param2
firefox, test2, param1, param2
firefox, test3, param1, param2
ie, test1, param1, param2
ie, test2, param1, param2
ie, test3, param1, param2

затем, используя один .CSV параметризованный тестовый класс (с которым вы собираетесь запускать несколько типов браузера), в методе @Before annotated сделайте следующее:

  1. если текущий тест параметра является первым тестом этого типа браузера, и нет уже существуют открытые окна, открыть новое окно браузера текущего типа.
  2. если браузер уже открыт и тип браузера тот же, то просто повторно использовать тот же объект драйвера.
  3. если браузер открыт другого типа, чем текущий тест, то закройте браузер и повторно откройте браузер правильного типа.

конечно, мой ответ не говорит вам, как обрабатывать параметры: я оставляю это для вас, чтобы выяснить.