Android测试基础整理篇

Android test framework

转载请注明来自:http://blog.csdn.net/liaoqianchuan00/article/details/23032357

1.  基本

1.  常用Assertions

l   assertEquals

l   assertFalse


l   assertNotNull

l   assertNotSame

l   assertNull

l   assertSame

l   assertTrue

l   fail

 

2.  自定义输出语句

public void testMax() {

final int a = 1;

final int b = 2;

final int expected = b;

final int actual = Math.max(a, b);

assertEquals("Expection " + expected + " but was " + actual, expected,   actual);

}

3.   控件Assertions

l   assertBaselineAligned: Asserts that two views arealigned on their baseline, that is their baselines are on the same y location.

l   assertBottomAligned: Asserts that two views arebottom aligned, that is their bottom edges are on the same y location.

l   assertGroupContains: Asserts that the specifiedgroup contains a specific child once and only once.

l   assertGroupIntegrity: Asserts the specified group'sintegrity. The children count should be >= 0 and each child should benon-null.

l   assertGroupNotContains: Asserts that the specifiedgroup does not contain a specific child.

l   assertHasScreenCoordinates: Asserts that a view hasa particular x and y position on the visible screen.

l   assertHorizontalCenterAligned: Asserts that the testview is horizontally center aligned with respect to the reference view.

l   assertLeftAligned: Asserts that two views are leftaligned, that is their left edges are on the same x location. An optionalmargin can also be provided.

l   assertOffScreenAbove: Asserts that the specifiedview is above the visible screen.

l   assertOffScreenBelow: Asserts that the specifiedview is below the visible screen.

l   assertOnScreen: Asserts that a view is on thescreen.

l   assertRightAligned: Asserts that two views areright-aligned, that is their right edges are on the same x location. Anoptional margin can also be specified.

l   assertTopAligned: Asserts that two views aretop-aligned, that is their top edges are on the same y location. An optionalmargin can also be specified.

l   assertVerticalCenterAligned: Asserts that the testview is vertically center aligned with respect to the reference view.

 

4.  TouchUtils

l   Clicking on a View and releasing it

l   Tapping on a View, that is touching it and quicklyreleasing

l   Long clicking on a View

l   Dragging the screen

 

 

 

public void testListScrolling() {

       mListView.scrollTo(0, 0);

       TouchUtils.dragQuarterScreenUp(this, mActivity);

       TouchUtils.dragQuarterScreenUp(this, mActivity);

       TouchUtils.dragQuarterScreenUp(this, mActivity);

       TouchUtils.dragQuarterScreenUp(this, mActivity);

       TouchUtils.tapView(this, mListView);

       final int expectedItemPosition = 6;

       final int actualItemPosition =

         mListView.getFirstVisiblePosition();

       assertEquals("Wrong position",

         expectedItemPosition, actualItemPosition);

       final String expected = "Anguilla";

       final String actual = mListView.getAdapter().

         getItem(expectedItemPosition).toString();

       assertEquals("Wrong content", actual, expected);

}

 

5.  Mock对象

l   MockApplication: A mock implementation of the Application class. All methods arenon-functional and throw UnsupportedOperationException.

l   MockContentProvider: A mock implementation of ContentProvider. All methods arenon-functional and throw UnsupportedOperationException.

l   MockContentResolver: A mock implementation of the ContentResolver class that isolatesthe test code from the real content system. All methods are non-functional andthrow UnsupportedOperationException.

l   MockContext: A mock Context class. This can be used to inject otherdependencies. All methods are non-functional and throwUnsupportedOperationException.

l   MockCursor: A mock Cursor class that isolates the test code from real Cursorimplementation. All methods are non-functional and throwUnsupportedOperationException.

l   MockDialogInterface: A mock implementation of DialogInterface class. All methods arenon-functional and throw UnsupportedOperationException.

l   MockPackageManager: A mock implementation of PackageManager class. All methods arenon-functional and throw UnsupportedOperationException.

l   MockResources: A mock Resources class. All methods are non-functional and throw UnsupportedOperationException.

2.  框架结构

1     AndroidTestCase

 

当你需要用到Activity Context的时候就使用这个类,比如Resources,database,file system,你可以是用mContext来是用context。你可以用ontext.startActivity()来启动多个Activity。

有很多个testcase继承自这个类:

l   ApplicationTestCase<T extends Application>

l   ProviderTestCase2<T extends ContentProvider>

l  ServiceTestCase<T extends Service>

 

2     Instrumentation

这个是用来监视Activity和Application的,你可以用它来控制Activity的生命周期和用户交互事件。

 

l   我们可以是用Instrumentation.ActivityMonitor来监测一个Activity。例如:

public void testFollowLink() {

final Instrumentation inst = getInstrumentation();

IntentFilter intentFilter = new IntentFilter(Intent.ACTION_VIEW);

intentFilter.addDataScheme("http");

intentFilter.addCategory(Intent.CATEGORY_BROWSABLE);

ActivityMonitor monitor = inst.addMonitor(

IntentFilter, null, false);

assertEquals(0, monitor.getHits());

TouchUtils.clickView(this, mLink);              monitor.waitForActivityWithTimeout(5000);

assertEquals(1, monitor.getHits()); inst.removeMonitor(monitor);

}

 

l   使用Instrumentation来测试activities,它的生命周期的函数不会自动调用,只有onCreate方法会自动调用,你可以调用其他的生命周期通过getInstrumentation().callActivityOnXXX。

3     InstrumentationTestCase


它的子类:

l   ActivityTestCase

l   ProviderTestCase2<T extends ContentProvider>

l   SingleLaunchActivityTestCase<T extends Activity>

l   SyncBaseInstrumentation

l   ActivityInstrumentationTestCase2<T extends Activity>

l   ActivityUnitTestCase<T extends Activity>

 

注意:android.test.ActivityInstrumentationTestCase从Android1.5开始已经不建议使用了,可以是用android.test.ActivityInstrumentationTestCase2替代。

 

3.  例子程序

1.  被测试程序

package com.vogella.android.test.simpleactivity;

 

import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.view.View;

 

public class MainActivity extends Activity {

 

  @Override

  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

  }

 

  public void onClick(View view) {

    Intent intent = new Intent(this, SecondActivity.class);

    intent.putExtra("URL", "http://www.vogella.com");

    startActivity(intent);

  }

}

 

 

2.  单元测试

package com.vogella.android.test.simpleactivity.test;

 

import android.content.Intent;

import android.test.TouchUtils;

import android.test.suitebuilder.annotation.SmallTest;

import android.widget.Button;

 

import com.vogella.android.test.simpleactivity.MainActivity;

 

public class MainActivityUnitTest extends

    android.test.ActivityUnitTestCase<MainActivity> {

 

  private int buttonId;

  private MainActivity activity;

 

  public MainActivityUnitTest() {

    super(MainActivity.class);

  }

  @Override

  protected void setUp() throws Exception {

    super.setUp();

    Intent intent = new Intent(getInstrumentation().getTargetContext(),

        MainActivity.class);

    startActivity(intent, null, null);

    activity = getActivity();

  }

 

  public void testLayout() {

    buttonId = com.vogella.android.test.simpleactivity.R.id.button1;

    assertNotNull(activity.findViewById(buttonId));

    Button view = (Button) activity.findViewById(buttonId);

    assertEquals("Incorrect label of the button", "Start", view.getText());

  }

 

  public void testIntentTriggerViaOnClick() {

    buttonId = com.vogella.android.test.simpleactivity.R.id.button1;

    Button view = (Button) activity.findViewById(buttonId);

    assertNotNull("Button not allowed to be null", view);

 

    view.performClick();

   

    // TouchUtils cannot be used, only allowed in

    // InstrumentationTestCase or ActivityInstrumentationTestCase2

 

    // Check the intent which was started

    Intent triggeredIntent = getStartedActivityIntent();

    assertNotNull("Intent was null", triggeredIntent);

    String data = triggeredIntent.getExtras().getString("URL");

 

    assertEquals("Incorrect data passed via the intent",

        "http://www.vogella.com", data);

  }

 

}

 

3.  功能测试

package com.vogella.android.test.simpleactivity.test;

 

import android.app.Activity;

import android.app.Instrumentation;

import android.app.Instrumentation.ActivityMonitor;

import android.test.ActivityInstrumentationTestCase2;

import android.test.TouchUtils;

import android.test.ViewAsserts;

import android.view.KeyEvent;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;

 

import com.vogella.android.test.simpleactivity.R;

 

import com.vogella.android.test.simpleactivity.MainActivity;

import com.vogella.android.test.simpleactivity.SecondActivity;

 

public class MainActivityFunctionalTest extends

    ActivityInstrumentationTestCase2<MainActivity> {

 

  private MainActivity activity;

 

  public MainActivityFunctionalTest() {

    super(MainActivity.class);

  }

  @Override

  protected void setUp() throws Exception {

    super.setUp();

    setActivityInitialTouchMode(false);

    activity = getActivity();

  }

 

  public void testStartSecondActivity() throws Exception {

   

   

   

    // add monitor to check for the second activity

    ActivityMonitor monitor =

        getInstrumentation().

          addMonitor(SecondActivity.class.getName(), null, false);

 

    // find button and click it

    Button view = (Button) activity.findViewById(R.id.button1);

   

    // TouchUtils handles the sync with the main thread internally

    TouchUtils.clickView(this, view);

 

    // to click on a click, e.g., in a listview

    // listView.getChildAt(0);

 

    // wait 2 seconds for the start of the activity

    SecondActivity startedActivity = (SecondActivity) monitor

        .waitForActivityWithTimeout(2000);

    assertNotNull(startedActivity);

 

    // search for the textView

    TextView textView = (TextView) startedActivity.findViewById(R.id.resultText);

   

    // check that the TextView is on the screen

    ViewAsserts.assertOnScreen(startedActivity.getWindow().getDecorView(),

        textView);

    // validate the text on the TextView

    assertEquals("Text incorrect", "Started", textView.getText().toString());

   

    // press back and click again

    this.sendKeys(KeyEvent.KEYCODE_BACK);

   

    TouchUtils.clickView(this, view);

  }

}

 

 

Robotium

https://code.google.com/p/robotium/wiki/RobotiumTutorials下载例子程序

 

public void testDisplayWhiteBox() {

 

              //Defining our own values to multiply

              float vFirstNumber = 10;

              float vSecondNumber = 20;

              float vResutl = vFirstNumber * vSecondNumber ;

             

              //Access First value (edit-filed) and putting firstNumber value in it

              EditText vFirstEditText = (EditText) solo.getView(R.id.EditText01);

              solo.clearEditText(vFirstEditText);

              solo.enterText(vFirstEditText, String.valueOf(vFirstNumber));

             

              //Access Second value (edit-filed) and putting SecondNumber value in it

              EditText vSecondEditText = (EditText) solo.getView(R.id.EditText02);

              solo.clearEditText(vSecondEditText);

              solo.enterText(vSecondEditText, String.valueOf(vSecondNumber));

             

              //Click on Multiply button

              solo.clickOnButton("Multiply");

             

              assertTrue(solo.searchText(String.valueOf(vResutl)));                         

              TextView outputField = (TextView) solo.getView(R.id.TextView01);          

              //Assert to verify result with visible value

              assertEquals(String.valueOf(vResutl), outputField.getText().toString());

       }

 

参考地址:

https://code.google.com/p/robotium/

 

https://code.google.com/p/robotium/wiki/RobotiumTutorials

 

1     简介

Robotium是一款国外的Android自动化测试框架,主要针对Android平台的应用进行黑盒自动化测试,它提供了模拟各种手势操作(点击、长按、滑动等)、查找和断言机制的API,能够对各种控件进行操作。Robotium结合Android官方提供的测试框架达到对应用程序进行自动化的测试。另外,Robotium 4.0版本已经支持对WebView的操作。Robotium 对Activity,Dialog,Toast,Menu 都是支持的。

 

类似于selenium。

 

2     API

Solo类中提供了自动点击、取得、拖拽、搜索等各种方法。 声明Solo类型的成员变量privateSolo solo;  

 

Activity&Fragment

assertCurrentActivity(text,Activity.class)- Ensure that the current activity equals the second parameter.

getCurrentActivity() .getFragmentManager().findFragmentById()-S earches for a fragment.

waitForActivity(SecondActivity.class,2000)- Waits for the specified activity for 2 seconds

点击 

clickOnButton(int)—Clickson a Button with a given index.

clickOnButton(String)—Clickson a Button with a given text. clickOnCheckBox(int)—Clicks on a CheckBox with agiven index.  clickOnView(View)—Clicks ona given View.  

clickOnText(String)—Clickson a View displaying a given text. clickLongOnText(String)—Long clicks on agiven View. clickOnRadioButton(int)—Clicks on a RadioButton with a given index.clickOnScreen(float, float)—Clicks on a given coordinate on the screen.

clickInList(x);-Click on item number x in a ListView

clickOnSearch-Allows to click on part of the screen.

pressSpinnerItem(0,2);-Presses an item in a Spinner

sendKey(Solo.MENU);-Sends the menu key event.

goBack()-Pressthe back button

取得 

getCurrentActivity()—Returnsthe current Activity.  

GetText(String)—Returnsa TextView which shows a given text. 

getView(int)—Returnsa View with a given id.  

getEditText(String)—Returnsan EditText which shows a given text.  getImage(int)—Returns an ImageView with a given index. 

 

拖拽

drag(float,float, float, float, int)—Simulate touching a given location and dragging it toa new location.

 

搜索

searchText(String)—Searchesfor a text string and returns true if at least one item is found with theexpected text.

searchEditText(String)—Searchesfor a text string in the EditText objects located in the current Activity.   S

earchButton(String,boolean)—Searches for a Button with the given text string and returns true ifat least one Button is found.

waitForText(text)

输入

enterText()-Entersa text.

 

界面判断

isCheckBoxChecked()-Checksif the checkbox is checked.

 

截屏

takeScreenshot()-Savesa screenshot on the device inthe /sdcard/Robotium-Screenshots/ folder. Requiresthe android.permission.WRITE_EXTERNAL_STORAGE permission in theAndroidManifest.xml ofthe application under test.

 

 

 

4.  QA

1.    在不知道ID的情况下怎么获取特定的view?

答:例如,ArrayList<TextView> aa = solo.getCurrentViews(TextView.class), 然后断点调试查看是哪个view,TextViewtextview = aa.get(5);

 

2.    如何滚动和拖动?

答:solo.scrollXXX();solo.drag(初始X坐标,目标X坐标,Y,toY,步数); X,Y坐标可以通过HierarchyViewer工具获得,也可以通过Year.getLocationOnScreen(zuobiao)。

 

3.    Robotium和robolectric区别?

答:Robotium是通过ui线程进行测试,一般用于对应传统的集成或系统测试;

Robolectric 是单元测试框架,好处是第一运行在JVM上,速度、性能高;

解决了Android自身因为环境问题的缺点以及减少了许多用Mock的地方。

 

4. 如何识别webview对象?

答:可以根据XPATH来进行点击和输入

solo.clickOnWebElement(By.xpath(text));

solo.enterTextInWebElement(By.xpath(text),s);

或者根据ID来获取

solo.clickOnWebElement(By.id(text));

solo.enterTextInWebElement(By.id(text), s);

 

 

Mockito

1.  简介

流行的mock框架

 

# jMock

http://jmock.org/

# EasyMock

http://easymock.org/

# Mockito

http://code.google.com/p/mockito/

 

使用Mockito不能用在下面的情况:

final classes

anonymous classes

primitive types

 

2.  使用方法

when(....).thenReturn(....)

或者

doReturn(object).when(kdskfsk).methodCall

 

 

使用verify来保证方法被调用到了

 

例如:

 

@Test

public void test1()  {

  MyClass test = Mockito.mock(MyClass.class);

  // define return value for method getUniqueId()

  test.when(test.getUniqueId()).thenReturn(43);

 

  // TODO use mock in test....

 

  // now check if method testing was called with the parameter 12

  Mockito.verify(test).testing(Matchers.eq(12));

 

  // was the method called twice?

  Mockito.verify(test, Mockito.times(2));

 

 

}

 

3.  Mock和Spy的区别

如果你mock了一个类,那么这个类的所有的函数都被Mockito改写了(如果是没有返回值的函数,则什么都不作,如果是有返回值,会返回默认值,比如布尔型的话返回false,List的话会返回一个空的列表,int的话会返回0等等),如果你Spy了一个类,那么所有的函数都没有被改变,除了那些被你打过桩的函数。看例子:

 

public class TestServiceImpl 

    public int getOrderCounts() 

    { 

        return 10; 

    } 

@Test 

    public void MockVsSpy() 

    { 

        TestServiceImpl service = mock(TestServiceImpl.class); 

        //输出0,因为该函数被Mockito改写了 

        System.out.println("Order counts of mock object" + service.getOrderCounts()); 

        when(service.getOrderCounts()).thenReturn(2); 

        //输出2, 因为我们给这个函数打了桩 

        System.out.println("Order counts of mock object AFTER stubs " + service.getOrderCounts()); 

         

        service = new TestServiceImpl(); 

        service = spy(service); 

        //输出10, 因为Mockito spy 不会改写已有的函数 

        System.out.println("Order counts of spy object" + service.getOrderCounts()); 

        when(service.getOrderCounts()).thenReturn(2); 

        //输出2, 因为我们给这个函数打了桩 

        System.out.println("Order counts of spy object AFTER stubs " + service.getOrderCounts()); 

    } 

 

4.    如何写自定义的参数匹配器

看例子

public class Account 

    private String    name; 

    private String    adddress; 

 

    public Account(String name, String address) 

    { 

        this.name = name; 

        this.adddress = address; 

    } 

    ...get/set 函数 

public interface AccountDao 

    public void addAccount(Account a); 

 

public class AccountServiceImpl 

    AccountDao dao; 

     

    public AccountServiceImpl(AccountDao dao) 

    { 

        this.dao = dao; 

    } 

     

    public void addAccount(String name, String address) 

    { 

        dao.addAccount(new Account(name, address)); 

    } 

 

 

public class AccountServiceImplTest 

    @Test 

    public void addAccount() 

    { 

        AccountDao dao = mock(AccountDao.class); 

        AccountServiceImpl service = new AccountServiceImpl(dao); 

         

        service.addAccount("obama", "white house"); 

         

        verify(dao).addAccount(new Account("obama", "white house")); 

    } 

上面的例子会失败,因为Mockito在做参数匹配时是根据equals函数的结果来判断两个参数是不是一样的。而我们的Account类并没有对equals作特殊的实现,所以会失败。修正的方法有三个,一个是改写Account类的equals函数。一个是用Mockito的反射相等匹配,就是把最后一句改成。

 

verify(dao).addAccount(refEq(new Account("obama", "white house")));

 

 

最后一种方法是写一个自定义的参数匹配器,如果Account的代码不是你控制的,那么你就只能选这种方法了。这时候最后一句要改成这样:

verify(dao).addAccount(argThat(new ArgumentMatcher<Account>() 

        { 

            @Override 

            public boolean matches(Object argument) 

            { 

                Account person = (Account)argument; 

                return person.getName().equals("obama") && person.getAddress().equals("white house") ? true : false; 

            } 

        })); 

 

在Android中使用Mockito:

需要下载下面3个库文件。

http://dexmaker.googlecode.com/files/dexmaker-1.0.jar

http://dexmaker.googlecode.com/files/dexmaker-mockito-1.0.jar

https://code.google.com/p/mockito/

 

 

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值