免费注册 查看新帖 |

Chinaunix

广告
  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1372 | 回复: 0
打印 上一主题 下一主题

Android Service Test——ServiceTestCase [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-02-18 13:23 |只看该作者 |倒序浏览
      在网上搜了半天,发现关于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.  * <p><b>Lifecycle Support.</b>
  34.  * Every Service is designed to be accessed within a specific sequence of
  35.  * calls. <insert link to Service lifecycle doc here>.
  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.  * <ul>
  40. The test case will not call onCreate() until your test calls
  41.  * {@link #startService} or {@link #bindService}. This gives you a chance
  42.  * to set up or adjust any additional framework or test logic before
  43.  * onCreate().

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

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

  52.  * </ul>
  53.  *
  54.  * <p><b>Dependency Injection.</b>
  55.  * Every service has two inherent dependencies, the {@link android.content.Context Context} in
  56.  * which it runs, and the {@link android.app.Application Application} with which it is associated.
  57.  * This framework allows you to inject modified, mock, or isolated replacements for these
  58.  * dependencies, and thus perform a true unit test.
  59.  *
  60.  * <p>If simply run your tests as-is, your Service will be injected with a fully-functional
  61.  * Context, and a generic {@link android.test.mock.MockApplication MockApplication} object.
  62.  * You can create and inject alternatives to either of these by calling
  63.  * {@link AndroidTestCase#setContext(Context) setContext()} or
  64.  * {@link #setApplication setApplication()}. You must do this <i>before</i> calling
  65.  * startService() or bindService(). The test framework provides a
  66.  * number of alternatives for Context, including {link android.test.mock.MockContext MockContext},
  67.  * {@link android.test.RenamingDelegatingContext RenamingDelegatingContext}, and
  68.  * {@link android.content.ContextWrapper ContextWrapper}.
  69.  */
  70. public abstract class ServiceTestCase<T extends Service> extends AndroidTestCase {

  71.     Class<T> mServiceClass;

  72.     private Context mSystemContext;
  73.     private Application mApplication;

  74.     public ServiceTestCase(Class<T> serviceClass) {
  75.         mServiceClass = serviceClass;
  76.     }

  77.     private T mService;
  78.     private boolean mServiceAttached = false;
  79.     private boolean mServiceCreated = false;
  80.     private boolean mServiceStarted = false;
  81.     private boolean mServiceBound = false;
  82.     private Intent mServiceIntent = null;
  83.     private int mServiceId;

  84.     /***
  85.      * @return Returns the actual service under test.
  86.      */
  87.     public T getService() {
  88.         return mService;
  89.     }

  90.     /***
  91.      * This will do the work to instantiate the Service under test. After this, your test
  92.      * code must also start and stop the service.
  93.      */
  94.     @Override
  95.     protected void setUp() throws Exception {
  96.         super.setUp();
  97.         
  98.         // get the real context, before the individual tests have a chance to muck with it
  99.         mSystemContext = getContext();

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

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

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

  231.         super.tearDown();
  232.     }
  233.     
  234.     /***
  235.      * Set the application for use during the test. If your test does not call this function,
  236.      * a new {@link android.test.mock.MockApplication MockApplication} object will be generated.
  237.      *
  238.      * @param application The Application object that will be injected into the Service under test.
  239.      */
  240.     public void setApplication(Application application) {
  241.         mApplication = application;
  242.     }

  243.     /***
  244.      * Return the Application object being used by the Service under test.
  245.      *
  246.      * @return Returns the application object.
  247.      *
  248.      * @see #setApplication
  249.      */
  250.     public Application getApplication() {
  251.         return mApplication;
  252.     }
  253.     
  254.     /***
  255.      * Return a real (not mocked or instrumented) system Context that can be used when generating
  256.      * Mock or other Context objects for your Service under test.
  257.      *
  258.      * @return Returns a reference to a normal Context.
  259.      */
  260.     public Context getSystemContext() {
  261.         return mSystemContext;
  262.     }

  263.     public void testServiceTestCaseSetUpProperly() throws Exception {
  264.         setupService();
  265.         assertNotNull("service should be launched successfully", mService);
  266.     }
  267. }

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP