How to putExtra data using newest ActivityScenarioRule/ActivityScenario?[Espresso/Jetpack]

Viewed 7421

There were added new classes to test Activities, such as ActivityScenarioRule and ActivityScenario And there is no documentation how to use them when you want put some extra data.

For now I've found two working ways, in short:

1) Use ActivityScenarioRule and put your extra in the method with @Before annotation using #onActivity. But, there would be unnecessary data for some test cases.

2) Second, use ActivityScenario with #launch(Class activityClass) when you dont need extra and #launch(Intent startActivityIntent) when you want put extra. But, this time I loose an ability to use #onActivity where can be placed common for all test cases

P.S. this is my first time of android testing :)

4 Answers

I was able to solve this problem by using a static Intent in my test class. That way, my intent is instantiated before I pass it to my ActivityScenarioRule.

My espresso test class:

package your.package.tests.example;

import android.content.Intent;
import android.os.Bundle;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.espresso.assertion.ViewAssertions;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.ViewMatchers.*;

@RunWith(AndroidJUnit4.class)
@LargeTest
public class TestExample {

    static Intent intent;
    static {
        intent = new Intent(ApplicationProvider.getApplicationContext(), YourActivity.class);
        Bundle bundle = new Bundle();
        bundle.putString("YOUR_BUNDLE_TAG", "YOUR_VALUE");
        intent.putExtras(bundle);
    }

    @Rule
    public ActivityScenarioRule<YourActivity> activityScenarioRule = new ActivityScenarioRule<>(intent);

    @Test
    public void yourTest() {
        onView(withId(R.id.simple_text)).check(ViewAssertions.matches(isDisplayed()));
    }
}

My espresso gradle dependencies:

dependencies {
    // Testing-only dependencies
    androidTestImplementation 'androidx.test:core:1.2.1-alpha02'
    androidTestImplementation 'androidx.test:core-ktx:1.2.1-alpha02'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2-alpha02'
    androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.2-alpha02'
    androidTestImplementation 'androidx.test:runner:1.3.0-alpha02'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha02'
    androidTestImplementation 'androidx.test.espresso:espresso-intents:3.3.0-alpha02'
    ...
}

References: Medium tutorial how to use espresso tests: https://medium.com/@heitorcolangelo/testes-no-android-com-espresso-parte-1-8d739672a235

Personally i do it like that

lateinit var activityScenario: ActivityScenario<MyActivity>

@After
fun tearDown() {
    activityScenario.close()
}

@Test
fun myTest() {
    val intent = Intent(ApplicationProvider.getApplicationContext(), MyActivity::class.java)
    intent.putExtra("key", "value") //obviously use a const for key
    activityScenario = ActivityScenario.launch<MyActivity>(intent)

    activityScenario.onActivity {
        //whatever you like
    }
}

What I've decided to do - marginally simpler than the other answers.

lateinit var scenario: ActivityScenario<MyActivity>

@After
fun cleanup() {
    scenario.close()
}

@Test
fun myTest() {
    val intent = Intent(ApplicationProvider.getApplicationContext(), MyActivity::class.java)
        .putExtra("key", "value")
    scenario = launchActivity(intent)
    // Your test code goes here.
}

You'll also need to add the below dependency to your gradle file

androidTestImplementation 'androidx.test:core-ktx:1.2.0'

Reference: https://medium.com/stepstone-tech/better-tests-with-androidxs-activityscenario-in-kotlin-part-1-6a6376b713ea

From the Java doc on the deprecated ActivityTestRule it mentions:

For simple cases where you want to launch the Activity before each test and tear it down after each test (eg you are using ActivityTestRule(Class)), convert directly to ActivityScenarioRule.

If you need control over when to launch the Activity (eg you are using ActivityTestRule(Class, false, false), use ActivityScenario.launch. Its recommended to wrap the launch in a try-block, so the Activity is closed automatically.

To convert cases that need to pass custom data in the Intent, I've gone with the following:

final Context targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
final Intent intent = new Intent(targetContext, MyActivity.class);
intent.putExtra("foo", new Foo());

try (final ActivityScenario<MyActivity> scenario = ActivityScenario.launch(intent)) {
    /* Here you have access to scenario.onActivity() */
}

But, this time I loose an ability to use #onActivity where can be placed common for all test cases

I've decided to pull out common set-up into helper methods that I can call within the test as needed. Not as neat as a @Before block, but does the job.

Related