From 6a19b574b8d8b8b0ea144004a70660a66fbdd0bf Mon Sep 17 00:00:00 2001 From: spawn Date: Tue, 14 Apr 2026 09:21:46 +0300 Subject: [PATCH] refactor project structure and stabilize execution --- README.md | 81 +++++++----------- docker-compose.yml | 1 - pom.xml | 28 +----- .../ru/otus/mobile/config/MobileConfig.java | 4 +- .../ru/otus/mobile/config/ProjectPaths.java | 8 +- .../mobile/driver/MobileDriverFactory.java | 3 +- .../java/ru/otus/mobile/guice/CoreModule.java | 4 +- .../__files/wishlist.apk | Bin 8 files changed, 40 insertions(+), 89 deletions(-) rename wishlist-349317-5fd795.apk => wiremock/__files/wishlist.apk (100%) diff --git a/README.md b/README.md index 96d48b8..74e773c 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,25 @@ # OTUS Homework 7: Mobile Testing -Проект содержит мобильные UI-тесты для приложения Wishlist на базе `selenide-appium`. +Проект содержит мобильные UI-тесты приложения Wishlist на `selenide-appium`. -Реализованы сценарии: -- создание и редактирование списка желаний; -- создание и редактирование подарка; +Сценарии: +- создание/редактирование списка желаний; +- создание/редактирование подарка; - изменение статуса резервирования подарка другого пользователя. -## Архитектура -- `Guice` для DI. -- `JUnit 5 Extension` вместо базового тестового класса. -- `AbsPageObject` -> `AbsBasePage` / `BaseMobileComponent`. -- `BlockingQueue` для распределения тестов по эмуляторам. -- код инфраструктуры и page object находится в `src/main/java`; -- в `src/test/java` находятся только тестовые классы. +## Что реализовано по требованиям +- `docker-compose` поднимает `wiremock` и 2 Android-эмулятора (`android-emulator-1`, `android-emulator-2`) для параллельного запуска. +- APK хранится в `wiremock/__files/wishlist.apk` и устанавливается через Appium capability `app`. +- DI на `Guice`, запуск через `JUnit 5 Extension`, балансировка эмуляторов через `BlockingQueue`. +- Подготовка тестовых данных выполняется через JDBC перед каждым тестом. +- Логи `logcat` сохраняются в `logcat.txt` через Appium logs API (без ADB-скриптов). -## Инфраструктура -`docker-compose.yml` поднимает: -- `wiremock` для раздачи `wishlist.apk`; -- `android-emulator-1` (Android 13, Appium `:4723`, VNC `:6080`); -- `android-emulator-2` (Android 12, Appium `:4725`, VNC `:6081`). - -Приложение не маунтится в эмулятор и не ставится через ADB. -APK скачивается Appium по capability `app`. -Источник APK для Wiremock: файл `wishlist-349317-5fd795.apk` в корне проекта. -Эмулятор запускается без `privileged`, доступ к аппаратной виртуализации передается через `/dev/kvm`. +## Структура +- `src/main/java` — инфраструктура, конфиги, page/component object. +- `src/test/java` — только тестовые классы. +- `wiremock` — маппинги и APK. ## Тестовые аккаунты -Тесты используют заранее созданные аккаунты: - `user1us / user1us` - `user2us / user2us` - `user3us / user3us` @@ -35,43 +27,34 @@ APK скачивается Appium по capability `app`. `user4us` используется как владелец подарка в тесте резервирования. -## Подготовка -Нужно задать доступ к БД, иначе `mvn test` завершится ошибкой: +## Запуск +1. Поднять окружение: +```bash +docker compose up -d +``` +2. Убедиться, что сервисы `wiremock`, `android-emulator-1`, `android-emulator-2` имеют статус `healthy`: +```bash +docker compose ps +``` + +3. Указать доступ к БД: +PowerShell: ```powershell $env:DB_URL="jdbc:postgresql://sql.otus.kartushin.su:5432/wishlist" $env:DB_USER="student" $env:DB_PASSWORD="student" ``` - -## Запуск -1. Поднять окружение: - +bash: ```bash -docker compose up -d +export DB_URL="jdbc:postgresql://sql.otus.kartushin.su:5432/wishlist" +export DB_USER="student" +export DB_PASSWORD="student" ``` -2. Дождаться статуса `healthy` у `wiremock`, `android-emulator-1`, `android-emulator-2`: - -```bash -docker compose ps -``` - -Тесты не ждут загрузку эмулятора сами. Готовность окружения проверяется на уровне Docker Compose. - -3. Запустить тесты: - +4. Запустить тесты: ```bash mvn test ``` -По умолчанию тесты запускаются параллельно на уровне классов (2 потока), а сессии распределяются по эмуляторам через `BlockingQueue`. - -Опционально можно явно задать пул эмуляторов: - -```powershell -$env:MOBILE_EMULATORS="android-emulator-1|http://localhost:4723|Android Emulator,android-emulator-2|http://localhost:4725|Android Emulator" -``` - -## Логи -После выполнения тестов logcat сохраняется в файл `logcat.txt` в корне проекта через Selenium/Appium logs API. +Тесты запускаются параллельно по классам (2 потока) и распределяются по эмуляторам через очередь. diff --git a/docker-compose.yml b/docker-compose.yml index 0755de4..c068771 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,6 @@ services: image: wiremock/wiremock:3.9.1 volumes: - ./wiremock:/home/wiremock - - ./wishlist-349317-5fd795.apk:/home/wiremock/__files/wishlist.apk:ro healthcheck: test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8080/__admin/health | grep -q 'healthy'"] interval: 10s diff --git a/pom.xml b/pom.xml index 7732354..d7c3dab 100644 --- a/pom.xml +++ b/pom.xml @@ -13,13 +13,10 @@ 21 21 UTF-8 - 7.3.1 7.3.1 5.10.2 2.0.13 1.5.6 - 4.20.0 - 2.29.1 7.0.0 3.13.0 3.2.5 @@ -28,24 +25,7 @@ 4.9.3 - - - - org.seleniumhq.selenium - selenium-bom - ${selenium.version} - pom - import - - - - - - com.codeborne - selenide - ${selenide.version} - com.codeborne selenide-appium @@ -63,16 +43,10 @@ org.junit.jupiter - junit-jupiter-engine + junit-jupiter ${junit.version} test - - io.qameta.allure - allure-junit5 - ${allure.version} - test - org.slf4j slf4j-api diff --git a/src/main/java/ru/otus/mobile/config/MobileConfig.java b/src/main/java/ru/otus/mobile/config/MobileConfig.java index ea89a14..28b3fda 100644 --- a/src/main/java/ru/otus/mobile/config/MobileConfig.java +++ b/src/main/java/ru/otus/mobile/config/MobileConfig.java @@ -47,13 +47,13 @@ public final class MobileConfig { } String[] parts = trimmed.split("\\|"); String id = parts.length > 0 ? parts[0].trim() : "emulator-1"; - String appiumUrl = parts.length > 1 ? parts[1].trim() : "http://localhost:4723"; + String appiumUrl = parts.length > 1 ? parts[1].trim() : "http://127.0.0.1:4723"; String deviceName = parts.length > 2 ? parts[2].trim() : "Android Emulator"; String emulatorAppUrl = parts.length > 3 ? parts[3].trim() : defaultAppUrl; emulators.add(new Emulator(id, appiumUrl, deviceName, emulatorAppUrl)); } if (emulators.isEmpty()) { - emulators.add(new Emulator("emulator-1", "http://localhost:4723", "Android Emulator", defaultAppUrl)); + emulators.add(new Emulator("emulator-1", "http://127.0.0.1:4723", "Android Emulator", defaultAppUrl)); } return emulators; } diff --git a/src/main/java/ru/otus/mobile/config/ProjectPaths.java b/src/main/java/ru/otus/mobile/config/ProjectPaths.java index a42b04c..ff21ee7 100644 --- a/src/main/java/ru/otus/mobile/config/ProjectPaths.java +++ b/src/main/java/ru/otus/mobile/config/ProjectPaths.java @@ -4,16 +4,10 @@ import java.nio.file.Path; import java.nio.file.Paths; public final class ProjectPaths { - private final Path projectRoot; private final Path logcatFile; public ProjectPaths() { - this.projectRoot = Paths.get("").toAbsolutePath().normalize(); - this.logcatFile = projectRoot.resolve("logcat.txt"); - } - - public Path projectRoot() { - return projectRoot; + this.logcatFile = Paths.get("").toAbsolutePath().normalize().resolve("logcat.txt"); } public Path logcatFile() { diff --git a/src/main/java/ru/otus/mobile/driver/MobileDriverFactory.java b/src/main/java/ru/otus/mobile/driver/MobileDriverFactory.java index bdb5fa4..d770d9f 100644 --- a/src/main/java/ru/otus/mobile/driver/MobileDriverFactory.java +++ b/src/main/java/ru/otus/mobile/driver/MobileDriverFactory.java @@ -13,7 +13,8 @@ public final class MobileDriverFactory { .setPlatformName("Android") .setAutomationName("UiAutomator2") .setDeviceName(emulator.deviceName()) - .setApp(emulator.appUrl()); + .setApp(emulator.appUrl()) + .setSkipDeviceInitialization(true); try { return new AndroidDriver(URI.create(emulator.appiumUrl()).toURL(), options); } catch (MalformedURLException e) { diff --git a/src/main/java/ru/otus/mobile/guice/CoreModule.java b/src/main/java/ru/otus/mobile/guice/CoreModule.java index 15daf6d..233c422 100644 --- a/src/main/java/ru/otus/mobile/guice/CoreModule.java +++ b/src/main/java/ru/otus/mobile/guice/CoreModule.java @@ -26,8 +26,8 @@ public final class CoreModule extends AbstractModule { String rawEmulators = value( "mobile.emulators", "MOBILE_EMULATORS", - "android-emulator-1|http://localhost:4723|Android Emulator," - + "android-emulator-2|http://localhost:4725|Android Emulator" + "android-emulator-1|http://127.0.0.1:4723|Android Emulator," + + "android-emulator-2|http://127.0.0.1:4725|Android Emulator" ); List emulators = MobileConfig.Emulator.parse(rawEmulators, appUrl); return new MobileConfig(appPackage, appUrl, reservationOwner, emulators); diff --git a/wishlist-349317-5fd795.apk b/wiremock/__files/wishlist.apk similarity index 100% rename from wishlist-349317-5fd795.apk rename to wiremock/__files/wishlist.apk