I using Roboguice to test application. I have two Modules for the app:
ProductionModule - used when application is not under test
TestingModule - used when application is under test
Those Modules are parameters for Injector which created in OnCreate() method of APPLICATION.
So how can i figure out, if the app is under test?
Is there a way to manage an APPLICATION lifecycle in ActivityInstrumentationTestCase2, so i can do some stuff BEFORE APPLICATION onCreate() method?
PS: I need to test activities after that so i using ActivityInstrumentationTestCase2 and i using custom TestRunner to generate reports.
Application on create is called before any of your tests run, this is because to run tests your application already has to exist.
You can however call application oncreate yourself within a test. You can cast an applicationcontext to your application and then call the onCreate method. I have had to do this in the past in order to clear any state in the application.
You can send extra params to the activity from JUnit in the setUp() like this:
#Override
public void setUp() {
Intent intent = new Intent();
intent.putExtra("debug", true);
setActivityIntent(intent);
mActivity = getActivity();
}
And inside the real Activity you can check it like this inside onCreate():
Bundle extras = getIntent().getExtras();
if(extras!=null){
boolean is= (Boolean)extras.get("debug");
if(is){
// here you are in debug mode
}
}
Related
I have a test that I want to get working in espresso to fetch all (100+) Activities in my app and assert that they start an Intent to launch a dialog activity. The test works perfecting on the first activity in the list (or if there is just one activity), but fails on the second activity, because espresso tries to cast it to the first activity.
I have to use ActivityTestRule and not launch the activity directly from the context, or Intendo intended assertions don't work.
Most of the documentation shows espresso being setup with one activity per test, which is not possible in my case, because I'm loading the list of activities dynamically.
Espresso is saving some kind of internal state that I can't figure out how to clear. Does anyone know how to clear it between assertions in a for loop?
Or is there another class other than ActivityTestRule without the internal state that also works with intended?
Here is an example of what I'm trying to test:
ActivityInfo[] activities =
appContext
.getPackageManager()
.getPackageInfo(appContext.getPackageName(), PackageManager.GET_ACTIVITIES)
.activities;
for (ActivityInfo activityInfo : activities) {
String className = activityInfo.name;
ActivityTestRule activityTestRule =
new ActivityTestRule<>((Class<Activity>) Class.forName(className));
Intent intent = new Intent()
.setClassName(appContext, className)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activityActivityTestRule.launchActivity(intent);
intended(hasComponent(ExpectedDialogActivity.class.getName()));
activityActivityTestRule.finishActivity();
}
I have a UI test which clicks a button, and then launch a new Activity in its onClickListener. The test checks whether expected intent is sent or not.
My problem is, I want to test whether expected intent is sent without actually launching the activity. Because I found that new activity initializes its state, and it makes subsequent tests flaky.
I know there are two Espresso Intents API, which are intended and intending, but both fail to meet my needs. intended API actually launches the target activity, and intending API doesn't launch the activity, but it calls onActivityResult callback which I don't want either. Because I'm afraid that code inside onActivityResult may cause another flakiness.
Also intending doesn't assert whether matching intent is sent. It just calls onActivityResult callback when matching intent is found, which means I have to check whether onActivityResult is called or not!
Is there a clean way to achieve what I want?
If you want to test whether expected intent is sent without actually launching the activity you can do it by capturing the intent with an activityResult and then catching the activity :
Intent intent = new Intent();
ActivityResult intentResult = new ActivityResult(Activity.RESULT_OK,intent);
intending(anyIntent()).respondWith(intentResult);
onView(withId(R.id.view_id_to_perform_clicking)).check(matches(isDisplayed())).perform(click());
intended(allOf(hasComponent(ActivityToBeOpened.class.getName())));
This would catch any attempt of launching ActivityToBeOpened. If you want to be more specific you can also catch an intent with Extras:
intended(allOf(hasComponent(ActivityToBeOpened.class.getName()), hasExtra("paramName", "value")));
Hope that helps.
Espresso's Intents class is a concise and handy api, but when it doesn't meet your needs, there is an alternative. If you use AndroidJUnit4 test runner, you can get Instrumentaion instance using InstrumentationRegistry.getInstrumentation(), and then you can add Instrumentation.ActivityMonitor instance.
Instrumentation.ActivityMonitor am = new Instrumentation.ActivityMonitor("YOUR_ACTIVITY", null, true);
InstrumentationRegistry.getInstrumentation().addMonitor(am);
onView(withId(R.id.view_id_to_perform_clicking)).check(matches(isDisplayed())).perform(click());
assertTrue(InstrumentationRegistry.getInstrumentation().checkMonitorHit(am, 1));
The third parameter of ActivityMonitor constructor tells we want to block activity launching. Note that this approach has its limitation. In contrast to Espresso Intents' rich Matcher support, You can not set multiple condition for ActivityMonitor.
You can find several samples in ApiDemos, especially in ContactsSelectInstrumentation class.
Actually, you can block any intent to launch an external or your own activity but still use the rich Espresso Intents API:
Instrumentation.ActivityMonitor soloMonitor = solo.getActivityMonitor();
instrumentation.removeMonitor(soloMonitor);
IntentFilter filter = null;
// Block any intent
Instrumentation.ActivityMonitor monitor = instrumentation.addMonitor(filter, null, true);
instrumentation.addMonitor(soloMonitor);
// User action that results in an external browser activity being launched.
user.clickOnView(system.getView(R.id.callButton));
instrumentation.waitForIdleSync();
Intents.intended(Matchers.allOf(
IntentMatchers.hasAction(Matchers.equalTo(Intent.ACTION_VIEW)),
IntentMatchers.hasData(Matchers.equalTo(Uri.parse(url))),
IntentMatchers.toPackage(chromePackage)));
instrumentation.removeMonitor(monitor);
You able to do that because Espresso Intents still records every Intent with IntentMonitor callback even if you block them. Look at the source code of Espresso Intents on how they do that.
If you use Robotium Solo framework you need to move your own ActivityMonitor before their one. Otherwise just skip the lines related to this.
Is it possible to trace which Activity is opened after a certain button is pressed?I have a test in which, when a button is clicked / pressed, it sends a request to the server. Till the time the request is sent, it opens an Activity. To verify the successful execution of the test, I need to check what is the open Activity. Example of my test:
Check which Intent is opened in Espresso ---
private void startTest() {
recreateAuthData(InstrumentationRegistry.getTargetContext(), "d78269d9-9e00-4b8d-9242-815204b0a2f6", "3f32da21-914d-4adc-b6a1-891b842a2972");
InstrumentationRegistry.getTargetContext().getSharedPreferences(ActivitySplashScreen.class.getSimpleName(),
Context.MODE_PRIVATE).edit().putInt(ActivitySplashScreen.PROPERTY_APP_VERSION, ActivitySplashScreen.getAppVersion(InstrumentationRegistry.getTargetContext())).commit();
InstrumentationRegistry.getTargetContext().getSharedPreferences(ActivitySplashScreen.class.getSimpleName(),
Context.MODE_PRIVATE).edit().putString(ActivitySplashScreen.PROPERTY_REG_ID, "testKey").commit();
mActivityRule.launchActivity(setIntent());
// inputPinCode("2794");
}
#Test
public void testIdent() {
startTest();
onView(withText("ПРО")).perform(click());
putDelay(500);
onView(withId(R.id.get_pro)).perform(click());
onView(withText("Авторизация по паспортным данным")).perform(click());
putDelay(500);
closeSoftKeyboard();
onView(withId(R.id.btn_goto_passport)).perform(click());
onView(withHint("Серия и номер паспорта")).perform(replaceText("9894657891"));
onView(withHint("Дата выдачи паспорта")).perform(replaceText("17032014"));
onView(withHint("Дата рождения")).perform(replaceText("31091994"));
onView(withHint("СНИЛС")).perform(replaceText("54665285919"));
putDelay(500);
Log.d("TestWidget", hasComponent(hasShortClassName("ActivityMain")).toString());
onView(withId(R.id.btn_next)).perform(click());
// some code which check which activity is display now
putDelay(500);
}
To actually match a started activity with Espresso intents you need to check for the component of the new intent:
intended(hasComponent(NewActivity.class.getName()));
Make sure to call Intents.init() in the setup and Intents.release() in teardown to be able to record intents with Espresso.
Whether it is possible to trace which the Activity opened after
pressing the button?
Check espresso-intents library:
Configuration
Add to your app/build.gradle these lines:
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
NOTICE: espresso-intents won't run without espresso-core, runner or rules libs.
You may also need to change ActivityTestRule<> to IntentsTestRule as it is described here:
IntentsTestRule
Use IntentsTestRule instead of ActivityTestRule when using
Espresso-Intents. IntentsTestRule makes it easy to use
Espresso-Intents APIs in functional UI tests. This class is an
extension of ActivityTestRule, which initializes Espresso-Intents
before each test annotated with #Test and releases Espresso-Intents
after each test run. The activity will be terminated after each test
and this rule can be used in the same way as ActivityTestRule.
From:
https://google.github.io/android-testing-support-library/docs/espresso/intents/
Example code (click on button to launch new activity)
Here's a solution using espresso-intents for similar problem:
An example test with intent stubbing:
#Test
public void testActivityResultIsHandledProperly() {
// Build a result to return when a particular activity is launched.
Intent resultData = new Intent();
String phoneNumber = "123-345-6789";
resultData.putExtra("phone", phoneNumber);
ActivityResult result = new ActivityResult(Activity.RESULT_OK, resultData);
// Set up result stubbing when an intent sent to "contacts" is seen.
intending(toPackage("com.android.contacts")).respondWith(result));
// User action that results in "contacts" activity being launched.
// Launching activity expects phoneNumber to be returned and displays it on the screen.
user.clickOnView(system.getView(R.id.pickButton));
// Assert that data we set up above is shown.
assertTrue(user.waitForText(phoneNumber));
}
From:
https://developer.android.com/reference/android/support/test/espresso/intent/Intents.html
Additional resources:
[Android Developers] Espresso Intents Reference
[Github || Google Samples] Basic sample for Espresso Intents
[Github] Android Espresso Intent Sample
Testing for Android Intents using Espresso
[Gist] Example of how to use espresso-intents in Android tests - source code for link above
I created Android app with Xamarin Forms. I use Xamarin UI Test for testing life cycle app. I need events for app - OnStart, OnSleep, OnResume.
My algorithm:
1. Start test, check UI (OnStart).
2. App goes to the background(OnSleep). For this I use this code:
in Activity
[Export("GoOut")]
public void GoOut()
{
var uri = Uri.Parse("http://www.google.ru");
var intent = new Intent(Intent.ActionView, uri);
StartActivity(intent);
}
in Test
app.Invoke("GoOut");
It work. Now I need to go back to the app. I want to trigger an event OnResume and testing my app. How to do it?
I tried this:
app.Invoke("GoOut");
app.Back();
and this
//in Activity
[Export("Back")]
public void Back()
{
this.OnBackPressed();
}
//in my test
app.Invoke("GoOut");
app.Invoke("Back");
But it not work for me.how Can I go back to the application from browser?
It was easy:
ConfigureApp.Android.StartApp(AppDataMode.DoNotClear);
I am implementing Localytics.com useage statistics in my Android app.
I am still just testing.
I note that just starting my app and then immediately exiting will register two sessions (as they are called by Localytics) in the live statistics.
I have followed the guidelines in Android Integration.
My app consists of a main Class of the TabActivity type. This TabActivity holds two tabs in which I display two other activities. Like this:
setContentView(R.layout.main);
mTabHost = getTabHost();
Context ctx = getApplicationContext();
Intent addTodo = new Intent(ctx, AddTodo.class);
Intent listTodos = new Intent(ctx, ListTodos.class);
mTabHost.addTab(mTabHost.newTabSpec("tab_1").setIndicator("New note").setContent(addTodo));
mTabHost.addTab(mTabHost.newTabSpec("tab_2").setIndicator("Saved notes").setContent(listTodos));
mTabHost.setCurrentTab(0);
I instantiate the Localytics object in all three Activities like this:
this.localyticsSession = new LocalyticsSession(
this.getApplicationContext(),
"identifier");
this.localyticsSession.open();
So, the above code lines are repeated in each of the three Activities.
In the TabHost Activity (only in this Activity) I then follow the instantiation with a
this.localyticsSession.upload();
And then (only in the TabHost Activity) I have these to finish things off:
public void onPause()
{
this.localyticsSession.close();
super.onPause();
}
public void onDestroy()
{
this.localyticsSession.upload();
super.onDestroy();
}
Any suggestions on how to make my code generate just one session per app-launch?
I work for Localytics and would be glad to work with you to see this to completion.
It sounds like the close() call is somehow getting missed but that doesn't seem right based on your code sample. Whenever you create a new session and then call open it should simply re-attach to your existing session. This is even more strange because simply launching and exiting the app should only instantiate the object once. What LaunchMode are you running?
A helpful thing to debug this would be to look at the log. Start and exit your app in the emulator and take a look at the logcat output. Localytics will explain what it is doing and then we can debug it better.
Please feel free to contact us directly: support#localytics.com and we'll work with you on this.
-- Henry