Software Development Magazine - Project Management, Programming, Software Testing |
Scrum Expert - Articles, tools, videos, news and other resources on Agile, Scrum and Kanban |
Green Coffee - Running Gherkin Tests for Android
Mauricio Togneri, https://mauriciotogneri.com/, @mauriciotogneri
Green Coffee is an open source library that allows you to run your acceptance tests written in Gherkin in your Android instrumentation tests using the step definitions that you declare. Gherkin is a Business Readable, Domain Specific Language that lets you describe software's behavior without detailing how that behavior is implemented. This allows to apply a Behavior-Driven Development (BDD) approach and Agile testing to mobile software development.
Website: https://github.com/mauriciotogneri/green-coffee
License & Pricing: CasperJS is licensed under the MIT License
Support: https://github.com/mauriciotogneri/green-coffee/issues
Example
This page contains a small example so you can have a glimpse of how to use and what is possible to do with the library. Given the following feature written in Gherkin:
First, create a class that extends from GreenCoffeeTest
and declare the Activity, the feature and the step definitions that will be used:
@RunWith(Parameterized.class) public class LoginFeatureTest extends GreenCoffeeTest { @Rule public ActivityTestRuleactivity = new ActivityTestRule<>(LoginActivity.class); public LoginFeatureTest(ScenarioConfig scenarioConfig) { super(scenarioConfig); } @Parameters(name = "{0}") public static Iterable scenarios() throws IOException { return new GreenCoffeeConfig(true) // automatically take a screenshot if a test fails .withFeatureFromAssets("assets/login.feature") .scenarios( new Locale("en", "GB"), new Locale("es", "ES") ); // the locales used to run the scenarios (optional) } @Test public void test() { start(new LoginSteps()); } }
If no locales are defined, the default one will be used. Next, create a class containing the steps definitions:
public class LoginSteps extends GreenCoffeeSteps { @Given("^I see an empty login form$") public void iSeeAnEmptyLoginForm() { onViewWithId(R.id.login_input_username).isEmpty(); onViewWithId(R.id.login_input_password).isEmpty(); } @When("^I introduce an invalid username$") public void iIntroduceAnInvalidUsername() { onViewWithId(R.id.login_input_username).type("guest"); } @When("^I introduce an invalid password$") public void iIntroduceAnInvalidPassword() { onViewWithId(R.id.login_input_password).type("1234"); } @When("^I press the login button$") public void iPressTheLoginButton() { onViewWithId(R.id.login_button_doLogin).click(); } @Then("^I see an error message saying 'Invalid credentials'$") public void iSeeAnErrorMessageSayingInvalidCredentials() { onViewWithText(R.string.login_credentials_error).isDisplayed(); } }
And that's it, now you can create your own tests using Green Coffee. This is how it looks when you run a more complex test:
You can see an example applied to a full app here.
Installation
In order to use Green Coffee, add the following dependency to your build.gradle
file:
dependencies { androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.mauriciotogneri:greencoffee:3.2.1' }
defaultConfig { testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner' }
How it works
In essence, Green Coffee is no more than a Gherkin test runner that you can use for your Android instrumentation tests. To understand this better, let's see the main components involved in a test execution:
Feature
Features are written using the Gherkin language. Each feature consists of one or more scenarios that describe different situations in order to test that feature. Each scenario consists of steps that will simulate user interactions with the UI.
Test definition
A test definition is a class that extends from GreenCoffeeTest
and declares the Activity, the feature and the step definitions that will be used during the test. You can find more information on Test definitons here.
Step definitions
The step definitions consist of set of classes that define all methods that will be used to match the steps in the scenarios involved in the test. You can find more information on Step definitons here.
Green Coffee is just the glue that interconnects these three components. This is how it works:
- When we launch a instrumentation test, Green Coffee will read the feature declared in the test class, parse it and create the list of scenarios that will be used to run the tests
- For each scenario declared in the feature, the Android platform will automatically create an instance of the Activity that we declared in the test class and run a single test
- For each test executed, Green Coffee will automatically match each step in the corresponding scenario with the step definitions declared in the test class
- When a step in a scenario matches a method in the step definitions, Green Coffee will invoke that method with the corresponding parameters
- Each method invoked will either interact with the UI simulating a user interaction or verify that certain conditions are fulfilled
Espresso support
Although you can choose how to interact with the UI once a step definition is invoked, the library includes a set of methods that makes it more readable using Espresso. You can choose to use these helper methods, use directly Espresso or use any other mechanism.
A class that extends from GreenCoffeeSteps
will have access to the following methods:
onViewWithId(int resourceId)
onViewWithId(@IdRes int resourceId, int index)
onViewWithText(int resourceId)
onViewWithText(@StringRes int resourceId, int index)
onViewWithText(String text)
onViewWithText(String text, int index)
onViewChildOf(@IdRes int parentViewId, int index)
withIndex(Matcher<View> matcher, int index)
nthChildOf(Matcher<View> parentMatcher, int childPosition)
waitFor(long value, TimeUnit timeUnit)
These methods are used to obtain a reference to an object that can perform operations on a UI component. For example:
@Given("...") public void someMethod() { onViewWithId(R.id.login_input_username); onViewWithText(R.string.login_credentials_error); onViewWithText("Some text"); }
All these methods return an object of the class ActionableView
. This object can perform actions on the view or verify some conditions. Let's see some examples
@Given("...") public void someMethod() { ActionableView view = onViewWithId(...); // action view.click(); view.doubleClick(); view.longClick(); view.type(String text); view.clearText(); view.scrollTo(); view.swipeUp(); view.swipeDown(); view.swipeLeft(); view.swipeRight(); // verification view.isDisplayed(); view.isNotDisplayed(); view.isCompletelyDisplayed(); view.isEmpty(); view.isNotEmpty(); view.isSelected(); view.isNotSelected(); view.isChecked(); view.isNotChecked(); view.isFocusable(); view.isNotFocusable(); view.isClickable(); view.isEnabled(); view.isDisabled(); view.doesNotExist(); view.hasFocus(); view.doesNotHaveFocus(); view.hasErrorText(String text); view.contains(Object element); view.doesNotContain(Object element); hasDrawable(); doesNotHaveDrawable(); check(ViewAssertion viewAssertion); perform(ViewAction viewAction); }
If a verification is not fulfilled, an exception will be thrown and the test for the scenario will be marked as failed.
The class GreenCoffeeSteps
also provides the following methods:
closeKeyboard()
: closes the keyboardpressBack()
: simulates pressing the back buttonstring(@StringRes int key)
: returns the string value for the given keylocale()
: return the current Locale used in the testtakeScreenshot(File file)
: takes a screenshot and stores it in the given file
Lists
All the previous methods are used to act on UI components such as TextView
, EditText
, CheckBox
, Button
, etc. However, when it comes to interact with list views, we need to access elements inside of them in a different way.
Let's imagine we have a list view that displays objects of the class Contact
. In order to obtain a reference on an element in the list, we have to create a custom matcher:
public class ContactMatcher extends DataMatcher{ public ContactMatcher(int resourceId) { super(resourceId, Contact.class); } @Override public boolean matches(Contact contact, String content) { return contact.name().equals(content); } }
Then we can use it to obtain an object of the class ActionableData
and perform operations on the matched UI component.
@Given("...") public void someMethod() { DataMatchercontactMatcher = new ContactMatcher(R.id.contacts_list_view); ActionableData data = contactMatcher.with("..."); data.click(); data.doubleClick(); data.longClick(); data.scrollTo(); }
Permissions
In order to accept permission during the execution of a test, the best option is to use the GrantPermissionRule
.
Related Resources
This article was originally published in April 2018
Methods & Tools Testmatick.com Software Testing Magazine The Scrum Expert |