1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.test; 18 19 import android.app.Activity; 20 import android.app.Application; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.ActivityInfo; 25 import android.os.Bundle; 26 import android.os.IBinder; 27 import android.test.mock.MockApplication; 28 import android.view.Window; 29 import android.util.Log; 30 31 32 33 /** 34 * This class provides isolated testing of a single activity. The activity under test will 35 * be created with minimal connection to the system infrastructure, and you can inject mocked or 36 * wrappered versions of many of Activity's dependencies. Most of the work is handled 37 * automatically here by {@link #setUp} and {@link #tearDown}. 38 * 39 * <p>If you prefer a functional test, see {@link android.test.ActivityInstrumentationTestCase}. 40 * 41 * <p>It must be noted that, as a true unit test, your Activity will not be running in the 42 * normal system and will not participate in the normal interactions with other Activities. 43 * The following methods should not be called in this configuration - most of them will throw 44 * exceptions: 45 * <ul> 46 * <li>{@link android.app.Activity#createPendingResult(int, Intent, int)}</li> 47 * <li>{@link android.app.Activity#startActivityIfNeeded(Intent, int)}</li> 48 * <li>{@link android.app.Activity#startActivityFromChild(Activity, Intent, int)}</li> 49 * <li>{@link android.app.Activity#startNextMatchingActivity(Intent)}</li> 50 * <li>{@link android.app.Activity#getCallingActivity()}</li> 51 * <li>{@link android.app.Activity#getCallingPackage()}</li> 52 * <li>{@link android.app.Activity#createPendingResult(int, Intent, int)}</li> 53 * <li>{@link android.app.Activity#getTaskId()}</li> 54 * <li>{@link android.app.Activity#isTaskRoot()}</li> 55 * <li>{@link android.app.Activity#moveTaskToBack(boolean)}</li> 56 * </ul> 57 * 58 * <p>The following methods may be called but will not do anything. For test purposes, you can use 59 * the methods {@link #getStartedActivityIntent()} and {@link #getStartedActivityRequest()} to 60 * inspect the parameters that they were called with. 61 * <ul> 62 * <li>{@link android.app.Activity#startActivity(Intent)}</li> 63 * <li>{@link android.app.Activity#startActivityForResult(Intent, int)}</li> 64 * </ul> 65 * 66 * <p>The following methods may be called but will not do anything. For test purposes, you can use 67 * the methods {@link #isFinishCalled()} and {@link #getFinishedActivityRequest()} to inspect the 68 * parameters that they were called with. 69 * <ul> 70 * <li>{@link android.app.Activity#finish()}</li> 71 * <li>{@link android.app.Activity#finishFromChild(Activity child)}</li> 72 * <li>{@link android.app.Activity#finishActivity(int requestCode)}</li> 73 * </ul> 74 * 75 * @deprecated Write 76 * <a href="{@docRoot}training/testing/unit-testing/local-unit-tests.html">Local Unit Tests</a> 77 * instead. 78 */ 79 @Deprecated 80 public abstract class ActivityUnitTestCase<T extends Activity> 81 extends ActivityTestCase { 82 83 private static final String TAG = "ActivityUnitTestCase"; 84 private Class<T> mActivityClass; 85 86 private Context mActivityContext; 87 private Application mApplication; 88 private MockParent mMockParent; 89 90 private boolean mAttached = false; 91 private boolean mCreated = false; 92 ActivityUnitTestCase(Class<T> activityClass)93 public ActivityUnitTestCase(Class<T> activityClass) { 94 mActivityClass = activityClass; 95 } 96 97 @Override getActivity()98 public T getActivity() { 99 return (T) super.getActivity(); 100 } 101 102 @Override setUp()103 protected void setUp() throws Exception { 104 super.setUp(); 105 106 // default value for target context, as a default 107 mActivityContext = getInstrumentation().getTargetContext(); 108 } 109 110 /** 111 * Start the activity under test, in the same way as if it was started by 112 * {@link android.content.Context#startActivity Context.startActivity()}, providing the 113 * arguments it supplied. When you use this method to start the activity, it will automatically 114 * be stopped by {@link #tearDown}. 115 * 116 * <p>This method will call onCreate(), but if you wish to further exercise Activity life 117 * cycle methods, you must call them yourself from your test case. 118 * 119 * <p><i>Do not call from your setUp() method. You must call this method from each of your 120 * test methods.</i> 121 * 122 * @param intent The Intent as if supplied to {@link android.content.Context#startActivity}. 123 * @param savedInstanceState The instance state, if you are simulating this part of the life 124 * cycle. Typically null. 125 * @param lastNonConfigurationInstance This Object will be available to the 126 * Activity if it calls {@link android.app.Activity#getLastNonConfigurationInstance()}. 127 * Typically null. 128 * @return Returns the Activity that was created 129 */ startActivity(Intent intent, Bundle savedInstanceState, Object lastNonConfigurationInstance)130 protected T startActivity(Intent intent, Bundle savedInstanceState, 131 Object lastNonConfigurationInstance) { 132 assertFalse("Activity already created", mCreated); 133 134 if (!mAttached) { 135 assertNotNull(mActivityClass); 136 setActivity(null); 137 T newActivity = null; 138 try { 139 IBinder token = null; 140 if (mApplication == null) { 141 setApplication(new MockApplication()); 142 } 143 ComponentName cn = new ComponentName(mActivityClass.getPackage().getName(), 144 mActivityClass.getName()); 145 intent.setComponent(cn); 146 ActivityInfo info = new ActivityInfo(); 147 CharSequence title = mActivityClass.getName(); 148 mMockParent = new MockParent(); 149 String id = null; 150 151 newActivity = (T) getInstrumentation().newActivity(mActivityClass, mActivityContext, 152 token, mApplication, intent, info, title, mMockParent, id, 153 lastNonConfigurationInstance); 154 } catch (Exception e) { 155 Log.w(TAG, "Catching exception", e); 156 assertNotNull(newActivity); 157 } 158 159 assertNotNull(newActivity); 160 setActivity(newActivity); 161 162 mAttached = true; 163 } 164 165 T result = getActivity(); 166 if (result != null) { 167 getInstrumentation().callActivityOnCreate(getActivity(), savedInstanceState); 168 mCreated = true; 169 } 170 return result; 171 } 172 173 @Override tearDown()174 protected void tearDown() throws Exception { 175 176 setActivity(null); 177 178 // Scrub out members - protects against memory leaks in the case where someone 179 // creates a non-static inner class (thus referencing the test case) and gives it to 180 // someone else to hold onto 181 scrubClass(ActivityInstrumentationTestCase.class); 182 183 super.tearDown(); 184 } 185 186 /** 187 * Set the application for use during the test. You must call this function before calling 188 * {@link #startActivity}. If your test does not call this method, 189 * @param application The Application object that will be injected into the Activity under test. 190 */ setApplication(Application application)191 public void setApplication(Application application) { 192 mApplication = application; 193 } 194 195 /** 196 * If you wish to inject a Mock, Isolated, or otherwise altered context, you can do so 197 * here. You must call this function before calling {@link #startActivity}. If you wish to 198 * obtain a real Context, as a building block, use getInstrumentation().getTargetContext(). 199 */ setActivityContext(Context activityContext)200 public void setActivityContext(Context activityContext) { 201 mActivityContext = activityContext; 202 } 203 204 /** 205 * This method will return the value if your Activity under test calls 206 * {@link android.app.Activity#setRequestedOrientation}. 207 */ getRequestedOrientation()208 public int getRequestedOrientation() { 209 if (mMockParent != null) { 210 return mMockParent.mRequestedOrientation; 211 } 212 return 0; 213 } 214 215 /** 216 * This method will return the launch intent if your Activity under test calls 217 * {@link android.app.Activity#startActivity(Intent)} or 218 * {@link android.app.Activity#startActivityForResult(Intent, int)}. 219 * @return The Intent provided in the start call, or null if no start call was made. 220 */ getStartedActivityIntent()221 public Intent getStartedActivityIntent() { 222 if (mMockParent != null) { 223 return mMockParent.mStartedActivityIntent; 224 } 225 return null; 226 } 227 228 /** 229 * This method will return the launch request code if your Activity under test calls 230 * {@link android.app.Activity#startActivityForResult(Intent, int)}. 231 * @return The request code provided in the start call, or -1 if no start call was made. 232 */ getStartedActivityRequest()233 public int getStartedActivityRequest() { 234 if (mMockParent != null) { 235 return mMockParent.mStartedActivityRequest; 236 } 237 return 0; 238 } 239 240 /** 241 * This method will notify you if the Activity under test called 242 * {@link android.app.Activity#finish()}, 243 * {@link android.app.Activity#finishFromChild(Activity)}, or 244 * {@link android.app.Activity#finishActivity(int)}. 245 * @return Returns true if one of the listed finish methods was called. 246 */ isFinishCalled()247 public boolean isFinishCalled() { 248 if (mMockParent != null) { 249 return mMockParent.mFinished; 250 } 251 return false; 252 } 253 254 /** 255 * This method will return the request code if the Activity under test called 256 * {@link android.app.Activity#finishActivity(int)}. 257 * @return The request code provided in the start call, or -1 if no finish call was made. 258 */ getFinishedActivityRequest()259 public int getFinishedActivityRequest() { 260 if (mMockParent != null) { 261 return mMockParent.mFinishedActivityRequest; 262 } 263 return 0; 264 } 265 266 /** 267 * This mock Activity represents the "parent" activity. By injecting this, we allow the user 268 * to call a few more Activity methods, including: 269 * <ul> 270 * <li>{@link android.app.Activity#getRequestedOrientation()}</li> 271 * <li>{@link android.app.Activity#setRequestedOrientation(int)}</li> 272 * <li>{@link android.app.Activity#finish()}</li> 273 * <li>{@link android.app.Activity#finishActivity(int requestCode)}</li> 274 * <li>{@link android.app.Activity#finishFromChild(Activity child)}</li> 275 * </ul> 276 * 277 * TODO: Make this overrideable, and the unit test can look for calls to other methods 278 */ 279 private static class MockParent extends Activity { 280 281 public int mRequestedOrientation = 0; 282 public Intent mStartedActivityIntent = null; 283 public int mStartedActivityRequest = -1; 284 public boolean mFinished = false; 285 public int mFinishedActivityRequest = -1; 286 287 /** 288 * Implementing in the parent allows the user to call this function on the tested activity. 289 */ 290 @Override setRequestedOrientation(int requestedOrientation)291 public void setRequestedOrientation(int requestedOrientation) { 292 mRequestedOrientation = requestedOrientation; 293 } 294 295 /** 296 * Implementing in the parent allows the user to call this function on the tested activity. 297 */ 298 @Override getRequestedOrientation()299 public int getRequestedOrientation() { 300 return mRequestedOrientation; 301 } 302 303 /** 304 * By returning null here, we inhibit the creation of any "container" for the window. 305 */ 306 @Override getWindow()307 public Window getWindow() { 308 return null; 309 } 310 311 /** 312 * By defining this in the parent, we allow the tested activity to call 313 * <ul> 314 * <li>{@link android.app.Activity#startActivity(Intent)}</li> 315 * <li>{@link android.app.Activity#startActivityForResult(Intent, int)}</li> 316 * </ul> 317 */ 318 @Override startActivityFromChild(Activity child, Intent intent, int requestCode)319 public void startActivityFromChild(Activity child, Intent intent, int requestCode) { 320 mStartedActivityIntent = intent; 321 mStartedActivityRequest = requestCode; 322 } 323 324 /** 325 * By defining this in the parent, we allow the tested activity to call 326 * <ul> 327 * <li>{@link android.app.Activity#finish()}</li> 328 * <li>{@link android.app.Activity#finishFromChild(Activity child)}</li> 329 * </ul> 330 */ 331 @Override finishFromChild(Activity child)332 public void finishFromChild(Activity child) { 333 mFinished = true; 334 } 335 336 /** 337 * By defining this in the parent, we allow the tested activity to call 338 * <ul> 339 * <li>{@link android.app.Activity#finishActivity(int requestCode)}</li> 340 * </ul> 341 */ 342 @Override finishActivityFromChild(Activity child, int requestCode)343 public void finishActivityFromChild(Activity child, int requestCode) { 344 mFinished = true; 345 mFinishedActivityRequest = requestCode; 346 } 347 } 348 } 349