I have an Espresso test suite for UI tests that looks like this:
#RunWith(AndroidJUnit4.class)
public class SpecialUiTests {
#Rule
public final ActivityTestRule<SpecialActivity> activity
= new ActivityTestRule<>(SpecialActivity.class);
#Test
public void specialTest() {
...
}
...
}
The problem is, that activity expects a bundle, and crashes when it can't find the value it expects
public class SpecialActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
final String specialValue = getIntent().getBundleExtra(ARG_SPECIAL_BUNDLE)
.getString(KEY_SPECIAL_VALUE);
//Do something with specialValue <--- Crash
}
...
}
Can I set up a test rule and still pass the parameter (a bundle) the activity expects?
#Rule
public ActivityTestRule activityRule = new ActivityTestRule<>(
SpecialActivity.class,
true, // initialTouchMode
false); //Lazy launching
#Test
public void specialTest() {
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putString(SpecialActivity.KEY_SPECIAL_VALUE, "789");
intent.putExtra(SpecialActivity.ARG_SPECIAL_BUNDLE, bundle);
activityRule.launchActivity(intent);
onView(withId(R.id.special))
.check(matches(withText("789")));
}
Source: http://blog.sqisland.com/2015/04/espresso-21-activitytestrule.html
You can also override getActivityIntent() of your ActivityTestRule to create the Intent. This way, an Activity with the appropriate Intent is started automatically for all of your test methods. Sample:
#Rule
public ActivityTestRule<SpecialActivity> mActivity = new ActivityTestRule<SpecialActivity>(SpecialActivity.class) {
#Override
protected Intent getActivityIntent() {
final Context targetContext = InstrumentationRegistry.getTargetContext();
final Intent intent = new Intent(targetContext, SpecialActivity.class);
intent.putExtra("arg_one", 1);
return intent;
}
};
Related
I'm trying to understand how functional tests work in android applications. But I have difficulties.
In the case shown below, I check in database if there are any "work" in the database and if this exists, it goes to the WorkActivity activity, otherwise it goes to CalendarActivity.
MainActivity
public void btnNotes(View view) {
if (userDAO.existWork() == false) {
Intent intentRegisterWork = new Intent(this, WorkActivity.class);
startActivity(intentRegisterWork);
} else {
Intent intent = new Intent(this, CalendarActivity.class);
startActivity(intent);
}
MainActivityTest
#RunWith(AndroidJUnit4.class)
public class MainActivityTest {
#Rule
public ActivityTestRule<MainActivity> mMainActivityTestRule =
new ActivityTestRule<MainActivity>(MainActivity.class);
#Test
public void testChangeIntent() {
onView(withId(R.id.btnNotes));
Intent intent = new Intent (mMainActivityTestRule.getActivity(),WorkActivity.class);
mMainActivityTestRule.launchActivity(intent);
}
}
How do I test this?
I'm using intent to start my React-Native app, and I'm trying to find out how to get the variables I put on my intent in the react native code. Is this possible from within react-native or do I have to write some java code to get it?
the code I use to start the app :
Intent intent = new Intent(this, MainActivity.class);
Intent.putExtra("alarm",true);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Thanks!
Try this to get Intent params at react-native app.
In my native App, I use this code:
Intent launchIntent = getPackageManager().getLaunchIntentForPackage("com.my.react.app.package");
launchIntent.putExtra("test", "12331");
startActivity(launchIntent);
In react-native project, my MainActivity.java
public class MainActivity extends ReactActivity {
#Override
protected String getMainComponentName() {
return "FV";
}
public static class TestActivityDelegate extends ReactActivityDelegate {
private static final String TEST = "test";
private Bundle mInitialProps = null;
private final
#Nullable
Activity mActivity;
public TestActivityDelegate(Activity activity, String mainComponentName) {
super(activity, mainComponentName);
this.mActivity = activity;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
Bundle bundle = mActivity.getIntent().getExtras();
if (bundle != null && bundle.containsKey(TEST)) {
mInitialProps = new Bundle();
mInitialProps.putString(TEST, bundle.getString(TEST));
}
super.onCreate(savedInstanceState);
}
#Override
protected Bundle getLaunchOptions() {
return mInitialProps;
}
}
#Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new TestActivityDelegate(this, getMainComponentName());
}
}
In my first Container I get the param in this.props
export default class App extends Component {
render() {
console.log('App props', this.props);
//...
}
}
The complete example I found here:
http://cmichel.io/how-to-set-initial-props-in-react-native/
You can pass initial props as a bundle to the third parameter in the startReactApplication method, like so:
Bundle initialProps = new Bundle();
initialProps.putString("alarm", true);
mReactRootView.startReactApplication( mReactInstanceManager, "HelloWorld", initialProps );
See this answer for more details: https://stackoverflow.com/a/34226172/293280
I think this more correct way based on answer of #Eduardo Junior
class MainDelegate(activity: ReactActivity, mainComponentName: String?) :
ReactActivityDelegate(activity, mainComponentName) {
private var params: Bundle? = null
override fun onCreate(savedInstanceState: Bundle?) {
params = plainActivity.intent.extras
super.onCreate(savedInstanceState)
}
override fun onNewIntent(intent: Intent?): Boolean {
params = intent?.extras
return super.onNewIntent(intent)
}
override fun getLaunchOptions() = params
}
class MainActivity : ReactActivity() {
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
override fun getMainComponentName() = "example"
override fun createReactActivityDelegate(): ReactActivityDelegate {
return MainDelegate(this, mainComponentName)
}
}
I'm trying to figure out how to test onSavedInstance using the newer AndroidJunit4 and Activity Rules.
#RunWith(AndroidJUnit4.class)
public class MyViewActivityTest{
#Rule
public UiThreadTestRule uiThreadTestRule = new UiThreadTestRule();
#Rule
public ActivityTestRule<MyViewActivity> mActivityRule = new ActivityTestRule<>(MyViewActivity.class);
#UiThreadTest
#Test
public void testOnSavedIntanceState() {
uiThreadTestRule.runOnUiThread(new Runnable() {
#Override
public void run() {
Intent in = new Intent();
MyViewActivity activity = mActivityRule.launchActivity(in);
activity.finish();
activity.recreate();
}
});
}
I get an error not sure if I am barking up the right tree.
java.lang.IllegalStateException: Must be called from main thread
at android.app.Activity.recreate(Activity.java:4620)
You should be able to run the test with the annotation #UiThreadTest. It works for every test rule that extends UiThreadTestRule. In this case ActivityTestRule happens to do just that.
EDIT:
#UiThreadTest
#Test
public void testOnUIThread() {
// Test to run on UI thread
}
EDIT:
I just ran it again and remembered that you can't launch the activity on the UI thread. I made this and ran it without complications.
#RunWith(AndroidJUnit4.class)
public class TestActivity {
#Rule
public ActivityTestRule<MyViewActivity> activityRule = new ActivityTestRule<>(MyViewActivity.class, true, false);
#Test
public void testOnSavedInstanceState() throws Throwable {
activityRule.launchActivity(new Intent());
final Activity activity = activityRule.getActivity();
activityRule.runOnUiThread(new Runnable() {
#Override
public void run() {
activity.finish();
activity.recreate();
}
});
}
Here is the code.
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
MyClass myClass = new MyClass(this);
Cursor cursor = myClass.getCursor();
startManagingCursor(cursor);
}
}
public class MyClass Extends ImageButton {
private Context context;
public MyClass(Context context) {
this.context = context;
}
public Cursor getOncreate() {
Intent intent = new Intent();
intent.setClassName(MyClass.this,MyActivity.class);
context.startActivity(intent);
}
}
I want to start MyActivity from MyClass. I am getting NullPointer exception at
ctx.startActvity(intent);
Could you please suggest me how to make it correct.
try calling
startActivity(intent);
instead of
ctx.startActivity(intent);
Let one of the parameters of the start activity method be of type Context:
public void methodStartActivity(Context context) {
Intent intent = new Intent(context, MyActivity.class);
context.startActivity(intent);
}
Here's a BroadcastReceiver from my project, which I'm looking to unit test. When the user makes a phone call, it grabs the phone number, and sets up an intent to start a new activity, passing in the phone number.
public class OutgoingCallReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context xiContext, Intent xiIntent)
{
if (xiIntent.getAction().equalsIgnoreCase(Intent.ACTION_NEW_OUTGOING_CALL))
{
String phoneNum = xiIntent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Intent intent = new Intent(xiContext, MyActivity.class);
intent.putExtra("phoneNum", phoneNum);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
xiContext.startActivity(intent);
setResultData(null);
}
}
}
So far, my unit test looks like this:
public class OutgoingCallReceiverTest extends AndroidTestCase
{
private OutgoingCallReceiver mReceiver;
#Override
protected void setUp() throws Exception
{
super.setUp();
mReceiver = new OutgoingCallReceiver();
}
public void testStartActivity()
{
Intent intent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
intent.putExtra(Intent.EXTRA_PHONE_NUMBER, "01234567890");
mReceiver.onReceive(getContext(), intent);
}
}
This runs through the code, but I want my test to be able to check that the intent was sent out, and to check the phone number on it. How do I do this?
Can I also test that the phone call gets cancelled (because of the setResultData(null) line)?
corlettk pointed me at the MockContext object in Android, which does the trick. I've made a subclass of it, TestContext, which looks like this:
public class TestContext extends MockContext
{
private List<Intent> mReceivedIntents = new ArrayList<Intent>();
#Override
public String getPackageName()
{
return "com.mypackage.test";
}
#Override
public void startActivity(Intent xiIntent)
{
mReceivedIntents.add(xiIntent);
}
public List<Intent> getReceivedIntents()
{
return mReceivedIntents;
}
}
And my test case now looks like this:
public class OutgoingCallReceiverTest extends AndroidTestCase
{
private OutgoingCallReceiver mReceiver;
private TestContext mContext;
#Override
protected void setUp() throws Exception
{
super.setUp();
mReceiver = new OutgoingCallReceiver();
mContext = new TestContext();
}
public void testStartActivity()
{
Intent intent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
intent.putExtra(Intent.EXTRA_PHONE_NUMBER, "01234567890");
mReceiver.onReceive(mContext, intent);
assertEquals(1, mContext.getReceivedIntents().size());
assertNull(mReceiver.getResultData());
Intent receivedIntent = mContext.getReceivedIntents().get(0);
assertNull(receivedIntent.getAction());
assertEquals("01234567890", receivedIntent.getStringExtra("phoneNum"));
assertTrue((receivedIntent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0);
}
}
Matt,
Sounds like you need to mock-up a Context ... and then swap your methods over to accepting interfaces instead of concrete classes: public void onReceive(IContext c, IIntent i), just for the purposes of testing. But then the Context and Intent classes aren't yours are they... they're Android's... so you can't "just" make them implement your interfaces, so you'd have to "wrap" them in order to expose a your interface, which is RATHER a lot of code for not much gain. Very Yucky!!!
So I started to wonder if someone's been through all this before, and done the hard-yards for us... and tada: http://developer.android.com/reference/android/test/mock/package-summary.html
Cheers. Keith.
Since this question was asked mocking Frameworks have evolved pretty much. With mockito you can now mock not only interfaces but as well classes. So I would suggest to solve this problem by mocking a context and using ArgumentCapture:
import static org.mockito.Mockito.*;
public class OutgoingCallReceiverTest extends AndroidTestCase {
private OutgoingCallReceiver mReceiver;
private Context mContext;
#Override
protected void setUp() throws Exception {
super.setUp();
//To make mockito work
System.setProperty("dexmaker.dexcache",
mContext.getCacheDir().toString());
mReceiver = new OutgoingCallReceiver();
mContext = mock(Context.class);
}
public void testStartActivity() {
Intent intent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
intent.putExtra(Intent.EXTRA_PHONE_NUMBER, "01234567890");
mReceiver.onReceive(mContext, intent);
assertNull(mReceiver.getResultData());
ArgumentCaptor<Intent> argument = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(1)).startActivity(argument.capture());
Intent receivedIntent = argument.getValue();
assertNull(receivedIntent.getAction());
assertEquals("01234567890", receivedIntent.getStringExtra("phoneNum"));
assertTrue((receivedIntent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0);
}
}