All Projects → RoRoche → AndroidTestingBox

RoRoche / AndroidTestingBox

Licence: Apache-2.0 License
Android project to experiment various testing tools

Programming Languages

java
68154 projects - #9 most used programming language
kotlin
9241 projects
Gherkin
971 projects

Projects that are alternatives of or similar to AndroidTestingBox

Clean Mvvm Archcomponents
👽 Android app consuming Star Wars API.Built with clean architecture ,MVVM pattern, Koin , Coroutines + Flows ,Architecture Components, Data Binding , Firebase , Unit/UI Tests ,Motion Layout
Stars: ✭ 285 (+352.38%)
Mutual labels:  junit, espresso, robolectric
Android Gif Example
Gif RecyclerView in MVP using Dagger 2 + Retrofit 2 + Moshi + RxJava 2 + Glide 4 with JUnit and Espresso tests written in Kotlin + Kotlin DSL!
Stars: ✭ 334 (+430.16%)
Mutual labels:  junit, espresso, robolectric
justtestlah
Dynamic test framework for web and mobile applications
Stars: ✭ 43 (-31.75%)
Mutual labels:  cucumber, junit, assertj
Kotlin Espresso Sample
MVP Android project that uses Espresso instrumentation tests and Robolectric. All written in Kotlin.
Stars: ✭ 104 (+65.08%)
Mutual labels:  junit, espresso, robolectric
Mvvm Kotlin Android Architecture
MVVM + Kotlin + Retrofit2 + Hilt + Coroutines + Kotlin Flow + mockK + Espresso + Junit5
Stars: ✭ 1,014 (+1509.52%)
Mutual labels:  junit, espresso
Androidut
Android开发中必要的一环---单元测试(Unit Test)
Stars: ✭ 419 (+565.08%)
Mutual labels:  junit, robolectric
Test Smells
Test Smells for Android developers
Stars: ✭ 120 (+90.48%)
Mutual labels:  junit, robolectric
Androidunittest
Save time & clear your unit tests on Android !
Stars: ✭ 205 (+225.4%)
Mutual labels:  junit, robolectric
EasyUtAndroid
Android unit testing example 全面的android应用单元测试方法及案例
Stars: ✭ 21 (-66.67%)
Mutual labels:  junit, robolectric
Testowanieoprogramowania
Testowanie oprogramowania - Książka dla początkujących testerów
Stars: ✭ 146 (+131.75%)
Mutual labels:  junit, espresso
Cleanguitestarchitecture
Sample project of Android GUI test automation using Espresso, Cucumber and the Page Object Pattern
Stars: ✭ 139 (+120.63%)
Mutual labels:  cucumber, espresso
Green Coffee
Android library that allows you to run your acceptance tests written in Gherkin in your Android instrumentation tests.
Stars: ✭ 219 (+247.62%)
Mutual labels:  cucumber, espresso
jpa-unit
JUnit extension to test javax.persistence entities
Stars: ✭ 28 (-55.56%)
Mutual labels:  cucumber, junit
qt-spek
基于Qt的频谱分析器,修改于spek
Stars: ✭ 34 (-46.03%)
Mutual labels:  spectrum, spek
osgi-test
Testing support for OSGi. Includes JUnit 4 and JUnit 5 support and AssertJ support.
Stars: ✭ 22 (-65.08%)
Mutual labels:  junit, assertj
Truth
Fluent assertions for Java and Android
Stars: ✭ 2,359 (+3644.44%)
Mutual labels:  junit, truth
Android tmdb clean architecture
Showcase of clean architecture concepts along with Continuous Integration and Development for modular Android applications. Includes test suits (functional and unit tests) along with code coverage.
Stars: ✭ 63 (+0%)
Mutual labels:  espresso, robolectric
Marvel
Marvel Characters Android Application Assigned by smava GmbH
Stars: ✭ 227 (+260.32%)
Mutual labels:  espresso, robolectric
Java Ddd Skeleton
♨️ DDD in Java skeleton & examples. Course:
Stars: ✭ 140 (+122.22%)
Mutual labels:  cucumber, junit
agent-java-cucumber
Cucumber JVM version [1.0.0; 2.0.0) adapter
Stars: ✭ 21 (-66.67%)
Mutual labels:  cucumber-jvm, cucumber

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 Android Weekly Dependency Status

logo

AndroidTestingBox in the news

System under test (SUT)

Simple Java class

public class Sum {
    public final int a;
    public final int b;
    private final LazyInitializer<Integer> mSum;

    public Sum(int a, int b) {
        this.a = a;
        this.b = b;
        mSum = new LazyInitializer<Integer>() {
            @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 version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/activity_main"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/ActivityMain_TextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="@string/app_name"/>

    <Button
        android:id="@+id/ActivityMain_Button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/ActivityMain_TextView"
        android:layout_centerHorizontal="true"
        android:text="@string/click_me"/>
</RelativeLayout>

and here stands the corresponding Activity:

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

@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

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

@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

BDD tools

Cucumber

  • Define the .feature file:
Feature: Sum computation

  Scenario Outline: Sum 2 integers
    Given two int <a> and <b> to sum
    When computing sum
    Then it should be <sum>

    Examples:
      |  a |  b | sum |
      |  1 |  3 |   4 |
      | -1 | -3 |  -4 |
      | -1 |  3 |   2 |
  • Define the corresponding steps:
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:
@RunWith(Cucumber.class)
@CucumberOptions(
        features = "src/test/resources/"
)
public class SumTestRunner {
}
  • Relevant tools:

JGiven

public class JGivenSumTest extends SimpleScenarioTest<JGivenSumTest.TestSteps> {

    @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<TestSteps> {
        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

@RunWith(JUnitPlatform::class)
class SpekSumTest : Spek({

    given("two numbers a = 1 and b = 3") {
        val a: Int = 1
        val b: Int = 3

        on("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

@RunWith(AndroidJUnit4.class)
public class MainActivityTest {
    //region Rule
    @Rule
    public final ActivityTestRule<MainActivity> 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

    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'
@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:
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:
Feature: Main activity

  Scenario: 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:
@CucumberOptions(features = "features")
public class CucumberMainActivitySteps extends ActivityInstrumentationTestCase2<MainActivity> {

    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:
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

@RunWith(AndroidJUnit4.class)
public class EspressoJGivenMainActivityTest extends
        SimpleScenarioTest<EspressoJGivenMainActivityTest.Steps> {

    @Rule
    @ScenarioState
    public ActivityTestRule<MainActivity> 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<Steps> {
        @ScenarioState
        CurrentStep currentStep;

        @ScenarioState
        ActivityTestRule<MainActivity> 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

Nota Bene

A relevant combination of Dagger2 and mockito is already described in a previous post I wrote: http://roroche.github.io/AndroidStarter/

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

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].