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.Application;
     20 import android.app.Service;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.os.IBinder;
     24 import android.test.mock.MockApplication;
     25 
     26 import java.util.Random;
     27 
     28 /**
     29  * This test case provides a framework in which you can test Service classes in
     30  * a controlled environment.  It provides basic support for the lifecycle of a
     31  * Service, and hooks with which you can inject various dependencies and control
     32  * the environment in which your Service is tested.
     33  *
     34  * <div class="special reference">
     35  * <h3>Developer Guides</h3>
     36  * <p>For more information about application testing, read the
     37  * <a href="{@docRoot}guide/topics/testing/index.html">Testing</a> developer guide.</p>
     38  * </div>
     39  *
     40  * <p><b>Lifecycle Support.</b>
     41  * A Service is accessed with a specific sequence of
     42  * calls, as described in the
     43  * <a href="http://developer.android.com/guide/topics/fundamentals/services.html">Services</a>
     44  * document. In order to support the lifecycle of a Service,
     45  * <code>ServiceTestCase</code> enforces this protocol:
     46  *
     47  * <ul>
     48  *      <li>
     49  *          The {@link #setUp()} method is called before each test method. The base implementation
     50  *          gets the system context. If you override <code>setUp()</code>, you must call
     51  *          <code>super.setUp()</code> as the first statement in your override.
     52  *      </li>
     53  *      <li>
     54  *          The test case waits to call {@link android.app.Service#onCreate()} until one of your
     55  *          test methods calls {@link #startService} or {@link #bindService}.  This gives you an
     56  *          opportunity to set up or adjust any additional framework or test logic before you test
     57  *          the running service.
     58  *      </li>
     59  *      <li>
     60  *          When one of your test methods calls {@link #startService ServiceTestCase.startService()}
     61  *          or {@link #bindService  ServiceTestCase.bindService()}, the test case calls
     62  *          {@link android.app.Service#onCreate() Service.onCreate()} and then calls either
     63  *          {@link android.app.Service#startService(Intent) Service.startService(Intent)} or
     64  *          {@link android.app.Service#bindService(Intent, ServiceConnection, int)
     65  *          Service.bindService(Intent, ServiceConnection, int)}, as appropriate. It also stores
     66  *          values needed to track and support the lifecycle.
     67  *      </li>
     68  *      <li>
     69  *          After each test method finishes, the test case calls the {@link #tearDown} method. This
     70  *          method stops and destroys the service with the appropriate calls, depending on how the
     71  *          service was started. If you override <code>tearDown()</code>, your must call the
     72  *          <code>super.tearDown()</code> as the last statement in your override.
     73  *      </li>
     74  * </ul>
     75  *
     76  * <p>
     77  *      <strong>Dependency Injection.</strong>
     78  *      A service has two inherent dependencies, its {@link android.content.Context Context} and its
     79  *      associated {@link android.app.Application Application}. The ServiceTestCase framework
     80  *      allows you to inject modified, mock, or isolated replacements for these dependencies, and
     81  *      thus perform unit tests with controlled dependencies in an isolated environment.
     82  * </p>
     83  * <p>
     84  *      By default, the test case is injected with a full system context and a generic
     85  *      {@link android.test.mock.MockApplication MockApplication} object. You can inject
     86  *      alternatives to either of these by invoking
     87  *      {@link AndroidTestCase#setContext(Context) setContext()} or
     88  *      {@link #setApplication setApplication()}.  You must do this <em>before</em> calling
     89  *      startService() or bindService().  The test framework provides a
     90  *      number of alternatives for Context, including
     91  *      {@link android.test.mock.MockContext MockContext},
     92  *      {@link android.test.RenamingDelegatingContext RenamingDelegatingContext},
     93  *      {@link android.content.ContextWrapper ContextWrapper}, and
     94  *      {@link android.test.IsolatedContext}.
     95  *
     96  * @deprecated Use
     97  * <a href="{@docRoot}reference/android/support/test/rule/ServiceTestRule.html">
     98  * ServiceTestRule</a> instead. New tests should be written using the
     99  * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
    100  */
    101 @Deprecated
    102 public abstract class ServiceTestCase<T extends Service> extends AndroidTestCase {
    103 
    104     Class<T> mServiceClass;
    105 
    106     private Context mSystemContext;
    107     private Application mApplication;
    108 
    109     /**
    110      * Constructor
    111      * @param serviceClass The type of the service under test.
    112      */
    113     public ServiceTestCase(Class<T> serviceClass) {
    114         mServiceClass = serviceClass;
    115     }
    116 
    117     private T mService;
    118     private boolean mServiceAttached = false;
    119     private boolean mServiceCreated = false;
    120     private boolean mServiceStarted = false;
    121     private boolean mServiceBound = false;
    122     private Intent mServiceIntent = null;
    123     private int mServiceId;
    124 
    125     /**
    126      * @return An instance of the service under test. This instance is created automatically when
    127      * a test calls {@link #startService} or {@link #bindService}.
    128      */
    129     public T getService() {
    130         return mService;
    131     }
    132 
    133     /**
    134      * Gets the current system context and stores it.
    135      *
    136      * Extend this method to do your own test initialization. If you do so, you
    137      * must call <code>super.setUp()</code> as the first statement in your override. The method is
    138      * called before each test method is executed.
    139      */
    140     @Override
    141     protected void setUp() throws Exception {
    142         super.setUp();
    143 
    144         // get the real context, before the individual tests have a chance to muck with it
    145         mSystemContext = getContext();
    146 
    147     }
    148 
    149     /**
    150      * Creates the service under test and attaches all injected dependencies
    151      * (Context, Application) to it.  This is called automatically by {@link #startService} or
    152      * by {@link #bindService}.
    153      * If you need to call {@link AndroidTestCase#setContext(Context) setContext()} or
    154      * {@link #setApplication setApplication()}, do so before calling this method.
    155      */
    156     protected void setupService() {
    157         mService = null;
    158         try {
    159             mService = mServiceClass.newInstance();
    160         } catch (Exception e) {
    161             assertNotNull(mService);
    162         }
    163         if (getApplication() == null) {
    164             setApplication(new MockApplication());
    165         }
    166         mService.attach(
    167                 getContext(),
    168                 null,               // ActivityThread not actually used in Service
    169                 mServiceClass.getName(),
    170                 null,               // token not needed when not talking with the activity manager
    171                 getApplication(),
    172                 null                // mocked services don't talk with the activity manager
    173                 );
    174 
    175         assertNotNull(mService);
    176 
    177         mServiceId = new Random().nextInt();
    178         mServiceAttached = true;
    179     }
    180 
    181     /**
    182      * Starts the service under test, in the same way as if it were started by
    183      * {@link android.content.Context#startService(Intent) Context.startService(Intent)} with
    184      * an {@link android.content.Intent} that identifies a service.
    185      * If you use this method to start the service, it is automatically stopped by
    186      * {@link #tearDown}.
    187      *
    188      * @param intent An Intent that identifies a service, of the same form as the Intent passed to
    189      * {@link android.content.Context#startService(Intent) Context.startService(Intent)}.
    190      */
    191     protected void startService(Intent intent) {
    192         if (!mServiceAttached) {
    193             setupService();
    194         }
    195         assertNotNull(mService);
    196 
    197         if (!mServiceCreated) {
    198             mService.onCreate();
    199             mServiceCreated = true;
    200         }
    201         mService.onStartCommand(intent, 0, mServiceId);
    202 
    203         mServiceStarted = true;
    204     }
    205 
    206     /**
    207      * <p>
    208      *      Starts the service under test, in the same way as if it were started by
    209      *      {@link android.content.Context#bindService(Intent, ServiceConnection, int)
    210      *      Context.bindService(Intent, ServiceConnection, flags)} with an
    211      *      {@link android.content.Intent} that identifies a service.
    212      * </p>
    213      * <p>
    214      *      Notice that the parameters are different. You do not provide a
    215      *      {@link android.content.ServiceConnection} object or the flags parameter. Instead,
    216      *      you only provide the Intent. The method returns an object whose type is a
    217      *      subclass of {@link android.os.IBinder}, or null if the method fails. An IBinder
    218      *      object refers to a communication channel between the application and
    219      *      the service. The flag is assumed to be {@link android.content.Context#BIND_AUTO_CREATE}.
    220      * </p>
    221      * <p>
    222      *      See <a href="{@docRoot}guide/components/aidl.html">Designing a Remote Interface
    223      *      Using AIDL</a> for more information about the communication channel object returned
    224      *      by this method.
    225      * </p>
    226      * Note:  To be able to use bindService in a test, the service must implement getService()
    227      * method. An example of this is in the ApiDemos sample application, in the
    228      * LocalService demo.
    229      *
    230      * @param intent An Intent object of the form expected by
    231      * {@link android.content.Context#bindService}.
    232      *
    233      * @return An object whose type is a subclass of IBinder, for making further calls into
    234      * the service.
    235      */
    236     protected IBinder bindService(Intent intent) {
    237         if (!mServiceAttached) {
    238             setupService();
    239         }
    240         assertNotNull(mService);
    241 
    242         if (!mServiceCreated) {
    243             mService.onCreate();
    244             mServiceCreated = true;
    245         }
    246         // no extras are expected by unbind
    247         mServiceIntent = intent.cloneFilter();
    248         IBinder result = mService.onBind(intent);
    249 
    250         mServiceBound = true;
    251         return result;
    252     }
    253 
    254     /**
    255      * Makes the necessary calls to stop (or unbind) the service under test, and
    256      * calls onDestroy().  Ordinarily this is called automatically (by {@link #tearDown}, but
    257      * you can call it directly from your test in order to check for proper shutdown behavior.
    258      */
    259     protected void shutdownService() {
    260         if (mServiceStarted) {
    261             mService.stopSelf();
    262             mServiceStarted = false;
    263         } else if (mServiceBound) {
    264             mService.onUnbind(mServiceIntent);
    265             mServiceBound = false;
    266         }
    267         if (mServiceCreated) {
    268             mService.onDestroy();
    269             mServiceCreated = false;
    270         }
    271     }
    272 
    273     /**
    274      * <p>
    275      *      Shuts down the service under test.  Ensures all resources are cleaned up and
    276      *      garbage collected before moving on to the next test. This method is called after each
    277      *      test method.
    278      * </p>
    279      * <p>
    280      *      Subclasses that override this method must call <code>super.tearDown()</code> as their
    281      *      last statement.
    282      * </p>
    283      *
    284      * @throws Exception
    285      */
    286     @Override
    287     protected void tearDown() throws Exception {
    288         shutdownService();
    289         mService = null;
    290 
    291         // Scrub out members - protects against memory leaks in the case where someone
    292         // creates a non-static inner class (thus referencing the test case) and gives it to
    293         // someone else to hold onto
    294         scrubClass(ServiceTestCase.class);
    295 
    296         super.tearDown();
    297     }
    298 
    299     /**
    300      * Sets the application that is used during the test.  If you do not call this method,
    301      * a new {@link android.test.mock.MockApplication MockApplication} object is used.
    302      *
    303      * @param application The Application object that is used by the service under test.
    304      *
    305      * @see #getApplication()
    306      */
    307     public void setApplication(Application application) {
    308         mApplication = application;
    309     }
    310 
    311     /**
    312      * Returns the Application object in use by the service under test.
    313      *
    314      * @return The application object.
    315      *
    316      * @see #setApplication
    317      */
    318     public Application getApplication() {
    319         return mApplication;
    320     }
    321 
    322     /**
    323      * Returns the real system context that is saved by {@link #setUp()}. Use it to create
    324      * mock or other types of context objects for the service under test.
    325      *
    326      * @return A normal system context.
    327      */
    328     public Context getSystemContext() {
    329         return mSystemContext;
    330     }
    331 
    332     /**
    333      * Tests that {@link #setupService()} runs correctly and issues an
    334      * {@link junit.framework.Assert#assertNotNull(String, Object)} if it does.
    335      * You can override this test method if you wish.
    336      *
    337      * @throws Exception
    338      */
    339     public void testServiceTestCaseSetUpProperly() throws Exception {
    340         setupService();
    341         assertNotNull("service should be launched successfully", mService);
    342     }
    343 }
    344