Framework Structure

Introduction

It is in this part of the series that  you will start actually writing code.  If you are somewhat new to coding in Java, I recommend writing out all the code snippets from scratch rather than copying and pasting them, for some of the following reasons;

  • As you start writing code, you will start picking up on patterns in the code, which will help you pick up things quicker
  • As you write out the code and try to reference certain libraries, IntelliJ will auto-suggest references to import for you, which will also help you learn the code that you are writing 😀

Starting Appium Server Programatically

Because we installed Appium Server globally to our system, it has been added to our PATH and thus, we are able to start and stop the Appium Server programatically in Java using ‘AppiumDriverLocalService’. Let’s set this up now 😀

  1. With the project open in IntelliJ, right-click on the ‘utils.appium’ package and select ‘New’ -> ‘Java Class’
  2. Name the class ‘AppiumServer’ and click OK
  3. Add the following ‘private static AppiumDriverLocalService’ variable in the class and also add two static methods to start and stop the Appium Server respectively, the whole thing should look like below…

utils.drivers package

Editing the ‘AndroidAppDriver’ Class

Appium has come a long way over the years and made it a lot easier to connect to the devices we want to connect to.  Because we will be specifying that we are using AndroidDriver, which is of course specific to the Android platform, we do not need to declare the ‘platformName’ in our Desired Capabilities. If we instead used the more general parent class of AppiumDriver that AndroidDriver inherits from, then it would need the ‘platformName’ defined in the Desired Capabilities to distinguish between iOS and Android.

All we really need to set in our DesiredCapabilities is the ‘deviceName’ (of the Android Virtual Device), the app we want to test and the ‘avd’ name (should be same as deviceName). A full list of Desired Capabilities can be found at http://appium.io/docs/en/writing-running-appium/caps/

In this blog series, we will automate the Reddit Android app.

  1. Download the Reddit APK from https://reddit-official-app.en.uptodown.com/android/download
  2. After downloading the Reddit APK file, put it in a suitable directory (e.g. the /src folder of your local project).

Next let’s setup our AndroidDriver 🙂

  1. Edit the ‘AndroidAppDriver’ class file so it looks similar to below…

In the above, we create a loadAndroidDriver() method which returns AndroidDriver.

At the start of this method, we start up the Appium Server.

We then set the filepath of the APK file to install on the device.

Next, we set the DesiredCapabilities that we want in our Appium session when we load this Android Driver…

  • avd – name of our Android Virtual Device
  • deviceName – name of our device (should match AVD name in this example)
  • app – filepath to the APK file we want to use

We finally set our AndroidDriver to an Appium session with the specified DesiredCapabilities and then return the driver to be used.

(Note that, we could also add a capability for ‘automationName’, where we could for example, specify that we want to use Android Espresso instead of UIAutomator2.  The tests would be written exactly the same for the most part, only difference is Espresso may run a bit faster, and you can also locate elements via Android view tag.)

utils.appium package

Editing the ‘DriverController’ Class

  1. Open up the ‘DriverController’ class in the ‘utils.appium’ package and edit the class file to match below, to create an instance of DriverController which can be used to start the AndroidDriver and device (we have also used Log4J2 here to report any errors when stopping the Driver)…

Editing the ‘Driver’ Class

  1. Open up the ‘Driver’ class in the ‘utils.appium’ package and edit the class file to match below, to create a public AndroidDriver instance which returns an instance of DriverController when initiated, which can then be used in test methods (e.g. finding elements or implicitly waiting etc)…

Editing the ‘Settings’ Class

  1. In our framework, we use the Settings file to store public static variables that we will want to be using in our project.  In this blog series, we will make a simple automated test of opening the Reddit app, going to the main screen and switching from the ‘Popular’ tab to the ‘Home’ tab. This test currently does not require us to store anything in our Settings class, so we can leave it empty for now, it’s just good to know what it would be used for, e.g. storing sensitive data like login credentials (or environment variable names to where the credentials are stored).

utils.hooks package

For now, we will only be adding Before and After hooks for the top-level Cucumber scenarios/feature files.

Editing the ‘CucumberHooks’ Class

  1. Open up the ‘CucumberHooks’ class in the ‘utils.hooks’ package and edit the class file to match below.  This class file will contain all our Before and After hooks for our Cucumber scenarios, which respectively get executed before or after a Cucumber feature and/or scenario. Please see https://github.com/cucumber/cucumber/wiki/Hooks for more information…

pages package

Editing the ‘BasePage’ Class

  1. Open up the ‘BasePage’ class in the ‘pages’ package and edit the class file to match below.  The ‘BasePage’ class is where we will later put all our test methods for our BaseScenarios, and any methods that exist / can be shared across more than one specific page object (e.g. main navigation, validate a passed pageUrl etc)…
    (Also note we threw in a Log4J Logger for the class so we can use methods like log.debug() instead of using System.out.println() to print out information) 🙂

Editing the ‘Page’ Class

If you’ve followed my Java Web Selenium or Appium Mobile Web blog series, you will notice that in our ‘Page’ class, we created a generic method called instanceOf() that takes the same generic class and initialises a new object from PageFactory with all the correct elements for the page initialised. It looked similar to below…

This works perfectly fine if we only use Selenium’s @FindBy annotations and WebElement class.  However, when we want to use annotations or element classes specific to mobile / Appium, it is required to decorate our driver instance with an AppiumFieldDecorator. If we look at Selenium’s PageFactory class, we can see that it contains the following methods…

If we examine the above PageFactory class carefully, we can see that unfortunately, there is no initElements() method which takes both a FieldDecorator and a generic class.

This is slightly problematic as we want to implement PageFactory using our instanceOf() method which takes a generic class that we pass into it as our page class, as well as be able to use the required AppiumFieldDecorator, instead of implementing PageFactory the traditional way where we initElements() in the constructor of each page class (in this case we would just pass in the page class and wouldn’t need a generic).

To solve this without resorting to the traditional implementation of PageFactory and be able to use both a generic class with AppiumFieldDecorator, we can create our own initElements() method which first instantiates the generic class as a page class and then passes that in to the AppiumFieldDecorator overloaded initElements() method. Note that we’ve had to copy the instantiatePage() method from Selenium’s PageFactory class, due to the fact they made it private 🙁

  1. Open up the ‘Page’ class in the ‘pages’ package and edit the class file to match below…

Step Definitions

Editing the ‘BaseSteps’ Class

  1. Open up the ‘BaseSteps’ class in the ‘steps’ package and edit it to match as below. This is just a placeholder step definition that inherits from ‘Page’ class, to help follow POM concepts…

Editing all the other Step Definitions

  1. Open up every other Step Definitions class file and change the classes so that they all inherit/extend from the ‘BaseSteps’ class (and thus in turn, inherit from the ‘Page’ class). Please find one example below…

TestNG

With TestNG, a TestRunner class can be made which defines everything that you want to happen to that group of tests. To work with Cucumber, you must define the directory where your Feature files are located in the project, as well as define where the glue is that connects your Feature files to your java test methods (both hooks and step definitions). The TestRunner class can also define what happens before, after and during the tests are run.

Creating the ‘TestRunner’ Class

  1. Right-click the src/test/java directory, select ‘New’ –> ‘Java Class’ and name the class “TestRunner”
  2. Add the following lines of code in to the ‘TestRunner’ class (also note the @CucumberOptions, where the location of the feature files, glue files and any formatter plugins are defined)…

    Interestingly, you can actually make as many TestRunner classes as you see fit for your project and then define which TestRunner classes, if any, get run within a test suite.  You define all of this within a file called ‘testng.xml’.

Creating the ‘testng.xml’ File

  1. Right-click the project root in the Project Explorer, select ‘New’ –> ‘File’ and create a file called ‘testng.xml’ and press the ‘OK’ button
  2. Open up the ‘testng.xml’ file and add the following lines of XML into the file (using similar/appropriate names for your own test suite and test name)…

Building the Project

Building the Maven project

  1. Open CMD-prompt / Terminal and cd (change directory) into the root of your Maven project. For example…
    cd /Users/username/DevProjects/ProductAutomationFramework
  2. Enter the following command to build your Maven project…
    mvn clean install -DskipTests  (we currently have no tests. If we don’t put in the skipTests command, we will get a build error around one of the cucumber report plugins we are using).

Building the Gradle project

If you have setup the project with Gradle instead of Maven, you can follow the steps below to build…

  1. Open CMD-prompt / Terminal and cd (change directory) into the root of your Maven project. For example…
    cd /Users/username/DevProjects/ProductAutomationFramework
  2. Enter the following command to build your Maven project…
    ./gradlew build

Our core framework is now setup, in the next part, we will begin adding our test scenario 😀

Digiprove sealCopyright secured by Digiprove © 2018
Liked it? Take a second to support Thomas on Patreon!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.