• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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