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 :)
ActivityScenarioRule launches a given activity before the test starts and closes after the test. You can access the ActivityScenario instance via getScenario() . You may finish your activity manually in your test, it will not cause any problems and this rule does nothing after the test in such cases.
ActivityScenario provides APIs to start and drive an Activity's lifecycle state for testing. It works with arbitrary activities and works consistently across different versions of the Android framework. The ActivityScenario API uses Lifecycle. State extensively.
The Activity can be manually launched with launchActivity(Intent) , and manually finished with finishActivity() . If the Activity is running at the end of the test, the test rule will finish it.
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
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
}
}
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 toActivityScenarioRule
.If you need control over when to launch the Activity (eg you are using
ActivityTestRule(Class, false, false)
, useActivityScenario.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With