美文网首页
Android单元测试和UI测试

Android单元测试和UI测试

作者: ztzt123 | 来源:发表于2018-02-26 15:58 被阅读0次

    原文出处Unit and UI Testing in Android Studio

    文章参考:android-testing-support-library

    本文将教你编写Java的单元测试JUnit 和编写运行在手机上的Espresso测试

    开始Junit单元测试.

    首先需要在Build Variants的窗口选择Test Artifact,设置为Unit Tests
    如图所示:

    在src下创建test文件夹,新建项目Android stadio会自动为我们生成,项目的目录结构如下。

    然后添加JUnit4依赖

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        testCompile 'junit:junit:4.12'
        compile 'com.android.support:appcompat-v7:23.4.0'
    }
    

    开始JUnit测试
    先编写一个简单的被测试的类

    public class Calculator {
    
        public double sum(double a, double b){
            return a+b;
        }
    
        public double substract(double a, double b){
            return a-b;
        }
    
        public double divide(double a, double b){
            return a/b;
        }
    
        public double multiply(double a, double b){
            return a*b;
        }
    }
    

    只需在编辑器内右键点击Calculator类的声明,选择Go to > Test,然后"Create a new test…"

    选择Create New Test

    在窗口中选择Junit4,修改测试类名,选中setUp/@Before,添加需要测试的方法。

    ok后会自动生成测试类文件。在相应的方法中添加测试用例,结果假设。

    public class CalculatorTest {
    
        Calculator calculator;
    
        @Before
        public void setUp() throws Exception {
            calculator = new Calculator();
        }
    
        @Test
        public void testSum() throws Exception {
            assertEquals(6d,calculator.sum(1d,5d),0);
        }
    
        @Test
        public void testSubstract() throws Exception {
            assertEquals(1d, calculator.substract(5d, 4d), 0);
        }
    
        @Test
        public void testDivide() throws Exception {
            assertEquals(4d, calculator.divide(20d, 5d), 0);
        }
    
        @Test
        public void testMultiply() throws Exception {
            assertEquals(10d, calculator.multiply(2d, 5d), 0);
        }
    }
    

    选择测试类,右键选择run就可以运行JUnit测试,关于方法的执行顺序是每一个有@before标注的都会在@Test标注的方法执行前执行一遍。具体的语法也比较简单。

    也可以在命令行输入gradle指令

    ./gradlew test
    

    所有假设都正确就可以代表测试通过会显示Process finished with exit code 0,同时在被测试的类会显示测试覆盖率。断言失败将会显示

     java.lang.AssertionError: 
    Expected :43.0
    Actual   :6.0
       <Click to see difference>
    

    可以查看到哪里测试没有通过。
    可能你已经注意到了Android Studio从来没有让你连接设备或者启动模拟器来运行测试。那是因为,位于src/tests目录下的测试是运行在本地电脑Java虚拟机上的单元测试。编写测试,实现功能使测试通过,然后再添加更多的测试...这种工作方式使快速迭代成为可能,我们称之为测试驱动开发。

    下面讲到的instrumentation测试

    Instrumentation测试

    测试库包含Espresso,Espresso是一个提供了简单 API 的用于 android app UI 测试的测试框架。让我们通过编辑build.gradle的相关部分来把它们添加进我们的工程。需要先把Test Artifact 修改为Android Instrumentation Tests。

    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 23
        buildToolsVersion "24.0.0"
    
        defaultConfig {
            applicationId "com.example.zhangtao21.testdemo"
            minSdkVersion 19
            targetSdkVersion 23
            versionCode 1
            versionName "1.0"
    
            //ADD THIS LINE:
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    
        //ADD THESE LINES:
        packagingOptions {
            exclude 'LICENSE.txt'
        }
    }
    
    dependencies {
        compile fileTree(include: ['*.jar'], dir: 'libs')
        testCompile 'junit:junit:4.12'
        compile 'com.android.support:appcompat-v7:23.4.0'
        compile 'com.jakewharton:butterknife:7.0.1'
        compile 'com.android.support:recyclerview-v7:24.1.1'
    
    
        androidTestCompile('com.android.support.test:runner:0.2') {
            exclude module: 'support-annotations'
        }
        androidTestCompile('com.android.support.test:rules:0.2') {
            exclude module: 'support-annotations'
        }
        androidTestCompile('com.android.support.test.espresso:espresso-core:2.1') {
            exclude module: 'support-annotations'
        }
    
        androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.0') {
            exclude group: 'com.android.support', module: 'appcompat'
            exclude group: 'com.android.support', module: 'support-v4'
            exclude module: 'recyclerview-v7'
            exclude module: 'support-annotations'
        }
    
        androidTestCompile ('com.android.support.test.espresso:espresso-intents:2.2.2'){
            exclude module: 'support-annotations'
        }
    }
    

    由于一些依赖版本冲突,和com.android.support:appcompat-v7的冲突,需要不包含·support-annotations。

    添加简单的App交互。
    [图片上传失败...(image-e1e587-1519631892004)]

    创建并运行Espresso测试

    在androidTest下创建MainActivityInstrumentationTest

    @RunWith(AndroidJUnit4.class)
    public class MainActivityInstrumentationTest {
        @Rule
        public ActivityTestRule<MainActivity> mainActivityActivityTestRule = new ActivityTestRule<MainActivity>(MainActivity.class);
    
        @Test
        public void sayHello(){
            onView(withId(R.id.editText)).perform(typeText("zhangtao"),closeSoftKeyboard());
            onView(withText("Say hello!")).perform(click());
            onView(withId(R.id.textView)).check(matches(withText("zhangtao")));
        }
    }
    

    也可以继承ActivityInstrumentationTestCase2<MainActivity>
    实现的操作如下:

    • 找到id为R.id.editText的控件输入文字zhangtao,关闭软键盘
    • 找到文字是Say hello!的控件,点击
    • 找到id为R.id.textView的控件,检测文字是否是zhangtao

    这样就会在模拟器或者连接的设备上运行测试,你可以在手机屏幕上看到被执行的动作。最后会在Android Studio输出通过和失败的测试结果。

    Espresso 由 3 个主要的组件构成。

    这些组件是:

    ViewMatchers - 在当前的 view 层级中定位一个 view
    ViewActions - 跟你的 view 交互
    ViewAssertions - 给你的 view 设置断言
    更简单的可以用下面的短语来表述它们:

    ViewMatchers – “ 找 某些东西“
    ViewActions – “ 做 某些事情“
    ViewAssertions – “ 检查 某些东西“

    p7.png

    下面我们要考虑几种情况,如果我们遇到了ListView 该怎么测试?

    有人会说直接找到文本然后执行点击,但是如果文本不在一个屏幕内呢,需要滑动。。。但是不知道滑动多少啊,espresso为我们提供了onData,用来操作AdapterView。
    如果adapterView里面绑定的是只有String模型的话,我们可以直接判断类型,然后判断是否相等,

        onData(allOf(is(instanceOf((String.class))), is("北京"))).perform(click());
        onView(withId(R.id.text2)).check(matches(withText("city:北京")));
    

    执行的操作如下,检测list中所有是String类型的,并且数值是北京的,然后点击,然后判断text2的文本是不是city:北京;

    如果不是String类型 我们就需要定义自己的matcher了

    public class CustomMatchers {
    
        public static Matcher<Object> withStudentname(final String name){
            return new BoundedMatcher<Object,Student>(Student.class) {
                @Override
                public void describeTo(Description description) {
          //           description.appendText("with name" + name);
                }
    
                @Override
                protected boolean matchesSafely(Student item) {
                    if(name.equals(item.name)){
                        return true;
                    }
                    return false;
    
                }
            };
        }
    }
    

    我们定义的原则是学生名称匹配。

    onData(allOf(is(instanceOf((Student.class))),withStudentname("zhangtao")))
         .inAdapterView(withId(R.id.list)).perform(click());
    onView(withId(R.id.text2)).check(matches(withText("zhangtao:1")));
    

    recyclerView 并不适用于onData,提供了独有的测试方式,

    onView(withId(R.id.rcview)).perform(RecyclerViewActions.actionOnItem(hasDescendant(withText("zhangtao")), click()));
    onView(withId(R.id.st)).check(matches(withText("zhangtao:1")));
    

    测试RecyclerView我们需要使用RecyclerViewActions。

    最头疼的就是测试一些同步或者异步的延时任务了,在测试延时任务时,我们需要通知测试在什么时间段开始,通知测试我们需要用到自定义IdlingResource,我们先来看一下我们测试的Activity。

    public class TimeActivity extends AppCompatActivity {
    
        public boolean isShow = false;
        TextView textView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_time);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            textView = (TextView) findViewById(R.id.time);
            textView.setVisibility(View.VISIBLE);
            textView.setText("sleep1000");
    
            isShow = true;
        }
    
        public boolean getShow(){
            return isShow;
        }
    }
    

    getShow 是我们外抛的方法,当返回true的时候开始执行测试。

    public class TestIdlingResource implements IdlingResource{
    
        TimeActivity timeActivity ;
    
        ResourceCallback resourceCallback;
    
        TestIdlingResource(TimeActivity timeActivity){
            this.timeActivity = timeActivity;
        }
    
        @Override
        public String getName() {
            return TestIdlingResource.class.getName();
        }
    
        @Override
        public boolean isIdleNow() {
            boolean idle = timeActivity.getShow();
            if (idle && resourceCallback != null) {
                resourceCallback.onTransitionToIdle();
            }
            return idle;
        }
    
        @Override
        public void registerIdleTransitionCallback(ResourceCallback callback) {
            this.resourceCallback = callback;
        }
    
    }
    

    espresso 会检测isIdleNow是否返回tre,true的时候开始执行,resourceCallback.onTransitionToIdle();是必须调用的。

    @RunWith(AndroidJUnit4.class)
    public class TimeActivityTest {
    
        TestIdlingResource  testIdlingResource;
    
        @Rule
        public ActivityTestRule<TimeActivity> rule = new ActivityTestRule<TimeActivity>(TimeActivity.class);
    
        @Before
        public void register(){
            testIdlingResource = new TestIdlingResource(rule.getActivity());
            Espresso.registerIdlingResources(testIdlingResource);
        }
    
        @Test
        public void testTimer(){
            onView(withId(R.id.time)).check(matches(withText("sleep1000")));
        }
    
        @After
        public void unregisterIntentServiceIdlingResource() {
            Espresso.unregisterIdlingResources(testIdlingResource);
        }
    
    }
    

    在测试开始前生成TestIdlingResource,Espresso调用registerIdlingResource表示开始延时操作,
    在结束的时候unregisterIdlingResources。

    最后分享一个google测试示例

    相关文章

      网友评论

          本文标题:Android单元测试和UI测试

          本文链接:https://www.haomeiwen.com/subject/gouyxftx.html