Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/roroche/androidtestingbox
Android project to experiment various testing tools
https://github.com/roroche/androidtestingbox
android assertj cucumber cucumber-jvm espresso fluent-assertions frutilla jgiven junit junit-hierarchicalcontextrunner kluent kotlin robolectric robotium spectrum spek truth zester
Last synced: 4 months ago
JSON representation
Android project to experiment various testing tools
- Host: GitHub
- URL: https://github.com/roroche/androidtestingbox
- Owner: RoRoche
- License: apache-2.0
- Created: 2016-11-11T19:04:25.000Z (about 8 years ago)
- Default Branch: master
- Last Pushed: 2020-04-26T16:49:41.000Z (almost 5 years ago)
- Last Synced: 2024-10-11T03:02:31.076Z (4 months ago)
- Topics: android, assertj, cucumber, cucumber-jvm, espresso, fluent-assertions, frutilla, jgiven, junit, junit-hierarchicalcontextrunner, kluent, kotlin, robolectric, robotium, spectrum, spek, truth, zester
- Language: Java
- Homepage: https://roroche.github.io/AndroidTestingBox/
- Size: 734 KB
- Stars: 63
- Watchers: 6
- Forks: 3
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# AndroidTestingBox
Android project to experiment various testing tools.
It targets **Java** and **Kotlin** languages.
Priority is given to fluency and ease of use.
The idea is to provide a toolbox to write elegant and intelligible tests, with modern techniques like **behavior-driven testing frameworks** or **fluent assertions**.[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-AndroidTestingBox-brightgreen.svg?style=flat)](https://android-arsenal.com/details/3/4658)
[![Android Weekly](https://img.shields.io/badge/Android%20Weekly-%23242-green.svg)](http://androidweekly.net/issues/issue-242)
[![Dependency Status](https://www.versioneye.com/user/projects/58b85f8401b5b7003a2129e7/badge.svg?style=flat-square)](https://www.versioneye.com/user/projects/58b85f8401b5b7003a2129e7)![logo](https://raw.githubusercontent.com/RoRoche/AndroidTestingBox/master/assets/logo.png)
- [AndroidTestingBox in the news](#androidtestingbox-in-the-news)
- [System under test (SUT)](#system-under-test-sut)
* [Simple Java class](#simple-java-class)
* [Android `Activity`](#android-activity)
- [JUnit](#junit)
* [Fluent assertions: truth](#fluent-assertions-truth)
+ [Alternative: AssertJ](#alternative-assertj)
* [Frutilla](#frutilla)
* [Fluent test method names](#fluent-test-method-names)
* [Specifications framework: Spectrum](#specifications-framework-spectrum)
+ [Alternative: Oleaster](#alternative-oleaster)
* [Hierarchies in JUnit: junit-hierarchicalcontextrunner](#hierarchies-in-junit-junit-hierarchicalcontextrunner)
+ [Novelty to consider: JUnit 5 Nested Tests](#novelty-to-consider-junit-5-nested-tests)
* [BDD tools](#bdd-tools)
+ [Cucumber](#cucumber)
+ [JGiven](#jgiven)
* [Mutation testing: Zester plugin](#mutation-testing-zester-plugin)
* [Alternative to JUnit: TestNG](#alternative-to-junit-testng)
- [Kotlin](#kotlin)
* [Fluent assertions: Kluent](#fluent-assertions-kluent)
+ [Alternative: Expekt](#alternative-expekt)
* [Specifications framework: Spek](#specifications-framework-spek)
- [Android](#android)
* [Fluent assertions: AssertJ Android](#fluent-assertions-assertj-android)
* [Robotium](#robotium)
* [Espresso](#espresso)
* [Robolectric](#robolectric)
* [Cucumber support](#cucumber-support)
* [JGiven support](#jgiven-support)
- [IDE configuration](#ide-configuration)
- [Nota Bene](#nota-bene)
- [Bibliography](#bibliography)
- [Interesting repositories](#interesting-repositories)
- [Interesting articles](#interesting-articles)
- [Resources](#resources)
- [Logo credits](#logo-credits)## AndroidTestingBox in the news
* [Android Weekly #242](http://androidweekly.net/issues/issue-242)
## System under test (SUT)
### Simple Java class
```java
public class Sum {
public final int a;
public final int b;
private final LazyInitializer mSum;public Sum(int a, int b) {
this.a = a;
this.b = b;
mSum = new LazyInitializer() {
@Override
protected Integer initialize() throws ConcurrentException {
return Sum.this.a + Sum.this.b;
}
};
}public int getSum() throws ConcurrentException {
return mSum.get();
}
}
```### Android `Activity`
Here stands the layout file:
```xml
```
and here stands the corresponding `Activity`:
```kotlin
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)val textView: TextView = findViewById(R.id.ActivityMain_TextView) as TextView
val button = findViewById(R.id.ActivityMain_Button)
button.setOnClickListener({ view: View -> textView.setText(R.string.text_changed_after_button_click) })
}
}
```## JUnit
### Fluent assertions: truth
-
#### Alternative: AssertJ
*
### Frutilla
-
```java
@RunWith(value = org.frutilla.FrutillaTestRunner.class)
public class FrutillaSumTest {@Frutilla(
Given = "two numbers a = 1 and b = 3",
When = "computing the sum of these 2 numbers",
Then = "should compute sum = 4"
)
@Test
public void test_addition_isCorrect() throws Exception {
given("two numbers", () -> {
final int a = 1;
final int b = 3;when("computing the sum of these 2 numbers", () -> {
final Sum sum = new Sum(a, b);then("should compute sum = 4", () -> assertThat(sum.getSum()).isEqualTo(4));
});
});
}
}
```### Fluent test method names
-
### Specifications framework: Spectrum
-
-```java
import static com.google.common.truth.Truth.assertThat;
import static com.greghaskins.spectrum.Spectrum.describe;
import static com.greghaskins.spectrum.Spectrum.it;@RunWith(Spectrum.class)
public class SpectrumSumTest {
{
describe("Given two numbers a = 1 and b = 3", () -> {
final int a = 1;
final int b = 3;it("computing the sum of these 2 numbers, should compute sum = 4", () -> {
final Sum sum = new Sum(a, b);assertThat(sum.getSum()).isEqualTo(4);
});
});
}
}
```#### Alternative: Oleaster
*
### Hierarchies in JUnit: junit-hierarchicalcontextrunner
-
```java
@RunWith(HierarchicalContextRunner.class)
public class HCRSumTest {public class GivenTwoNumbers1And3 {
private int a = 1;
private int b = 3;@Before
public void setUp() {
a = 1;
b = 3;
}public class WhenComputingSum {
private Sum sum;@Before
public void setUp() {
sum = new Sum(a, b);
}@Test
public void thenShouldBeEqualTo4() throws ConcurrentException {
assertThat(sum.getSum()).isEqualTo(4);
}
}public class WhenMultiplying {
private int multiply;@Before
public void setUp() {
multiply = a * b;
}@Test
public void thenShouldBeEqualTo3() throws ConcurrentException {
assertThat(multiply).isEqualTo(3);
}
}
}
}
```#### Novelty to consider: JUnit 5 Nested Tests
-
- The `@Nested` and `@DisplayName` annotations allow developers to reach an elegant "given/when/then" canvas### BDD tools
#### Cucumber
-
* Define the `.feature` file:
```gherkin
Feature: Sum computationScenario Outline: Sum 2 integers
Given two int and to sum
When computing sum
Then it should beExamples:
| a | b | sum |
| 1 | 3 | 4 |
| -1 | -3 | -4 |
| -1 | 3 | 2 |
```* Define the corresponding steps:
```java
public class SumSteps {
Sum moSum;
int miSum;@Given("^two int (-?\\d+) and (-?\\d+) to sum$")
public void twoIntToSum(final int a, final int b) {
moSum = new Sum(a, b);
}@When("^computing sum$")
public void computingSum() throws ConcurrentException {
miSum = moSum.getSum();
}@Then("^it should be (-?\\d+)$")
public void itShouldBe(final int expected) {
Assert.assertEquals(expected, miSum);
}
}
```* Define the specific runner:
```java
@RunWith(Cucumber.class)
@CucumberOptions(
features = "src/test/resources/"
)
public class SumTestRunner {
}
```* Relevant tools:
* to write Gherkin features: [Tidy Gherkin](https://chrome.google.com/webstore/detail/tidy-gherkin/nobemmencanophcnicjhfhnjiimegjeo?hl=en-GB)
* to display Gherkin features in Chrome a way pretty way: [Pretty Gherkin](https://chrome.google.com/webstore/detail/pretty-gherkin/blemhogdenfkkojlpghcinocbfjheioc?hl=en-GB)
* to generating specifications from Gherkin source files: [featurebook](https://www.npmjs.com/package/featurebook)#### JGiven
-
```java
public class JGivenSumTest extends SimpleScenarioTest {@Test
public void addition_isCorrect() throws ConcurrentException {
given().first_number_$(1).and().second_number_$(3);
when().computing_sum();
then().it_should_be_$(4);
}public static class TestSteps extends Stage {
private int mA;
private int mB;
private Sum mSum;public TestSteps first_number_$(final int piA) {
mA = piA;
return this;
}public void second_number_$(final int piB) {
mB = piB;
}public void computing_sum() {
mSum = new Sum(mA, mB);
}public void it_should_be_$(final int piExpected) throws ConcurrentException {
assertThat(mSum.getSum()).isEqualTo(piExpected);
}
}
}
```### Mutation testing: Zester plugin
-
-For this sample project, define a new "Run configuration" with Zester such as:
```
Target classes: com.guddy.android_testing_box.zester.*
Test class: com.guddy.android_testing_box.zester.ZesterExampleTest
```It generates an HTML report in the `build/reports/zester/` directory, showing that 2 "mutants" survived to unit tests (so potential bugs, and in this case, yes it is).
### Alternative to JUnit: TestNG
*
## Kotlin
### Fluent assertions: Kluent
-
#### Alternative: Expekt
*
### Specifications framework: Spek
-
-```kotlin
@RunWith(JUnitPlatform::class)
class SpekSumTest : Spek({given("two numbers a = 1 and b = 3") {
val a: Int = 1
val b: Int = 3on("computing the sum of these 2 numbers") {
val sum: Sum = Sum(a, b)it("should compute sum = 4") {
sum.sum shouldBe 4
}
}
}
})
```## Android
### Fluent assertions: AssertJ Android
-
### Robotium
-
```java
@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
//region Rule
@Rule
public final ActivityTestRule mActivityTestRule = new ActivityTestRule<>(MainActivity.class, true, false);
//endregion//region Fields
private Solo mSolo;
private MainActivity mActivity;
private Context mContextTarget;
//endregion//region Test lifecycle
@Before
public void setUp() throws Exception {
mActivity = mActivityTestRule.getActivity();
mSolo = new Solo(InstrumentationRegistry.getInstrumentation(), mActivity);
mContextTarget = InstrumentationRegistry.getTargetContext();
}@After
public void tearDown() throws Exception {
mSolo.finishOpenedActivities();
}
//endregion//region Test methods
@Test
public void testTextDisplayed() throws Exception {
given("the main activity", () -> {when("launching activity", () -> {
mActivity = mActivityTestRule.launchActivity(null);then("should display 'app_name'", () -> {
final boolean lbFoundAppName = mSolo.waitForText(mContextTarget.getString(R.string.app_name), 1, 5000L, true);
assertThat(lbFoundAppName);
});
});
});
}
//endregion
}
```### Espresso
-
### Robolectric
-
-```groovy
testCompile 'org.robolectric:robolectric:3.2.2'
testCompile 'org.robolectric:shadows-multidex:3.2.2'
testCompile 'org.robolectric:shadows-support-v4:3.2.2'
testCompile 'org.khronos:opengl-api:gl1.1-android-2.1_r1'
``````java
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
public class RobolectricMainActivityTest {@Test
public void test_clickingButton_shouldChangeText() throws Exception {given("The MainActivity", () -> {
final MainActivity loActivity = Robolectric.setupActivity(MainActivity.class);
final Button loButton = (Button) loActivity.findViewById(R.id.ActivityMain_Button);
final TextView loTextView = (TextView) loActivity.findViewById(R.id.ActivityMain_TextView);when("clicking on the button", () -> {
loButton.performClick();then("text should have changed", () -> assertThat(loTextView.getText().toString()).isEqualTo("Text changed after button click"));
});
});
}}
```### Cucumber support
-
* Configure the `build.gradle` file:
```groovy
android {
defaultConfig {
testApplicationId "com.guddy.android_testing_box.ui"
testInstrumentationRunner "com.guddy.android_testing_box.ui.CucumberInstrumentationRunner"
}
sourceSets {
androidTest {
assets.srcDirs = ['src/androidTest/assets']
}
}
}
```* Write features in the `src/androidTest/assets` directory, for example this `main.feature` file:
```gherkin
Feature: Main activityScenario: Click on the button
Given the initial state is shown
When clicking on the button
Then the text changed to "Text changed after button click"
```* Define the corresponding steps:
```java
@CucumberOptions(features = "features")
public class CucumberMainActivitySteps extends ActivityInstrumentationTestCase2 {public CucumberMainActivitySteps() {
super(MainActivity.class);
}@Given("^the initial state is shown$")
public void the_initial_main_activity_is_shown() {
// Call the activity before each test.
getActivity();
}@When("^clicking on the button$")
public void clicking_the_Click_Me_button() {
onView(withId(R.id.ActivityMain_Button)).perform(click());
}@Then("^the text changed to \"([^\"]*)\"$")
public void text_$_is_shown(final String s) {
onView(withId(R.id.ActivityMain_TextView)).check(matches(withText(s)));
}
}
```* Define the specific runner:
```java
public class CucumberInstrumentationRunner extends MonitoringInstrumentation {private final CucumberInstrumentationCore mInstrumentationCore = new CucumberInstrumentationCore(this);
@Override
public void onCreate(Bundle arguments) {
super.onCreate(arguments);mInstrumentationCore.create(arguments);
start();
}@Override
public void onStart() {
super.onStart();waitForIdleSync();
mInstrumentationCore.start();
}
}
```### JGiven support
-
-```java
@RunWith(AndroidJUnit4.class)
public class EspressoJGivenMainActivityTest extends
SimpleScenarioTest {@Rule
@ScenarioState
public ActivityTestRule activityTestRule = new ActivityTestRule<>(MainActivity.class);@Rule
public AndroidJGivenTestRule androidJGivenTestRule = new AndroidJGivenTestRule(this.getScenario());@Test
public void clicking_ClickMe_changes_the_text() {
given().the_initial_main_activity_is_shown()
.with().text("AndroidTestingBox");
when().clicking_the_Click_Me_button();
then().text_$_is_shown("Text changed after button click");
}public static class Steps extends Stage {
@ScenarioState
CurrentStep currentStep;@ScenarioState
ActivityTestRule activityTestRule;public Steps the_initial_main_activity_is_shown() {
// nothing to do, just for reporting
return this;
}public Steps clicking_the_Click_Me_button() {
onView(withId(R.id.ActivityMain_Button)).perform(click());
return this;
}public Steps text(@Quoted String s) {
return text_$_is_shown(s);
}public Steps text_$_is_shown(@Quoted String s) {
onView(withId(R.id.ActivityMain_TextView)).check(matches(withText(s)));
takeScreenshot();
return this;
}private void takeScreenshot() {
currentStep.addAttachment(
Attachment.fromBinaryBytes(ScreenshotUtils.takeScreenshot(activityTestRule.getActivity()), MediaType.PNG)
.showDirectly());
}
}
}
```## IDE configuration
- MoreUnit plugin:
## Nota Bene
A relevant combination of [Dagger2](https://google.github.io/dagger/) and [mockito](http://site.mockito.org/) is already described in a previous post I wrote:
## Bibliography
-
-
-## Interesting repositories
-
-
-
-## Interesting articles
-
-
-
-## Resources
-
-## Logo credits
Science graphic by Pixel perfect from Flaticon is licensed under CC BY 3.0. Made with Logo Maker