{"id":16536982,"url":"https://github.com/eliasnogueira/appium-cross-platform-example","last_synced_at":"2025-07-28T16:10:27.585Z","repository":{"id":147152709,"uuid":"84103436","full_name":"eliasnogueira/appium-cross-platform-example","owner":"eliasnogueira","description":"Appium Cross Platform example using Java","archived":false,"fork":false,"pushed_at":"2025-01-14T16:21:38.000Z","size":54264,"stargazers_count":8,"open_issues_count":0,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-08T20:06:53.842Z","etag":null,"topics":["android-testing","appium","appium-android","appium-ios","ios-testing","java","mobile-testing"],"latest_commit_sha":null,"homepage":"http://eliasnogueira.com","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eliasnogueira.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-03-06T17:51:19.000Z","updated_at":"2025-01-14T16:21:42.000Z","dependencies_parsed_at":"2025-02-13T12:44:31.771Z","dependency_job_id":null,"html_url":"https://github.com/eliasnogueira/appium-cross-platform-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/eliasnogueira/appium-cross-platform-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliasnogueira%2Fappium-cross-platform-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliasnogueira%2Fappium-cross-platform-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliasnogueira%2Fappium-cross-platform-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliasnogueira%2Fappium-cross-platform-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eliasnogueira","download_url":"https://codeload.github.com/eliasnogueira/appium-cross-platform-example/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eliasnogueira%2Fappium-cross-platform-example/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267544439,"owners_count":24104762,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-28T02:00:09.689Z","response_time":68,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android-testing","appium","appium-android","appium-ios","ios-testing","java","mobile-testing"],"created_at":"2024-10-11T18:34:19.098Z","updated_at":"2025-07-28T16:10:27.575Z","avatar_url":"https://github.com/eliasnogueira.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Appium Cross-Platform (Android, iOS) Example\n\nDon't forget to give this project a ⭐\n\nThis project delivers a basic, but concise, example of a cross-platform test creation and execution using Appium and\nJava by applying simple design patterns and best Appium features.\n\n## Example\n\n![Test execution](assets/test-execution.gif)\n\n## Preconditions\n\n* Java 23 (you might downgrade it without any problem)\n* Install [Appium](https://appium.io/docs/en/latest/quickstart/install/)\n* Install the [UiAutomator2 Driver](https://appium.io/docs/en/latest/quickstart/uiauto2-driver/)\n    * Make sure you update it by running\n      ```shell\n      appium driver update uiautomator2\n      ```\n* Install the [XCUITest Driver](https://appium.github.io/appium-xcuitest-driver/latest/installation/)\n    * Make sure you update it by running\n        ```shell\n        appium driver update xcuitest\n        ```\n\n## About the apps\n\nThe app used in this project is from the WebDriverIO native demo app: https://github.com/webdriverio/native-demo-app\n\n## How to run\n\n### Precondition\n\nAs a precondition you must run Appium in your local machine\n\n```shell\nappium\n```\n\n### Properties file\n\nIn the `config.properties` you will manage important data as:\n\n* `platform`: indicates the platform the tests will run\n* `appium.ip`: the IP address Appium is expected to run\n* `appium.port`: the port address Appium is expected to run\n* `device.ios.name`: the iPhone Simulator expected to run where you must have it created\n* `device.android.name`: the Android Emulator expected to run where you must have it created\n* `platform.ios.version`: the iOS version expected to run in the iPhone Simulator where you must have it created\n* `platform.android.version`: the Android version expected to run in the Android Emulator where you must have it created\n* `app.ios.path`: the path to the Android app (`.apk` file)\n* `app.android.path`: the path to the iOS app (`.zip` or `.ipa` file)\n\n**NOTE**\n\n*The apps are located in the app folder and there is a concatenation in the `DriverFactory` to get its full path using\n`System.getProperty(\"user.dir\")`\n\n### Running it\n\nChange the `config.properties` file within the correct data you want to run based on the platform.\n\n#### Example 1: Android\n\nif you want to run the tests in the Android platform using _Android 13 (Tiramisu)_ in a existing emulator called\n_TiramisuTest_, you might end up with the following information in the `config.properties` file\n\n```properties\n# all others not changed properties removed\nplatform=android\ndevice.android.name=TiramisuTest\nplatform.android.version=13\n```\n\n#### Example 2: iOS\n\nif you want to run the tests in the iOS platform using _iOS 18.2_ in an existing emulator called\n_iPhone Simulator_, you might end up with the following information in the `config.properties` file\n\n```properties\n# all others not changed properties removed\nplatform=ios\ndevice.android.name=iPhone Simulator\nplatform.android.version=18.2\n```\n\n## Code Explanation\n\n### Configuration\n\nThe configuration, mostly based on the platform, is done by a property file located in the\n`src/test/resources/config.properties`.\n\nThe code uses the value from each property through two classes. The `ConfigurationManager` is responsible to load the\nconfiguration imitating a Singleton pattern using the `ConfigCache.getOrCreate()` method from\nthe [Owner library](https://matteobaccan.github.io/owner/).\n\nThe `Configuration` class is the one responsible to match each property in the `config.properties` file, enabling a\nfluent way to get its data by associating the property name withing an attribute in the class by using the `@Key`\nannotation\n\n```java\n// this will return the value from the device.android.name property\nclass ConfigExample {\n\n    interface Configuration {\n        @Key(\"device.android.name\")\n        String androidDeviceName();\n    }\n\n    class Usage {\n        void main() {\n            ConfigurationManager.configuration().androidDeviceName();\n        }\n    }\n}\n```\n\nPlease, note that the above code is an example that won't work by copy-past. It's just an education example. You must\nrely on in the already created code.\n\n### Driver Management\n\nThe basic driver information is done by the `DriverFactory` enumeration by setting all the necessary configurations to\nrun the tests using either Android or iOS.\n\nNote that you can use any approach: `if-else`, `switch-case` or any that might work... This is a more elegant way to\nimplement the Factory pattern to execute the tests in the target platform.\n\nYou noticed that each enum will return an instance of the `AppiumDriver` for the specific platform. To use it in your\ntest you must use the `valueOf()` method from the enumeration and call the method associated with its creation which is\n`createDriver()`\n\n```java\nclass DriverExample {\n    // gets the platform property value, set's it to upper case to match with the existing enums and call the createDriver()\n    AppiumDriver driver = DriverFactory.valueOf(configuration().platform().toUpperCase()).createDriver();\n}\n```\n\n### Page Objects\n\nThe `screens` package contains the Page Objects where two important things happens there.\n\nFirst, the different annotation to locate the elements for Android or iOS which are `@AndroidFindBy` and\n`iOSXCUITFindBy`, respectively. This will ensure you can use the same methods in the page object without duplications\nwhere the only possible subject of change is the locator, where we annotate to have the correct one based on the target\nplatform.\n\n```java\nclass PageObjectExample {\n    @AndroidFindBy(id = \"android:id/button1\")\n    @iOSXCUITFindBy(accessibility = \"OK\")\n    WebElement alertButton;\n}\n```\n\nSecond, the constructor which will initialize the elements (based on the target platform). This is necessary to make\nsure the element is instantiated using the correct locator value per platform. The code is simple\n\n```java\nclass PageObjectExample {\n    public PageObjectExample(AppiumDriver driver) {\n        PageFactory.initElements(new AppiumFieldDecorator(driver), this);\n    }\n}\n```\n\n### The test\n\nThe project shows two different examples: one per platform and one cross-platform\n\n#### Per platform\n\nLocated in the `basic` package at the `src/test/java` it shows one test per platform by not using the `DriverFactory`.\n\nNote that both the `AndroidTest` and `IOSTest` does the same thing, but the different (apart from the ugly code), is in\nthe locators. In a cross-platform test you would end up with a lot of code duplication, even test duplications.\n\n#### Cross-Platform\n\nTo solve the above-mentioned problem we can make the usage of the Page Object approach using the specific locator\nannotations to have a simple source of truth when interacting with the app. Note that this wouldn't be possible without\nusing Page Objects.\n\nThe combination of the `DriverFactory` with the created Page Objects will end up with a reliable ans elegant code that\nwill run in both platforms.\n\n```java\nclass CombinedTest {\n\n    private static AppiumDriver driver;\n\n    @BeforeAll\n    static void setUp() throws Exception {\n        driver = DriverFactory.valueOf(configuration().platform().toUpperCase()).createDriver();\n    }\n\n    @AfterAll\n    static void tearDown() {\n        driver.quit();\n    }\n\n    @Test\n    void testCalculateDefaultTip() {\n        MainScreen mainScreen = new MainScreen(driver);\n        mainScreen.tagOnLogin();\n\n        LoginScreen loginScreen = new LoginScreen(driver);\n        loginScreen.login(\"elias@elias.com\", \"12w3e4r5t\");\n\n        assertEquals(\"You are logged in!\", loginScreen.retrieveAlertMessage());\n\n        loginScreen.tapOnOK();\n    }\n}\n```\n\nThe value of the `platform` property will determine the platform where the test will be executed. In case of Android,\nthe page objects will use the locators set by the `@AndroidFindBy`, where in iOS it will use the `@iOSXCUITFindBy`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feliasnogueira%2Fappium-cross-platform-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feliasnogueira%2Fappium-cross-platform-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feliasnogueira%2Fappium-cross-platform-example/lists"}