Table of Contents
Creating your First Proper Test
Introduction
In this section, we will create our first proper test scenario. In this test scenario we will launch the Reddit app, tap ‘Skip for Now’ and then select the ‘Home’ tab on the main screen.
We will then assert that we are on the Home tab.
Let’s get started 🙂
Creating the Cucumber Scenario
When creating our BDD scenarios, we want to keep them simple and write them in a declarative style, sticking simply to the business logic, rather than writing them in an imperative style. A great article on this can be found at http://itsadeliverything.com/declarative-vs-imperative-gherkin-scenarios-for-cucumber.
- Right-click on the src/test/resources/features directory, select ‘New’ –> ‘File’ and create a file called something like ‘NavigationScenarios.feature’
- Open up the ‘NavigationScenarios.feature’ file and add the following test scenario. We can also add a Cucumber tag again (e.g ‘@Nexus5xOreo’) either above the ‘Scenario’ (to apply to the specific scenario) or above the ‘Feature’ (to apply to all scenarios within the feature)…
@Nexus5xOreo Feature: Navigation Scenarios As a user of Reddit, I can navigate around the Reddit app Scenario: 01. View the 'Home' tab whilst logged out Given the welcome screen has been skipped without logging in When the Reddit user views the Home tab Then they see new posts and information pertaining to the Home tab
We can verify that the text we are asserting on the ‘Home’ tab screen is correct by observing the screen in the Android Virtual Device and then inspecting it via UIAutomatorViewer. Let’s do that now 🙂
- Open up a Terminal (or Command Prompt) and change directory (cd) into $ANDROID_HOME/platform-tools
cd $ANDROID_HOME/platform-tools
(on Windows it will be %ANDROID_HOME% instead of $ANDROID_HOME)
- Launch the Android Virtual Device…
emulator -avd Nexus5xOreo
- Launch the Reddit app on the device (drag and drop the APK again if necessary) and navigate your way through the app to the ‘Home’ tab. It should look like below…
Now let’s launch UiAutomatorViewer so we can inspect the elements of this screen. - Open up a new Terminal (or Command Prompt) window and change directory (cd) into the $ANDROID_HOME/tools/bin directory…
cd $ANDROID_HOME/tools/bin
(on Windows it will be %ANDROID_HOME% instead of $ANDROID_HOME)
- Type the following command to launch the UIAutomatorViewer…
./uiautomatorviewer
We can now inspect the text elements and see it matches what we put in our test scenario :)Next, let’s verify what the current activity is…
- Open up another Terminal (or Command Prompt) window again and enter the following command to start an ADB shell…
adb shell
- Now in the ADB shell, enter the following command to find out the current activity in the foreground of the Android device…
dumpsys window windows | grep -E 'mCurrentFocus'
- You should see text output like below…
{8da933 u0 com.reddit.frontpage/com.reddit.frontpage.MainActivity}
We can see here that the current activity is therefore “.MainActivity”
- You should see text output like below…
If you’ve been following all the blog posts in this series, you will notice that we have reused an existing step in this scenario, such as;
- Given the Reddit app has launched
The other undefined step definitions will be highlighted as being undefined still.
Creating the Undefined Step Definitions
- Highlight some of the ‘And the welcome screen has been skipped without logging in”‘ step in IntelliJ until a yellow light bulb icon appears next to it
- Click the yellow light bulb icon and select ‘Create step definition’ –> ‘BaseSteps (steps)’
- An undefined step definition should be automatically added to ‘BaseSteps’ like below…
- If ‘BaseSteps (steps)’ is not listed, choose one that is listed then cut and paste the step definition in to the ‘BaseSteps’ class. It should look similar to below…
package steps; import cucumber.api.PendingException; import io.cucumber.java.en.Given; public class BaseSteps extends Page { @Given("^the welcome screen has been skipped without logging in$") public void theWelcomeScreenHasBeenSkippedWithoutLoggingIn() throws Throwable { // Write code here that turns the phrase above into concrete actions throw new PendingException(); } }
- If ‘BaseSteps (steps)’ is not listed, choose one that is listed then cut and paste the step definition in to the ‘BaseSteps’ class. It should look similar to below…
- An undefined step definition should be automatically added to ‘BaseSteps’ like below…
- Highlight some of the ‘When the Reddit user views the Home tab’ step in IntelliJ until a yellow light bulb icon appears next to it
- Click the yellow light bulb icon and select ‘Create step definition’ –> ‘NavigationSteps (steps)’
- An undefined step definition should be automatically added to ‘NavigationSteps’ like below…
- If ‘NavigationSteps (steps)’ is not listed, choose one that is listed then cut and paste the step definition in to the ‘NavigationSteps’ class. It should look similar to below…
package steps; import cucumber.api.PendingException; import io.cucumber.java.en.When; public class NavigationSteps extends Page { @When("^the Reddit user views the Home tab$") public void iSelectTheHomeTab() throws Throwable { // Write code here that turns the phrase above into concrete actions throw new PendingException(); } }
- If ‘NavigationSteps (steps)’ is not listed, choose one that is listed then cut and paste the step definition in to the ‘NavigationSteps’ class. It should look similar to below…
- An undefined step definition should be automatically added to ‘NavigationSteps’ like below…
- Highlight some of the ‘Then they see posts and information pertaining to the Home tab’ step in IntelliJ until a yellow light bulb icon appears next to it
- Click the yellow light bulb icon and select ‘Create step definition’ –> ‘HomeSteps (steps)’
- An undefined step definition should be automatically added to ‘HomeSteps’ like below…
- If ‘HomeSteps (steps)’ is not listed, choose one that is listed then cut and paste the step definition in to the ‘HomeSteps’ class. It should look similar to below…
package steps; import cucumber.api.PendingException; import io.cucumber.java.en.Then; import pages.BasePage; import java.util.List; public class HomeSteps extends Page { @Then("^I see posts and information pertaining to the Home tab$") public void iSeePostsAndInformationPertainingToTheHomeTab() throws Throwable { // Write code here that turns the phrase above into concrete actions throw new PendingException(); } }
- If ‘HomeSteps (steps)’ is not listed, choose one that is listed then cut and paste the step definition in to the ‘HomeSteps’ class. It should look similar to below…
- An undefined step definition should be automatically added to ‘HomeSteps’ like below…
Creating the Test Methods
skipWelcomeScreen() Method
We will now create test methods in Java for each of our undefined steps.
Because we are taken to a new ‘Popular’ tab screen when we skip past the welcome screen, we should create a new page class for the ‘Popular’ tab screen.
- Right-click on the ‘pages’ package, select ‘New’ –> ‘Java Class’ and create a class file called ‘PopularTabPage’
- Open up the ‘PopularTabPage’ class and edit it so it looks like below (we want it to inherit from BasePage, as that is where we navigate to the page from)…
package pages; public class PopularTabPage extends BasePage { }
We now need to add an AndroidElement on the BasePage for the ‘Skip for Now’ button. Let’s use the Android Virtual Device and UIAutomatorViewer again, to grab the details this element
- Uninstall the Reddit app from the Android Virtual Device and then drag and drop the Reddit APK file back on to the device to reinstall it (easiest way to access the welcome screen of the app again)
- Launch the app and wait for it load to the welcome screen
- Once the welcome screen is displayed, open up UIAutomatorViewer and take a snapshot of the screen to get the XML hierarchy of it
- Once the screenshot has loaded in the UIAutomatorViewer, click on the ‘Skip for Now’ element to inspect the details of it. It should look like below…
If we look at the details of the element, we can see that it has a unique ‘resource-id’ of ‘com.reddit.frontpage:id/skip_text’. We can use this to find the AndroidElement by ID 🙂 - Open up the ‘BasePage’ class in the ‘pages’ package again and add the following AndroidElement in the top of the class…
@AndroidFindBy(id = "com.reddit.frontpage:id/skip_text") private AndroidElement btnSkipForNow;
We will use this element in our test method for skipping the welcome screen (whilst logged out).
- Add the following method in the ‘BasePage’ class for skipping the welcome screen and being taken to the new PopularTabPage…
public PopularTabPage skipWelcomeScreen() { btnSkipForNow.click(); log.info(":: We click the 'Skip For Now' button on the Welcome screen"); return instanceOf(PopularTabPage.class); }
In the above method, we click on the ‘Skip for Now’ button and then return the PopularTabPage, because that is the page we end up when clicking on the ‘Skip for Now’ button.
It is worth noting that ‘clicking’ elements isn’t really correct when interacting with mobile elements, normally we would say that we ‘tap’ on an element or button.
Appium does actually have a method for tapping on elements (as well as others like swiping and long pressing etc). I won’t be covering those in this part, but in the next part of this series, we will create a bunch of static methods (wrapper methods) we can use to perform these types of actions on elements 😀
navToHomeTab() Method
Because we are taken to a new ‘Home’ tab screen when we select the ‘Home’ tab, we should create a new page class for the ‘Home’ tab screen.
- Right-click on the ‘pages’ package, select ‘New’ –> ‘Java Class’ and create a class file called ‘HomeTabPage’
- Open up the ‘HomeTabPage’ class and edit it so it looks like below (we want it to inherit from PopularTabPage, which in turn inherits from BasePage, as that is where we navigate to the page from)…
package pages; public class HomeTabPage extends PopularTabPage { }
We now need to add an AndroidElement on the PopularTabPage for the ‘Home’ tab button. Let’s use the Android Virtual Device and UIAutomatorViewer again, to grab the details of this element.
- Open the Reddit app in the Android Virtual Device and navigate (if necessary) to the ‘Popular’ tab screen
- Once the ‘Popular’ tab screen is displayed, open up UIAutomatorViewer and take a snapshot of the screen to get the XML hierarchy of it
- Once the screenshot has loaded in the UIAutomatorViewer, click on the ‘Home’ tab element/button to inspect the details of it. It should look similar to below…
If we look at the details of this element, we can unfortunately see that there isn’t actually that much uniqueness to go on. It has a non-unique class name of ‘android.widget.TextView’ and has a text value of ‘Home’.We can therefore try and grab the element by Xpath. In Appium, Xpath works similar to Selenium but it is written based on the convention of //className[@attribute=’value’].Based on that convention, our Xpath for the ‘Home tab’ would be //android.widget.TextView[@text=’Home’]We could also try and grab the element by AndroidUIAutomator. This follows the convention of attribute(“value”).Based on that convention, our AndroidUIAutomator for the ‘Home’ tab would be text(“Home”)Let’s use AndroidUIAutomator as our way for grabbing the element, due to the fact it’s less to type and will look cleaner in our code 🙂 - Open up the ‘PopularTabPage’ class in the ‘pages’ package again and add the following AndroidElement in the top of the class…
@AndroidFindBy(uiAutomator = "text(\"Home\")") public AndroidElement homeTab;
(Note that we have to use ‘\’ to escape the double quotes inside quotes)
- Add the following method in the ‘PopularTabPage’ class for selecting the Home tab and being taken to the ‘Home’ tab screen…
public HomeTabPage navToHomeTab() { homeTab.click(); log.info(":: We navigate to the 'Home' tab"); return instanceOf(HomeTabPage.class); }
For the above log.info() method to work, we need to add a Logger for this ‘PopularTabPage’ class…
private static Logger log = LogManager.getLogger(PopularTabPage.class.getName());
The whole thing should look like below…
package pages; import io.appium.java_client.android.AndroidElement; import io.appium.java_client.pagefactory.AndroidFindBy; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class PopularTabPage extends BasePage { private static Logger log = LogManager.getLogger(PopularTabPage.class.getName()); @AndroidFindBy(uiAutomator = "text(\"Home\")") public AndroidElement homeTab; public HomeTabPage navToHomeTab() { homeTab.click(); log.info(":: We navigate to the 'Home' tab"); return instanceOf(HomeTabPage.class); } }
assertIAmOnHomeTab() Method
- Open up the ‘HomeTabPage’ class and add the following method which calls two of our base validation methods and asserts that we are indeed on the Reddit home tab, like below (remembering to add the appropriate imports)…
package pages; import java.util.Arrays; import java.util.List; public class HomeTabPage extends PopularTabPage { public void assertIAmOnHomeTab() { validateCurrentActivity(".MainActivity"); List<String> homeInfo = Arrays.asList("Welcome!", "Vote", "Subscribe", "Reddit community"); validateMultipleInPageSource(homeInfo); } }
Connecting the Step Definitions to the Test methods
We will now call the test methods from their appropriate step definitions.
- Open up the ‘BaseSteps’ class again, and refactor the “And the welcome screen has been skipped without logging in” step so it initialises the page (in this case ‘BasePage’) with the correct Android elements rendered and then calls the ‘skipWelcomeScreen()’ method, like below (also remove the Throwable that will never be thrown)…
@Given("^the welcome screen has been skipped without logging in$") public void theWelcomeScreenHasBeenSkippedWithoutLoggingIn() { instanceOf(BasePage.class).skipWelcomeScreen(); }
- In the ‘NavigationSteps’ class, refactor the “And I select the Home tab” step so it initialises the page (in this case ‘PopularTabPage’) with the correct Android elements rendered and then calls the ‘navToHomeTab()’ method, like below (also remove the Throwable that will never be thrown)…
@When("^the Reddit user views the Home tab$") public void iSelectTheHomeTab() { instanceOf(PopularTabPage.class).navToHomeTab(); }
- In the ‘HomeSteps’ class, refactor the undefined ‘Then I see posts and information pertaining to the Home tab’ step so it initialises the ‘HomeTabPage’ with the correct Android elements rendered and calls the ‘assertIAmOnHomeTab()’ method, like below…
@Then("^they see posts and information pertaining to the Home tab$") public void iSeePostsAndInformationPertainingToTheHomeTab() { instanceOf(HomeTabPage.class).assertIAmOnHomeTab(); }
Running the Test Scenario
Running the Test
- Open up ‘NavigationScenarios.feature’ file and make sure you have the ‘@Nexus5xOreo’ tag at the top of the feature file or above each test scenario you want to run
- Right-click on the test scenario and run it
It should pass 😀
In the next section, we will create static methods we can use as wrapper methods to keep our code nice and clean 😀
Copyright secured by Digiprove © 2018