Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1337766
  • 博文数量: 113
  • 博客积分: 3526
  • 博客等级: 中校
  • 技术积分: 1815
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-08 09:46
个人简介

记录总结自己的工作

文章分类

全部博文(113)

文章存档

2015年(19)

2014年(10)

2013年(6)

2012年(16)

2011年(24)

2010年(21)

2009年(17)

分类: 嵌入式

2011-02-18 13:23:42

      在网上搜了半天,发现关于ServiceTestCase的文章少得可怜,而在SDK中也只有少量的说明,还是自己总结研究一下吧,以下内容大部分出自SDK,外加自己的理解,可能会有理解错误的地方。
      根据SDK中的说明画了一个类的继承图如下:



       从图中可以看出ServiceTestCase继承了JUnit的TestCase类,因此可以可以在测试中控制程序和服务进行测试工作。另外还可以提供mock application和Context将服务独立系统,这点非常重要,应该可以消除服务对外部的依赖,但还需要进行进一步的研究才能确定。

       对于ServiceTestCase有一下几点需要注意:

       1.ServiceTestCase.startService()和ServiceTestCase.bindService()这两个方法负责完成测试环境的初始化工作,其中包括mock objects,然后启动服务。

       2.ServiceTestCase.bindService()和Service.bindService()方法的不同之处在于其返回值的类型:
                ServiceTestCase.bindService()——>Intent
                Service.bindService()——>IBinder object

      3.同其余的测试一样,ServiceTestCase也在每次测试的时候调用setUp()方法,该方法会通过复制当前的系统Context来建立测试平台,通过调用getSystemContext()方法可以获得此Context。如果要重写这个方法,则第一句必须为super.setUp()。

     4.setApplication()和setContext(Context)可以在启动服务前设定mock Context和mock Application。

     5.在运行前,ServiceTestCase会默认地运行testAndroidTestCaseSetupProperly()方法来确定测试类正确地搭建好了Context

     那么对于Service进行测试到底要测什么呢?在SDK中所提到的主要有以下几个方面:
    
     1.调用Context.startService()或者Context.bindService()后要确定onCreate()方法被正确地调用;同样,当调用Context.stopService(), Context.unbindService(), stopSelf()或者 stopSelfResult()等方法时要确定onDestroy()方法被正确地调用。
 
     2.服务能够正确地处理Context.startService()的多次调用,只有第一次调用才会触发Service.onCreate()方法,但是每次都会调用Service.onStartCommand()方法。还要注意的是startService()不会嵌套调用,因此对Context.stopService()或者 Service.stopSelf() ( stopSelf(int)不再此列)的一次调用就应该能够终止服务。

     3.测试服务在实现上的逻辑正确性。

      以上都是些理论上的东西,下篇文章结合例子从头完成对一个服务的测试。下面附上ServiceTestCase类的源代码,以供参考。

      
  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. package android.test;

  17. import android.app.Application;
  18. import android.app.Service;
  19. import android.content.ComponentName;
  20. import android.content.Context;
  21. import android.content.Intent;
  22. import android.os.IBinder;
  23. import android.os.RemoteException;
  24. import android.test.mock.MockApplication;

  25. import java.lang.reflect.Field;
  26. import java.util.Random;

  27. /***
  28.  * This test case provides a framework in which you can test Service classes in
  29.  * a controlled environment. It provides basic support for the lifecycle of a
  30.  * Service, and hooks by which you can inject various dependencies and control
  31.  * the environment in which your Service is tested.
  32.  *
  33.  *

    Lifecycle Support.

  34.  * Every Service is designed to be accessed within a specific sequence of
  35.  * calls. .
  36.  * In order to support the lifecycle of a Service, this test case will make the
  37.  * following calls at the following times.
  38.  *
  39.  *
    • The test case will not call onCreate() until your test calls
    •  * {@link #startService} or {@link #bindService}. This gives you a chance
    •  * to set up or adjust any additional framework or test logic before
    •  * onCreate().

    •  *
    • When your test calls {@link #startService} or {@link #bindService}
    •  * the test case will call onCreate(), and then call the corresponding entry point in your service.
    •  * It will record any parameters or other support values necessary to support the lifecycle.

    •  *
    • After your test completes, the test case {@link #tearDown} function is
    •  * automatically called, and it will stop and destroy your service with the appropriate
    •  * calls (depending on how your test invoked the service.)

    •  *

  40.  *
  41.  *

    Dependency Injection.

  42.  * Every service has two inherent dependencies, the {@link android.content.Context Context} in
  43.  * which it runs, and the {@link android.app.Application Application} with which it is associated.
  44.  * This framework allows you to inject modified, mock, or isolated replacements for these
  45.  * dependencies, and thus perform a true unit test.
  46.  *
  47.  *

    If simply run your tests as-is, your Service will be injected with a fully-functional

  48.  * Context, and a generic {@link android.test.mock.MockApplication MockApplication} object.
  49.  * You can create and inject alternatives to either of these by calling
  50.  * {@link AndroidTestCase#setContext(Context) setContext()} or
  51.  * {@link #setApplication setApplication()}. You must do this before calling
  52.  * startService() or bindService(). The test framework provides a
  53.  * number of alternatives for Context, including {link android.test.mock.MockContext MockContext},
  54.  * {@link android.test.RenamingDelegatingContext RenamingDelegatingContext}, and
  55.  * {@link android.content.ContextWrapper ContextWrapper}.
  56.  */
  57. public abstract class ServiceTestCase<T extends Service> extends AndroidTestCase {

  58.     Class<T> mServiceClass;

  59.     private Context mSystemContext;
  60.     private Application mApplication;

  61.     public ServiceTestCase(Class<T> serviceClass) {
  62.         mServiceClass = serviceClass;
  63.     }

  64.     private T mService;
  65.     private boolean mServiceAttached = false;
  66.     private boolean mServiceCreated = false;
  67.     private boolean mServiceStarted = false;
  68.     private boolean mServiceBound = false;
  69.     private Intent mServiceIntent = null;
  70.     private int mServiceId;

  71.     /***
  72.      * @return Returns the actual service under test.
  73.      */
  74.     public T getService() {
  75.         return mService;
  76.     }

  77.     /***
  78.      * This will do the work to instantiate the Service under test. After this, your test
  79.      * code must also start and stop the service.
  80.      */
  81.     @Override
  82.     protected void setUp() throws Exception {
  83.         super.setUp();
  84.         
  85.         // get the real context, before the individual tests have a chance to muck with it
  86.         mSystemContext = getContext();

  87.     }
  88.     
  89.     /***
  90.      * Create the service under test and attach all injected dependencies (Context, Application) to
  91.      * it. This will be called automatically by {@link #startService} or by {@link #bindService}.
  92.      * If you wish to call {@link AndroidTestCase#setContext(Context) setContext()} or
  93.      * {@link #setApplication setApplication()}, you must do so before calling this function.
  94.      */
  95.     protected void setupService() {
  96.         mService = null;
  97.         try {
  98.             mService = mServiceClass.newInstance();
  99.         } catch (Exception e) {
  100.             assertNotNull(mService);
  101.         }
  102.         if (getApplication() == null) {
  103.             setApplication(new MockApplication());
  104.         }
  105.         mService.attach(
  106.                 getContext(),
  107.                 null, // ActivityThread not actually used in Service
  108.                 mServiceClass.getName(),
  109.                 null, // token not needed when not talking with the activity manager
  110.                 getApplication(),
  111.                 null // mocked services don't talk with the activity manager
  112.                 );
  113.         
  114.         assertNotNull(mService);
  115.         
  116.         mServiceId = new Random().nextInt();
  117.         mServiceAttached = true;
  118.     }
  119.     
  120.     /***
  121.      * Start the service under test, in the same way as if it was started by
  122.      * {@link android.content.Context#startService Context.startService()}, providing the
  123.      * arguments it supplied. If you use this method to start the service, it will automatically
  124.      * be stopped by {@link #tearDown}.
  125.      *
  126.      * @param intent The Intent as if supplied to {@link android.content.Context#startService}.
  127.      */
  128.     protected void startService(Intent intent) {
  129.         assertFalse(mServiceStarted);
  130.         assertFalse(mServiceBound);
  131.         
  132.         if (!mServiceAttached) {
  133.             setupService();
  134.         }
  135.         assertNotNull(mService);
  136.         
  137.         if (!mServiceCreated) {
  138.             mService.onCreate();
  139.             mServiceCreated = true;
  140.         }
  141.         mService.onStart(intent, mServiceId);
  142.         
  143.         mServiceStarted = true;
  144.     }
  145.     
  146.     /***
  147.      * Start the service under test, in the same way as if it was started by
  148.      * {@link android.content.Context#bindService Context.bindService()}, providing the
  149.      * arguments it supplied.
  150.      *
  151.      * Return the communication channel to the service. May return null if
  152.      * clients can not bind to the service. The returned
  153.      * {@link android.os.IBinder} is usually for a complex interface
  154.      * that has been described using
  155.      * aidl.
  156.      *
  157.      * Note: In order to test with this interface, your service must implement a getService()
  158.      * method, as shown in samples.ApiDemos.app.LocalService.

  159.      * @param intent The Intent as if supplied to {@link android.content.Context#bindService}.
  160.      *
  161.      * @return Return an IBinder for making further calls into the Service.
  162.      */
  163.     protected IBinder bindService(Intent intent) {
  164.         assertFalse(mServiceStarted);
  165.         assertFalse(mServiceBound);
  166.         
  167.         if (!mServiceAttached) {
  168.             setupService();
  169.         }
  170.         assertNotNull(mService);
  171.         
  172.         if (!mServiceCreated) {
  173.             mService.onCreate();
  174.             mServiceCreated = true;
  175.         }
  176.         // no extras are expected by unbind
  177.         mServiceIntent = intent.cloneFilter();
  178.         IBinder result = mService.onBind(intent);
  179.         
  180.         mServiceBound = true;
  181.         return result;
  182.     }
  183.     
  184.     /***
  185.      * This will make the necessary calls to stop (or unbind) the Service under test, and
  186.      * call onDestroy(). Ordinarily this will be called automatically (by {@link #tearDown}, but
  187.      * you can call it directly from your test in order to check for proper shutdown behaviors.
  188.      */
  189.     protected void shutdownService() {
  190.         if (mServiceStarted) {
  191.             mService.stopSelf();
  192.             mServiceStarted = false;
  193.         } else if (mServiceBound) {
  194.             mService.onUnbind(mServiceIntent);
  195.             mServiceBound = false;
  196.         }
  197.         if (mServiceCreated) {
  198.             mService.onDestroy();
  199.         }
  200.     }
  201.     
  202.     /***
  203.      * Shuts down the Service under test. Also makes sure all resources are cleaned up and
  204.      * garbage collected before moving on to the next
  205.      * test. Subclasses that override this method should make sure they call super.tearDown()
  206.      * at the end of the overriding method.
  207.      *
  208.      * @throws Exception
  209.      */
  210.     @Override
  211.     protected void tearDown() throws Exception {
  212.         shutdownService();
  213.         mService = null;

  214.         // Scrub out members - protects against memory leaks in the case where someone
  215.         // creates a non-static inner class (thus referencing the test case) and gives it to
  216.         // someone else to hold onto
  217.         scrubClass(ServiceTestCase.class);

  218.         super.tearDown();
  219.     }
  220.     
  221.     /***
  222.      * Set the application for use during the test. If your test does not call this function,
  223.      * a new {@link android.test.mock.MockApplication MockApplication} object will be generated.
  224.      *
  225.      * @param application The Application object that will be injected into the Service under test.
  226.      */
  227.     public void setApplication(Application application) {
  228.         mApplication = application;
  229.     }

  230.     /***
  231.      * Return the Application object being used by the Service under test.
  232.      *
  233.      * @return Returns the application object.
  234.      *
  235.      * @see #setApplication
  236.      */
  237.     public Application getApplication() {
  238.         return mApplication;
  239.     }
  240.     
  241.     /***
  242.      * Return a real (not mocked or instrumented) system Context that can be used when generating
  243.      * Mock or other Context objects for your Service under test.
  244.      *
  245.      * @return Returns a reference to a normal Context.
  246.      */
  247.     public Context getSystemContext() {
  248.         return mSystemContext;
  249.     }

  250.     public void testServiceTestCaseSetUpProperly() throws Exception {
  251.         setupService();
  252.         assertNotNull("service should be launched successfully", mService);
  253.     }
  254. }

阅读(5829) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~