logo logo

Easily Create Mobile Testing Framework with Java & Appium

Easily Create Mobile Testing Framework with Java & Appium

In this tutorial, you will learn how to create a mobile testing framework in Java and Appium. I’ll take you through all the steps needed to set up your environment, record a mobile test using TestProject’s AI-powered Test Recorder, and by the end, write your mobile test with the open source TestProject Java SDK.

Table of Contents – Mobile Testing Framework with Java and Appium

  1. What is Appium?
  2. Why TestProject
  3. First steps
  4. Emulate a mobile device
  5. Environment setup
  6. Create a recorded mobile test with TestProject
    1. Record the mobile test
    2. Run the mobile test
    3. View the test report
    4. Generate the code
  7. Create your mobile test framework
    1. Create a new project
    2. Create the test class
    3. Create the first mobile test
    4. View the mobile automated test report
  8. Optimize the code
    1. Page Object Model
  9. Conclusion

What is Appium?

Appium is the most popular automated testing framework for mobile apps. What makes it so great is that it’s open-source, it can automate native, hybrid and mobile web applications, and it works on iOS, Android, and Windows applications, all using the well-known WebDriver protocol.

Why TestProject?

TestProject allows you to record your web or mobile tests, or implement them in Java, C# or Python using the advanced scripting capabilities of the TestProject SDK. The SDK handles all the drivers, so you don’t have to worry about keeping them up to date.

In the end, you’ll get the added benefit of beautiful and comprehensive test reports, available in HTML or PDF formats. Oh, and did I mention that it’s free forever? šŸ˜Š

First steps

First things first – whether you want to use the record and playback feature, or you want to write a coded test, you need a TestProject account. If you don’t already have one, go ahead and sign up.Ā Next, download and register the TestProject agent. And lastly, start the agent.

For demo purposes, let’s make a simple test using the TestProject example APK, where we enter a username and password and verify that the correct page is loaded after the login:

Mobile Login Test

Emulate a mobile device

Just like we need to have the browser installed for our Selenium tests, we need a mobile device to test on. If you want to use your actual physical mobile device, go ahead and skip to the next steps, and simply connect it via a USB cable to your machine (For iOS devices, you can follow the steps here to get started).

Otherwise, for Android devices, download and install Android Studio, open AVD Manager, choose the device model you want to emulate, and start it.

A bonus from TestProject is that you don’t need to run your Appium server, TestProject will do the job for you.

Environment setup

This part is optional if you plan on stopping at the record and playback phase. If you want to generate the code or write the code by yourself, you need to have the environment set up for Java development. The first thing you need to do is to download and install Java JDK, then set it as an environment variable. You also need a TestProject Developer token, that you can obtain from the Integrations tab (the auto-generated code will contain this key, but if you want to write your code, you will need to get it yourself):

TestProject Developer Token

Last, but not least, you need to have an IDE installed and you’re good to go.

Create a recorded mobile test with TestProject

This part requires no coding skills, but it can be helpful if you want to understand how the code for the automated tests should look like.

Record the mobile test

Go back to the TestProject dashboard, click on New Test and select Mobile test type, then enter a name and an optional description, and select if you want to run your test on Android or iOS. Click inside theĀ Mobile applicationsĀ field to expand the menu, and select Add new application for testing.Ā You should find your device listed in the drop-down, and after you select, the list of all apps installed on it:

Select device and application

If you haven’t installed the APK on the device yet, you can also upload it, or add the app manually. On the last step of the wizard, select RecordĀ and go on with running the test steps manually.

On the first step, you’ll be asked to reset the application, so possible cached data won’t affect your tests. I recommend to confirm this before continuing (this will create an extra step in the automated test):

Reset Application Confirmation

Now you should see the device in your browser, with the demo application started. Follow the steps as you normally would – enter a username, the valid password (“12345” – without the quotes), and click the Login button.

Now that you are redirected to the user’s page, add a validation step. Let’s say our validation is to confirm that the Logout button is displayed. This will not be an action performed inside the demo app, but it’s something we’ll do from TestProject.

Click on the plus button to add a new step, highlight the Logout button by hovering, click on the multiple dots icon to expand the menu, then select Validation -> Is Visible.

Validate Logout button is visible

If you now press the Play button, you will see the test is played back automatically, and decide if you want to make any adjustments to the steps. Once you’re happy with your test, press Save & Exit.

Run the mobile test

After you save the test, you’ll be redirected to the Projects page. Here, you should see the test you created. Click the Run button, select the TestProject agent and the device, and let TestProject do its thing. You’ll see a blinking dot next to the Monitor tab, which means that a job is currently run.

View the test report

Once the test is finished running, you can navigate to the ReportsĀ tab. Here, you’ll be able to see the result of your test. For the moment, we only ran one test, but you should see valuable information on the Trends page, such as the ratio between passed and failed tests, number of executions in time, and distribution per agent and per device.

TestProject Trends Report

You can also click on the test run and see a detailed report for this specific run, including step statistics or execution time, and you can download a summary or a full PDF report with all this information. Or you can go back to the test for further adjustments, which is what we’re going to do now for the next step.

Generate the code

You can take things one step forward and generate the code of your test. For the moment, Java and C# languages are available. From the Projects view, click on the three dots menu and select Generated code.

Generate the test's code

Select Java for this tutorial, and a download will start in your browser shortly. Unzip the downloaded file, open it in your IDE of choice, build it and run the tests. You should get a brand new test report for this test run.

And we can use this generated code as a starting point for our coded tests. Let’s get started šŸ˜Š

Create your mobile test framework

Create a new project

Create a new Gradle project – I use IntelliJ for my Java projects, but feel free to use any IDE you prefer, and add the TestProject SDK to build.gradle file. This is the beauty of using the TestProject SDK – you don’t need to use many dependencies, and you don’t need to worry about keeping your drivers up to date, it will keep them up to date for you. You can find the dependencies you need to add from the Integrations tab -> SDK -> Install the SDK:

Install the TestProject SDK

You’ll be redirected to the Maven Central Repository Search, where you can select the SDK version you want to use, then add it to your project:

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'
    implementation 'io.testproject:java-sdk:0.64.4-RELEASE'
}

Create the test class

Create a new class in the src\test\java folder, that implements the ExceptionsReporter interface. Your class should look like this:

import io.testproject.sdk.drivers.ReportingDriver;
import io.testproject.sdk.interfaces.junit5.ExceptionsReporter;

public class AndroidTest implements ExceptionsReporter {
    
    @Override
    public ReportingDriver getDriver() {
        return null;
    }
}

Then, you need to instantiate the driver and actually return it in the above method. First, define the capabilities:

private static AndroidDriver<? extends MobileElement> driver;

private static DesiredCapabilities getCapabilities() {
        DesiredCapabilities capabilities = new DesiredCapabilities();
        capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, MobilePlatform.ANDROID);
        capabilities.setCapability(MobileCapabilityType.UDID, "emulator-5554");
        capabilities.setCapability(CapabilityType.BROWSER_NAME, "");
        capabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, "io.testproject.demo");
        capabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, "io.testproject.demo.MainActivity");
        return capabilities;
}

IntelliJ will automatically import all the libraries needed for this. My test is done on Android and the UDID is the ID of the emulated device. Please change these capabilities to reflect your mobile device.

You’ll want the driver to start before each test, so you can use the JUnit @BeforeEach annotation – this means you’ll have a method running before all the tests start.

@BeforeEach
static void setup() throws Exception {
    driver = new AndroidDriver<>(System.getenv("TP_DEV_TOKEN"), getCapabilities(), "Android Test");
}

The parameters for the AndroidDriver are: the developer token you obtained in the Environment setup part of the tutorial (you can add the string directly in your test, I saved it as an environment variable and get it from there), the capabilities defined earlier, and the project name.

You will also need to add the JUnit API dependency to your gradle.build:

testImplementation  'org.junit.jupiter:junit-jupiter-api:5.6.0'

And now you can make the getDriver() method return your driver:

@Override
public ReportingDriver getDriver() {
    return driver;
}

Create the first mobile test

On to the actual test. We’ll use the @Test JUnit annotation. First set up a wait in the test method, so you don’t get NoSuchElementExceptions if your test tries to find the elements before the page was loaded. You can see this part of the code in the previously generated test as well.

@Test
public void loginTest() {
   driver.manage().timeouts().implicitlyWait(15000, TimeUnit.MILLISECONDS);
   driver.resetApp();
}

Next, let’s interact with the elements. We need to identify the best locators for our elements. Normally for mobile apps, you can use Appium Inspector, but here’s another cool feature from TestProject: it helps you inspect your element from the browser app. As long as your Agent is connected, you can access your phone or emulated device in the TestProject app, and inspect the elements from there:

Element Locator in TestProject

You’ll even see the id marked as unique, so you know that this locator won’t cause any problems.

Now that you know how to locate the element, you can interact with it. What we need for the mobile test is to tap and insert a text inside the name field:

new TouchAction(driver).tap(TapOptions.tapOptions().withElement(ElementOption.element((driver).
           findElement(By.id("io.testproject.demo:id/name"))))).perform();
driver.findElementById("io.testproject.demo:id/name").sendKeys("Andreea");

Do the same for the password, and for the Login button just tap it:

new TouchAction(driver).tap(TapOptions.tapOptions().withElement(ElementOption.element((driver).
           findElement(By.id("io.testproject.demo:id/password"))))).perform();
driver.findElementById("io.testproject.demo:id/password").sendKeys("12345");
new TouchAction(driver).tap(TapOptions.tapOptions().withElement(ElementOption.element((driver).
           findElement(By.id("io.testproject.demo:id/login"))))).perform();

The last step is verifying if the test actually did what it was supposed to do, i.e. log in. We can check that the Logout button is displayed, like before, or we can verify that the correct name is displayed:

Last Step Validation

We can use the Assert class from JUnit:

Assert.assertTrue(driver.findElement(By.id("io.testproject.demo:id/greetings")).getText().contains("Andreea"));

Let’s also create a method to close the app at the end of the test. The JUnit property we need to use for this @AfterEach:

@AfterEach
public void tearDown() {
   if (driver != null) {
       driver.quit();
   }
}

That’s pretty much it. Build the project and run the test.

View the mobile automated test report

Now go back to the TestProject app and check theĀ ReportsĀ tab. You should see the new project here, and a report with the test you just ran. If all went well, you should see a passing rate of 100%:

TestProject HTML Trends Report

On the right-hand side, you can expand each step and see the details. You can also download a summary or a full report of your test run in PDF format.

Optimize the code

Now we have the test running, but we can definitely improve the code. We only created one test, but if we extend our test framework to include more tests, we don’t want to duplicate the code that starts the application in each of our tests. So let’s move that to the setup() method:

@BeforeEach
public void setup() throws Exception {
    driver = new AndroidDriver<>(System.getenv("TP_DEV_TOKEN"), getCapabilities(), "Android Test");
    driver.manage().timeouts().implicitlyWait(15000, TimeUnit.MILLISECONDS);
    driver.resetApp();
}

Let’s also remove the code duplication we use to locate the elements. To do that, declare a variable for each element and then use it as a parameter. The test method should look like this now:

@Test
public void loginTest() {
   MobileElement user = driver.findElement(By.id("io.testproject.demo:id/name"));
   MobileElement password = driver.findElement(By.id("io.testproject.demo:id/password"));
   MobileElement loginButton = driver.findElement(By.id("io.testproject.demo:id/login"));

   new TouchAction(driver).tap(TapOptions.tapOptions().withElement(ElementOption.element(user))).perform();
   user.sendKeys("Andreea");
   new TouchAction(driver).tap(TapOptions.tapOptions().withElement(ElementOption.element(password))).perform();
   password.sendKeys("12345");
   new TouchAction(driver).tap(TapOptions.tapOptions().withElement(ElementOption.element(loginButton))).perform();

   MobileElement greeting = driver.findElement(By.id("io.testproject.demo:id/greetings"));
   Assert.assertTrue(greeting.getText().contains("Andreea"));
}

Page Object Model

But what if we will need to interact with these elements in other methods, or even classes? This is where the Page Object Model design pattern comes in handy. In short, in Page Object Model, or simply POM, we keep our tests method separate from element locators. This way, we can reuse code without duplicating it.

To get started, create a new class for the Login page. The page should have a constructor with the driver as a parameter. Then, get all the elements previously identified in the test class. This is what you should have so far:

package Pages;

import io.appium.java_client.MobileElement;
import io.appium.java_client.TouchAction;
import io.appium.java_client.touch.TapOptions;
import io.appium.java_client.touch.offset.ElementOption;
import io.testproject.sdk.drivers.android.AndroidDriver;
import org.openqa.selenium.By;

public class LoginPage {

    AndroidDriver driver;

    public LoginPage(AndroidDriver driver) {
        this.driver = driver;
    }

    private MobileElement getName() {
        return (MobileElement) driver.findElement(By.id("io.testproject.demo:id/name"));
    }
    private MobileElement getPassword() {
        return (MobileElement) driver.findElement(By.id("io.testproject.demo:id/password"));
    }
    private MobileElement getLoginButton() {
        return (MobileElement) driver.findElement(By.id("io.testproject.demo:id/login"));
    }
    private MobileElement getGreeting() {
        return (MobileElement) driver.findElement(By.id("io.testproject.demo:id/greetings"));
    }
}

Next, create the methods you’ll call from the tests. It’s unlikely that some of the tests will only fill in the username, or only the password, so create a login() method with all these actions:

public void login(String user, String pass) {
    new TouchAction(driver).tap(TapOptions.tapOptions().withElement(ElementOption.element(getName()))).perform();
    getName().sendKeys(user);
    new TouchAction(driver).tap(TapOptions.tapOptions().withElement(ElementOption.element(getPassword()))).perform();
    getPassword().sendKeys(pass);
    new TouchAction(driver).tap(TapOptions.tapOptions().withElement(ElementOption.element(getLoginButton()))).perform();
}

For the verification step, create a method which returns a boolean value, which we can reuse in other tests:

public boolean isLoginSuccessful(String name) {
    return getGreeting().getText().contains(name);
}

Now go back to the test class, and call the methods inside the test:

private LoginPage loginPage;
//the rest of your code here
...
@Test
public void loginTest() {
    var user = "Andreea";
    var pass = "12345";
    loginPage = new LoginPage(driver);
    loginPage.login(user, pass);
    Assert.assertTrue(loginPage.isLoginSuccessful(user));
}

Looks much cleaner, doesn’t it? Plus, you can reuse the methods in different tests, and create new page object classes and methods to extend the framework.

If you rerun the test now, and go to the TestProject app, in theĀ ReportsĀ tab, you will see 2 executions:

TestProject Distribution Report

You can clone the full final project from GitHub, the master branch contains the unoptimized version, and the POM version of the project is in the optimized branch.

Conclusion

I hope you’re now convinced that creating a mobile testing framework in Java is very easy using TestProject. You can use all existing Selenium and Appium methods, with less setup, and in the end, beyond easy to maintain tests, you also get great reports with almost no extra code āœØ

Avatar

About the author

Andreea Draniceanu

Andreea is a QA engineer, experienced in web and desktop applications, and always looking to improve her automation skills and knowledge. Her current focus is automation testing with C#.

Join TestProject Community

Get full access to the world's first cloud-based, open source friendly testing community. Enjoy TestProject's end-to-end test automation Platform, Forum, Blog and Docs - All for FREE.

Join Us Now  

Comments

10 1 comment
  • Avatar
    Zakiev Evgenii April 6, 2021, 11:16 pm

    Awesome article.
    I wonder if TestProject allows platform agnostic setup alternative to this regular AppiumDriver setup for parallel execution locally. Note that this code intentionally avoids mentioning IOSDriver or Android Driver. I tried generic driver but it does not seem to serve this purpose, or it least it new AndroidDriver() can not be instantiated to GenericDriver:

    public class BaseTest {
    protected static ThreadLocal driver = new ThreadLocal();

    public AppiumDriver getDriver() {
    return driver.get();
    }

    public void setDriver(AppiumDriver driver2) {
    driver.set(driver2);
    }

    public BaseTest() {
    PageFactory.initElements(new AppiumFieldDecorator(getDriver(), Duration.ofSeconds(15)), this);
    }

    public void waitForVisibility(MobileElement e, String message, long seconds) {
    utils.log().info(“Wait for Visibility of ” + message);
    WebDriverWait wait = new WebDriverWait(getDriver(), seconds);
    wait.until(ExpectedConditions.visibilityOf(e));
    }

    public void closeApp() {
    ((InteractsWithApps) getDriver()).closeApp();
    }

    public void launchApp() {
    ((InteractsWithApps) getDriver()).launchApp();
    }
    }

Leave a Reply

popup image

Unlock the power of Appium testing

Forget complex setups & maintenance hassles. Focus on creating reliable Android or iOS tests using a FREE Appium-powered platform, with built-in reports, designed to help deliver quality at speed.
Get Started
FacebookLinkedInTwitterEmail