Is Robolectric able to assert that methods have been invoked? - android

I've got a button defined in my layout as follows :
<Button
android:id="#+id/speakButton"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:text="#string/speak"
android:onClick="speak"
/>
The onClick binds to a method in my activity, such as :
public void speak(View v)
{
// Do my stuff here
}
Using Robolectric, I'm able to create a simple test class for that activity, I'd like to know if its possible that I could have a test that invokes the button, and ensures the method in my activity was invoked OK.
(I've got a whole bunch of buttons throughout my app, so intending to have tests to ensure they are wired up correctly, unless anyone has any suggestions as to why I shoudln't bother)
#RunWith(RobolectricTestRunner.class)
public class MyActivityTest
{
private MyActivitymActivity;
private Button speakButton;
#Before
public void setUp() throws Exception
{
mActivity = new MyActivity();
mActivity.onCreate(null);
speakButton = (Button) mActivity.findViewById(com.jameselsey.apps.androidsam.R.id.speakButton);
}
#Test
public void testButtonsVisible()
{
assertThat(speakButton.getVisibility(), equalTo(View.VISIBLE));
}
#Test
public void buttonsInvokeIntendedMethods()
{
// Unsure how to implement this test
}
}

I've never used them but I believe you can do this with the TouchUtils class. Here is a link to the Android TouchUtils docs. In particular you should look at the clickView method.

Related

How to test swiping between Activities in Espresso?

I am creating a test in Espresso that calls swipeRight() to go to the MainActivity where behavior is set before returning to ConsumerSettingsActivity to test Views. However, the issue is that after calling the swipe, I get a NoActivityResumedException that points to the second tested line with R.id.mode_text.
#RunWith(AndroidJUnit4.class)
public class ConsumerSettingsActivityTest {
private ConsumerSettingsActivity mConsumerSettingsActivity;
#Rule
public ActivityTestRule<ConsumerSettingsActivity> mActivityTestRule =
new ActivityTestRule<>(ConsumerSettingsActivity.class);
#Before
public void initialize() {
mConsumerSettingsActivity = mActivityTestRule.getActivity();
}
#Test
public void checkCorrectButtonsAreClickableInPhotoModeThenReturnToMainActivity() {
// swipe to MainActivity and set behavior
onView(allOf(withId(R.id.settings_top_bar), isDisplayed())).perform(swipeRight());
onView(withId(R.id.mode_text)).perform(click());
onView(withId(R.id.photo_mode)).perform(click());
// test ratio Views
onView(withId(R.id.ratio_full)).check(matches(isClickable()));
onView(withId(R.id.ratio_full)).perform(click());
onView(withId(R.id.ratio_square)).check(matches(isClickable()));
onView(withId(R.id.ratio_square)).perform(click());
...
}
}
Short answer: MainActivity was called without being created.
Long answer: When ConsumerSettingsActivity called swipe to change to MainActivity, the actual swiping called finish(). Since MainActivity was not created, a NoActivityResumedException was thrown. To solve this, the test was changed to start with MainActivity, where it would then swipe to ConsumerSettingsActivity. Also, I did not need to make the thread sleep to be able to swipe between Activities.

Android Espresso Testing - Checking Buttons

So I've written a basic test for my homescreen right now that checks if the three buttons on the homescreen are clickable. I was wondering how I would check to see if each of the buttons go to the right activity when they are clicked.
#RunWith(AndroidJUnit4.class)
#SmallTest
public class HomeScreenTest {
#Rule
public ActivityTestRule<StartActivity> mActivityRule = new ActivityTestRule<>(StartActivity.class);
#Test
public void testButton() {
Espresso.onView(withId(R.id.event_button)).check(matches(isClickable()));
Espresso.onView(withId(R.id.navigation_button)).check(matches(isClickable()));
Espresso.onView(withId(R.id.alarm_button)).check(matches(isClickable()));
}
}
two options for you.
assert on the next activity component to see it is correctly showing. or,
You probably need to test the Intent that you are sending through button clicks.
see https://google.github.io/android-testing-support-library/docs/espresso/intents/
#Test
public void validateIntentSentToPackage() {
user.clickOnView(system.getView(R.id.callButton));
intended(toPackage("com.android.phone"));
}

Testing Android Components With Espresso

I have a number of custom Android components and wish to test them using Espresso. As an Espresso test runs against an Activity I added a simple Activity class to the androidTest directory which programatically creates a view with my component in it ready for testing.
For example if I'm testing a MyView component then my Espresso test class might look something like this:
public class MyViewTest extends ActivityInstrumentationTestCase2<MyViewTestActivity>
{
private MyViewTestActivity activity;
public MyViewTest()
{
super(MyViewTestActivity.class);
}
#Override
protected void setUp() throws Exception
{
super.setUp();
setActivityInitialTouchMode(false);
// Launches the activity
activity = getActivity();
}
// Ensure that expected items are present
public void testLayout()
{
onView(withId(activity.view.getId())).check(matches(isDisplayed()));
}
}
with a simple MyViewTestActivity as follows:
public class MyViewTestActivity extends Activity
{
private static final Random RANDOM = new Random();
public LinearLayout layout;
public MyView view;
#Override
public void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
layout = new LinearLayout(this);
layout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT));
view = new MyView(this);
view.setId(RANDOM.nextInt());
view.setItem("Test text");
layout.addView(view);
setContentView(layout);
}
}
My problem is I appear to need to add the test Activity class, in this case MyViewTestActivity, to the main AndroidManifest.xml to make this work, otherwise I receive an Unable to resolve activity for: Intent... error when attempting to run the test. However I now have test activities in the main manifest, which seems like a bad thing to do.
How can I set up test-specific activities which are included in the test manifest but not the main one?
I'm using the gradle-based build system for Android.
Yes, you have to add MyViewTestActivity to the main AndroidManifest.xml.
If you look at the ActivityInstrumentationTestCase2 source code, you will see that getActivity() looks for the tested Activity in the target (i.e., the app under test) context.
Here is the relevant part of the source code.
#Override
public T getActivity() {
// ...
final String targetPackage = getInstrumentation().getTargetContext().getPackageName();
// ...
a = launchActivity(targetPackage, mActivityClass, null);
// ...
setActivity(a);
// ...
}
What I do in my projects is that I create a generic TestingActivity, put it in a .test package in the target app, and use it for all GUI-component testing. It is not ideal, but I never had any problem with this approach.

NoClassDefFoundError FragmentActivity test

I'm trying to create a simple Android Activity test that checks that a new Activity is started when a button is clicked. The code:
public class LoginActivityTest extends ActivityInstrumentationTestCase2<LoginActivity> {
public LoginActivityTest() {
super(LoginActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
setActivityInitialTouchMode(false);
mActivity = getActivity();
mLoginButton = (Button) mActivity.findViewById(R.id.login_button);
mSkipButton = (Button) mActivity.findViewById(R.id.skip_button);
}
{...}
public void testSkipButton() {
Instrumentation.ActivityMonitor monitor =
getInstrumentation().addMonitor(
"com.mycompany.myproject.view.QuestionsActivity", null, false);
mActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
mSkipButton.requestFocus();
mSkipButton.performClick();
}
});
QuestionsActivity nextActivity =
(QuestionsActivity) getInstrumentation().waitForMonitorWithTimeout(monitor, 20);
assertNotNull(nextActivity);
nextActivity.finish();
}
private LoginActivity mActivity;
private Button mLoginButton;
private Button mSkipButton;
}
When I reach waitForMonitorWithTimeout() a NoClassDefFoundException is raised.
It's important to take into account that QuestionsActivity (the activity that should be launched) is a FragmentActivity, not an Activity, but FragmentActivity inherits from Activity, so I really don't understand what's happening there. Maybe InstrumentationTest cannot deal with Fragments or FragmentActivities.
mActivity is the current Activity that it's being tested, and it's a pure Activity.
Please, help!
The issue was that I added a reference to the support library from the testing project. See this link:
FragmentActivity can not be tested via ActivityInstrumentationTestCase2
Now tests work fine :)

easy android unit test fail

I have a simple activity with only one edittext which was set to "http://" in xml. Based on google tutorial, I wrote some unit tests for status check. I want to practice unit test and pass all tests. But I cannot pass testStateDestroy() and testStatePause() (log shows mUrlView=="changed"). The code below is very easy, did I miss something? Thank you in advance.
public class MainActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
private Activity mActivity;
private EditText mUrlView;
public MainActivityTest() {
super("au.com.crystalfish.safeshare.activity", MainActivity.class);
}
protected void setUp() throws Exception {
super.setUp();
mActivity = this.getActivity();
mUrlView = (EditText) mActivity.findViewById(au.com.crystalfish.safeshare.R.id.url);
}
public void testPreconditions() {
assertNotNull(mActivity);
assertEquals(mUrlView.getText().toString(), "http://");
}
#UiThreadTest
public void testRotate() {
mUrlView.setText("changed");
mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
assertEquals(mUrlView.getText().toString(), "changed");
}
#UiThreadTest
public void testStateDestroy() {
mUrlView.setText("changed");
assertEquals(mUrlView.getText().toString(), "changed");
mActivity.finish();
mActivity = this.getActivity();
assertEquals(mUrlView.getText().toString(), "http://"); <===========should be "http://" since it is a new activity
}
#UiThreadTest
public void testStatePause() {
Instrumentation mInstr = this.getInstrumentation();
mInstr.callActivityOnPause(mActivity);
mUrlView.setText("changed");
mInstr.callActivityOnResume(mActivity);
assertEquals(mUrlView.getText().toString(), "http://");<======should be "http://" since the text should bot be changed when the activity was paused
}
}
Well, for your testStatePause() test I think that may be valid. Even if the activity is paused you are still free to modify it however you want since you still have a reference to it (or one of its textViews at least. In a test like this, you should change the value of the textView in the actual activity's onPause method, then use your testStatePause test to verify that onPause was called and did its job correctly, then do the same for onResume().
I'm not really familiar with Android's testing framework but maybe a similar issue is happening with your testStateDestroy() test. Your mUrlView could still be pointing to the old, finished Activity. The old activity is no longer valid but its widgets might be.

Categories

Resources