15. Библиотека Owner - qa-guru/knowledge-base GitHub Wiki
На своей локальной машине мы можем писать для каждой страницы и браузера создавать отдельный проект и писать код заново. Такой подход не совсем удобен и подразумевает за собой слишком много дополнительной работы, забирающей время. Можно упростить процесс, один раз написать файл конфигурации и запускать тесты для разных браузеров и страниц.
Для начала в проекте создадим пакет config
и в нем сперва создадим enum перечисление, в котором будут храниться все браузеры, в которых мы тестируем страницу. Для этого создадим перечисление с именем Browser
:
public enum Browser {
CHROME,
FIREFOX
}
Теперь создадим класс конфигурации с именем WebDriverConfig
и вынесем в него все настройки:
public class WebDriverConfig {
public String getBaseUrl() {
return "https://github.com";
}
public Browser getBrowser() {
return Browser.CHROME;
}
}
И в конце напишем класс, который будет отвечать за создание веб-драйвера на основе данных из класса конфигурации. Назовем класс, к примеру, WebDriverProvider
и напишем в нем следующий код:
public class WebDriverProvider implements Supplier<WebDriver> {
private WebDriverConfig config;
public WebDriverProvider() {
this.config = new WebDriverConfig();
@Override
public WebDriver get() {
WebDriver driver = createWebDriver();
driver.get(config.getBaseUrl());
return driver;
}
private WebDriver createWebDriver() {
if (config.getBrowser().equals(Browser.CHROME)) {
WebDriverManager.chromedriver().setup();
return new ChromeDriver();
}
if (config.getBrowser().equals(Browser.FIREFOX)) {
WebDriverManager.firefoxdriver().setup();
return new FirefoxDriver();
}
throw new RuntimeException("No such browser");
}
}
На этом шаге можно написать тест, который будет содержать чистый код без лишних строк. Также можно будет менять содержимое переменных в файле конфигурации и запускать тесты с разными параметрами:
public class SeleniumTest {
private WebDriver driver = new WebDriverProvider().get();
@Test
public void testGithubTitle() {
// код выполнения теста
String title = driver.getTitle();
assertEquals(title, "GitHub: Where the world builds software · GitHub");
}
@AfterEach
public void stopDriver() {
driver.quit();
}
}
В шаге выше нам удалось вынести все конфигурационные строки кода в отдельные файлы. Но не всегда удобно каждый раз идти в файл конфигурации, менять в нем переменные и снова запускать тест. Для автоматизации этого процесса можно использовать SystemGetProperty
.
Для этого модифицируем класс WebDriverConfig
таким образом:
public class WebDriverConfig {
public String getBaseUrl() {
return "https://github.com";
}
public Browser getBrowser() {
String browser = System.getProperty("browser");
return Browser.ValueOf(browser);
}
}
Теперь можно запускать тесты из консоли, указывая браузер, в котором следует запускать тест:
./gradlew clean test -Dbrowser=CHROME
Теперь мы можем работать с консолью и на ходу выбирать необходимый браузер для запуска, но если выполнить команду ./gradlew clean test
, то ничего полезного не произойдет и тесты упадут. Все дело в том, что мы не указали значение по умолчанию, которое будет запускаться, если не указать ничего дополнительно.
Для этого добавим еще одно условие в класс WebDriverConfig
, вместе с этим модифицируем метод getBaseUrl()
:
public class WebDriverConfig {
public String getBaseUrl() {
String baseUrl = System.getProperty("baseUrl");
if (Objects.isNull(baseUrl)) {
baseUrl = "https://github.com";
}
return baseUrl;
}
public Browser getBrowser() {
String browser = System.getProperty("browser");
if (Objects.isNull(browser)) {
browser = "CHROME";
}
return Browser.ValueOf(browser);
}
}
Теперь мы можем задавать из командной строки необходимый браузер и адрес страницы:
./gradlew clean test -Dbrowser=FIREFOX -DbaseUrl=https://testing.github.com"
Подключаем библиотеку уже привычным образом:
dependencies {
testImplementation(
'org.aeonbits.owner:owner:1.0.4',
)
testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.8.2')
}
Теперь мы можем упростить код класса WebDriverConfig
, но важно отметить, что класс надо сделать интерфейсом. Указываем ключи и значения по умолчанию:
public interface WebDriverConfig extends Config {
@Key("baseUrl")
@DefaultValue("https://github.com")
String getBaseUrl();
@Key("browser")
@DefaultValue("CHROME")
Browser getBrowser();
// зачитываем данные из командной строки
@Key("remoteUrl")
// обрабатывает дефолтное значение
@DefaultValue("http://localhost:4444/wd/hub")
// конвертируем в возращаемый тип
URL getRemoteUrl();
}
После этого надо вернуться к файлу WebDriverProvider
и изменить его следующим образом:
public class WebDriverProvider implements Supplier<WebDriver> {
private WebDriverConfig config;
public WebDriverProvider() {
this.config = ConfigFactory.create(WebDriverConfig.class,System.getProperties());
@Override
public WebDriver get() {
WebDriver driver = createWebDriver();
driver.get(config.getBaseUrl());
return driver;
}
private WebDriver createWebDriver() {
if (config.getBrowser().equals(Browser.CHROME)) {
WebDriverManager.chromedriver().setup();
return new ChromeDriver();
}
if (config.getBrowser().equals(Browser.FIREFOX)) {
WebDriverManager.firefoxdriver().setup();
return new FirefoxDriver();
}
throw new RuntimeException("No such browser");
}
}
Теперь мы можем также запускать тесты из консоли, но при этом файл конфигурации стал в разы меньше и его легче читать.
Важно: файлы конфигурации следует разбивать на маленькие фалы, отвечающие за определенную часть конфигов.
Данные для файлов конфигурации не всегда удобно хранить в SystemGetProperty
. Иногда удобнее вынести все в отдельные файлы. К примеру, нам надо написать файл конфигурации для теста под Android-смартфон. Создадим файл AndroidConfig
и заполним его:
@Config.Sources({
"classpath:pixel.properties"
})
public interface AndroidConfig extends Config {
@Key("platform.name")
String platformName();
@Key("platform.version")
String platformVersion();
@Key("device.name")
String deviceName();
}
Теперь в папке resources
создадим файл с названием pixel.properties
с данными для конкретного смартфона:
platform.name=Android
platform.version=27.0
device.name=Google Pixel XL
Все данные из этого файла будут использованы в конфигурации.
К примеру, нам надо запустить тест сначала на Android, а потом на iOS. Для этого сначала добавим файл с ресурсами для iOS и назовем его iphone12.properties
:
platform.name=iOS
platform.version=15.0
device.name=Apple iPhone 12 Pro
И добавим небольшое изменение в файл конфигурации:
@Config.Sources({
"classpath:${device}.properties"
})
public interface MobileConfig extends Config {
@Key("platform.name")
String platformName();
@Key("platform.version")
String platformVersion();
@Key("device.name")
String deviceName();
}
И теперь мы можем запускать тесты сразу для нескольких устройств:
public class MobileTest {
@Test
public void testAndroid() {
System.setProperty("device", "pixel");
MobileConfig config = ConfigFactory
.create(MobileConfig.class, System.getProperties());
assertThat(config.platformName()).isEqualTo("Android");
assertThat(config.platformVersion()).isEqualTo("27.0");
assertThat(config.deviceName()).isEqualTo("Google Pixel XL");
}
@Test
public void testIphone12() {
System.setProperty("device", "iphone12");
MobileConfig config = ConfigFactory
.create(MobileConfig.class, System.getProperties());
assertThat(config.platformName()).isEqualTo("IOS");
assertThat(config.platformVersion()).isEqualTo("14.0");
assertThat(config.deviceName()).isEqualTo("iPhone 12 Pro Max Ulra High");
}
}
Рассмотрим пример, когда мы тестируем систему локально и используем свои данные для входа, но на CI-системе необходимо использовать специальные данные.
В таком случае создаем следующий файл конфигурации:
@Config.Sources({
"file:/tmp/secret.properties",
"classpath:auth.properties"
})
public interface AuthConfig extends Config {
@Key("username")
String username();
@Key("password")
String password();
}
В этом случае система сначала будет искать данные для входа в "file:/tmp/secret.properties"
, но если не найдет, то перейдет к локальным данным "classpath:auth.properties"
.
Код теста:
public class AuthTest {
@Test
public void testLocalFile() throws IOException {
// вот это будет происходить в Jenkins, вы к этому не имеете доступа
String content = "username=secret-user\password=secret-pass";
Path propsPath = Paths.get("/tmp/secret.properties");
Files.write(propsPath, content.getBytes(StandardCharsets.UTF_8));
// вот это будет происходить в Jenkins, вы к этому не имеете доступа
AuthConfig config = ConfigFactory
.create(AuthConfig.class, System.getProperties());
assertThat(config.username()).isEqualTo("secret-user");
assertThat(config.password()).isEqualTo("secret-pass");
// вот это будет происходить в Jenkins, вы к этому не имеете доступа
Files.delete(propsPath);
// вот это будет происходить в Jenkins, вы к этому не имеете доступа
}
}