After completing our page object classes it is time to refactor our test cases. To start, let’s open the class "OrderItemFromMenuTheTest" and refactor the shouldBeAbleToSelectAnItemInTheMenu()
test method with our page objects.
Start by initializing new page object instances in the "OrderItemFromMenuTheTest" class.
public class OrderItemFromMenuTheTest extends TestBase {
OnboardingScreen onboardingScreen = new OnboardingScreen();
MenuScreen menuScreen = new MenuScreen();
.
.
.
}
Inside of the shouldBeAbleToSelectAnItemInTheMenu()
test method, the calls to "close_button" and "use_menu" need to be refactored.
Recall that the method scrollToMenuItem()
we defined in the previous chapter requires two arguments.
So this:
public void shouldBeAbleToSelectAnItemInTheMenu() {
onView(ViewMatchers.withId(R.id.close_button))
.perform(click());
onView(withId(R.id.use_menu))
.perform(click());
onView(withId(R.id.beverage_recycler_view))
.perform(actionOnItem(hasDescendant(withText("CAPPUCCINO"))
, click()));
onView(withId(R.id.beverage_detail_title))
.check(matches(withText("Cappuccino")));
}
Becomes:
public void shouldBeAbleToSelectAnItemInTheMenu() {
onboardingScreen.closeOnBoardingScreen();
menuScreen.clickMenuButton();
menuScreen
.scrollToMenuItem("CAPPUCCINO", "Cappuccino");
}
Let’s also go ahead and add private access modifiers to our variables.
Now we can run our test and check the results of our refactored code.
Our test passes and appears to be working.
Now we should remove all of the unused method import statements resulting from our changes.
From the "Code" toolbar, select "Optimize Imports".
Now our test code is clean.
Let's refactor the second test. Open the "CreateCustomOderWithIngredientsTest" class.
Here, we find all of the steps previously refactored for the page object pattern.
The "Close" button is from the onboarding screen, the "+" and "Chocolate" elements are from the custom order screen, and the order summary is from the review order screen.
So we have three page objects.
As in the previous example, begin by initializing new page object instances in class "CreateCustomOderWithIngredientsTest":
public class CreateCustomOderWithIngredientsTest extends TestBase {
OnboardingScreen onboardingScreen = new OnboardingScreen();
CustomOrderScreen customOrderScreen = new CustomOrderScreen();
ReviewOrder reviewOrder = new ReviewOrder();
.
.
.
}
Now refactor the test using page objects. Method customOrderScreen.customizeYourOrder()
takes an argument for the number of shots.
So this:
public void orderOverViewShouldDisplayIngredients() {
onView(ViewMatchers.withId(R.id.close_button))
.perform(click());
onView(withText("+"))
.perform(click(), click());
onView(withId(R.id.chocolate))
.perform(click());
onView(withText("Review order"))
.perform(click());
onView(withId(R.id.beverage_detail_ingredients))
.check(matches(
withText("Ingredients:\n2 shots of espresso\nChocolate")));
}
Becomes:
public void orderOverViewShouldDisplayIngredients() {
onboardingScreen.closeOnBoardingScreen();
customOrderScreen.customizeYourOrder(4);
reviewObject.checkIngredients(3);
}
Recall that the test procedure subtracts one shot after adding shots so our final ingredient count should be 3.
Let’s run this test and check the test result.
The ingredient count appears to be correct and our test passes.
The last class we need to refactor is "SendOrderToEmailTest". Here we will open the class and paste the same three page object instantiations we used in the previous test.
public class SendOrderToEmailTest extends TestBase {
OnboardingScreen onboardingScreen = new OnboardingScreen();
CustomOrderScreen customOrderScreen = new CustomOrderScreen();
ReviewOrder reviewOrder = new ReviewOrder();
.
.
.
}
Now refactor the shouldSendAnIntentContainingTheRightOrderDetails()
method.
Method customizeYourOrder()
requires an item count parameter and reviewOrder()
needs the appropriate text parameters.
So this:
public void shouldSendAnIntentContainingTheRightOrderDetails() {
onView(ViewMatchers.withId(R.id.close_button))
.perform(click());
for (int i = 0; i < 4; i++) {
onView(withText("+")).perform(click());
}
onView(withText("-"))
.perform(click());
onView(withId(R.id.chocolate))
.perform(click());
onView(withText("Review order"))
.perform(click());
onView(withId(R.id.name_text_box))
.perform(scrollTo(), typeText("Moataz"));
onView(withId(R.id.custom_order_name_box))
.perform(scrollTo(), typeText("TAU Order Name"));
onView(withId(R.id.mail_order_button))
.perform(scrollTo(), click());
intended(allOf(
hasAction(equalTo(Intent.ACTION_SENDTO)),
hasExtra(Intent.EXTRA_SUBJECT, "Order: Moataz - TAU Order Name")));
}
Becomes:
public void shouldSendAnIntentContainingTheRightOrderDetails()
{
onboardingScreen.closeOnBoardingScreen();
customOrderScreen.customizeYourOrder(4);
reviewOrder.reviewOrder("Moataz", "TAU Order Name");
}
Note that we no longer need the third parameter of reviewOrder()
since we already include that information as part of the order title.
Let’s remove that last parameter from the method definition in "ReviewOrder.java".
Now run our test and check the test result.
The test passes.
In this demo we will learn how to run multiple test cases using a test suite.
After finishing the implementation of the page object and refactoring our test cases it is time to create a custom class to run all of the test cases together.
Right-click on the "java" folder and select "New > Package".
Call this new package "runner". Now right-click on the new "runner" folder and select "New > Java Class".
Name this class "UITestSuite".
In the generated class add a @RunsWith
annotation with the argument "Suite.class" from JUnitRunner.
@RunWith(Suite.class)
public class UITestSuite {
}
Also add annotation @Suite.SuiteClasses()
into which we will pass the classes that we wish to run.
@RunWith(Suite.class)
@Suite.SuiteClasses(
{CreateCustomOderWithIngredientsTest.class,
OrderItemFromMenuTheTest.class,
SendOrderToEmailTest.class
})
public class UITestSuite {
}
Now we can run this suite and check the results.
On execution it will begin to run all of the test cases added in the annotations. All of the test cases appear to pass.
In this demo we will learn how to create custom annotations for our test cases.
For example, we can create an annotation for a smoke test or an annotation for an end-to-end test.
Custom annotations offer additional naming flexibility for organizational purposes.
We will create two custom annotations, one for smoke tests and one for end-to-end tests.
Afterwards we will use them in our test cases.
Right-click on the "java" folder and select "New > Package".
Call this new package "utils" and click "OK".
Now we need to create two interfaces, one for end-to-end tests and one for smoke tests.
Right-click on the "utils" package and select "New > Java Class".
Call this class "E2ETest" and change the "Kind:" field from "Class" to "Interface".
Click "OK".
Repeat these steps to create a second interface named "SmokeTest".
To complete the implementation we need to add two lines of annotations to each class as well as prefixing "interface" with an "@" symbol which effectively makes it a runtime policy.
@Target( {ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface E2ETest {
}
@Target( {ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SmokeTest {
}
To use these annotations in our tests simply reference one or the other below the @Test
annotation. For example:
@Test
public void orderOverViewShouldDisplayIngredients() {
}
Becomes:
@Test
@SmokeTest //alternately @E2ETest
public void orderOverViewShouldDisplayIngredients() {
}
There are other built-in annotations provided by Android and JUnit. These include:
@SmallTest (androidx.test.filters)
@LargeTest (androidx.test.filters)
@MediumTest (androidx.test.filters)
If you only wish to run end-to-end tests or only smoke tests, we can specify an annotation in the "build.gradle" file and pass the environmental variable with that specification.
In the next chapter we will explore annotation usage with Cucumber.
CreateCustomOderWithIngredientsTest
package tests;
import androidx.test.filters.SmallTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
import pageobjects.CustomOrderScreen;
import pageobjects.OnboardingScreen;
import pageobjects.ReviewOrderScreen;
@RunWith(AndroidJUnit4ClassRunner.class)
@SmallTest
public class CreateCustomOderWithIngredientsTest extends TestBase {
private OnboardingScreen onboardObject = new OnboardingScreen();
private CustomOrderScreen customizeObject = new CustomOrderScreen();
private ReviewOrderScreen reviewObject = new ReviewOrderScreen();
@Test
@SmokeTest
public void orderOverViewDisplayIngredients() {
onboardObject.closeOnBoardingScreen();
customizeObject.customizeYourOrder(3);
reviewObject.checkIngredients(2);
}
}
package tests;
import androidx.test.filters.MediumTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
import pageobjects.MenuScreen;
import pageobjects.OnboardingScreen;
@RunWith(AndroidJUnit4ClassRunner.class)
@MediumTest
public class OrderItemFromMenuTheTest extends TestBase {
private OnboardingScreen onboardObject = new OnboardingScreen();
private MenuScreen menuObject = new MenuScreen();
private String selectedItem = "CAPPUCCINO";
private String itemTitle = "Cappuccino";
@Test
@E2ETest
public void selectAnItemInTheMenuWithPageObject() {
onboardObject.closeOnBoardingScreen();
menuObject.clickOnMenuButton();
menuObject.scrollToMenuItem(selectedItem, itemTitle);
}
}
package tests;
import androidx.test.filters.LargeTest;
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
import pageobjects.CustomOrderScreen;
import pageobjects.OnboardingScreen;
import pageobjects.ReviewOrderScreen;
@RunWith(AndroidJUnit4ClassRunner.class)
@LargeTest
public class SendOrderToEmailTest extends TestBase {
private OnboardingScreen onboardObject = new OnboardingScreen();
private CustomOrderScreen customizeObject = new CustomOrderScreen();
private ReviewOrderScreen reviewObject = new ReviewOrderScreen();
private String name = "Moataz";
private String orderName = "TAU Order Name";
private String mailSubject = "Order: Moataz - TAU Order Name";
@Test
public void sendAnIntentContainingTheRightOrderDetails() {
onboardObject.closeOnBoardingScreen();
customizeObject.customizeYourOrder(4);
reviewObject.reviewOrder(name, orderName, mailSubject);
}
}
package tests;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Rule;
import nl.testchamber.mailordercoffeeshop.MainActivity;
class TestBase {
@Rule
public IntentsTestRule<MainActivity> activityTestRule =
new IntentsTestRule<MainActivity>(MainActivity.class) {
@Override
public void beforeActivityLaunched() {
super.beforeActivityLaunched();
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
SharedPreferences.Editor editor =
context.getSharedPreferences(context.getPackageName(),
Activity.MODE_PRIVATE).edit();
editor.putBoolean("is_first_launch", true);
editor.commit();
}
};
}
package runner;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import tests.CreateCustomOderWithIngredientsTest;
import tests.OrderItemFromMenuTheTest;
import tests.SendOrderToEmailTest;
@RunWith(Suite.class)
@Suite.SuiteClasses(
{CreateCustomOderWithIngredientsTest.class,
OrderItemFromMenuTheTest.class,
SendOrderToEmailTest.class
})
public class InstrumentationTestSuite {}
package utils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target( {ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface E2ETest {
}
package utils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target( {ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SmokeTest {
}