Home | History | Annotate | Download | only in test
      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 
     93     public ActivityUnitTestCase(Class<T> activityClass) {
     94         mActivityClass = activityClass;
     95     }
     96 
     97     @Override
     98     public T getActivity() {
     99         return (T) super.getActivity();
    100     }
    101 
    102     @Override
    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      */
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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
    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
    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
    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
    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
    343         public void finishActivityFromChild(Activity child, int requestCode) {
    344             mFinished = true;
    345             mFinishedActivityRequest = requestCode;
    346         }
    347     }
    348 }
    349