帮酷LOGO
0 0 评论
  • 显示原文与译文双语对照的内容
文章标签:测试  test  COM  Refer  Android 测试  Complete  REF  
Complete reference for Android Testing with examples.

  • 源代码名称:android-testing-guide
  • 源代码网址:http://www.github.com/ravidsrk/android-testing-guide
  • android-testing-guide源代码文档
  • android-testing-guide源代码下载
  • Git URL:
    git://www.github.com/ravidsrk/android-testing-guide.git
  • Git Clone代码到本地:
    git clone http://www.github.com/ravidsrk/android-testing-guide
  • Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/ravidsrk/android-testing-guide
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
    
  • Android测试指南 Android ArsenalBackers on Open CollectiveSponsors on Open CollectiveJoin the chat at https://gitter.im/android-testing-guide/Lobby

    GitHub starsGitHub forksGitHub watchersGitHub followersTwitter Follow

    通过示例完成对Android测试的完整参考。

    电子邮件内容

    简介

    为什么测试?

    • 测试强迫你以不同的方式思考,并隐式地使代码在流程中变得更加清晰。
    • 如果代码有测试,你对代码的信心更大。
    • 闪亮绿色状态栏和详细报告详细说明了你的代码多少,这都是编写测试的结果。
    • 回归测试变得更加容易,因为自动化测试会首先获取 Bug。

    为什么 单元测试?

    一个 单元测试 通常以可以重复的方式运行最小可以能的代码( 可以是一个方法,类或者组件) 单元的功能。

    用于执行这里测试的工具:

    • JUnit - 正常测试断言。
    • Mockito 模拟不受测试的其他类。
    • PowerMock - 模拟诸如Android环境类等的static 类。

    测试测试

    UI测试或者测试测试模拟了典型用户与你的应用程序的交互。 点击按钮,输入文本是UI测试可以完成的一些事情。

    • Espresso - 用于在你的应用程序中测试,选择项目,确保某些东西可见。
    • UIAutomator - 用于测试不同应用程序之间的交互。

    还有其他工具可以用于此类测试,例如 RobotiumAppium,葫芦,Robolectric。

    本地测试

    JUnit基础知识

    Calculator.java

    publicclassCalculator {
     publicintadd(intop1, intop2) {
     return op1 + op2;
     }
     publicintdiff(intop1, intop2) {
     return op1 - op2;
     }
     publicdoublediv(intop1, intop2) {
     // if (op2 == 0) return 0;return op1 / op2;
     }
    }

    CalculatorTest.java

    publicclassCalculatorTest {
     privateCalculator calculator;
     @Beforepublicvoidsetup() {
     calculator =newCalculator();
     System.out.println("Ready for testing!");
     }
     @Afterpublicvoidcleanup() {
     System.out.println("Done with unit test!");
     }
     @BeforeClasspublicstaticvoidtestClassSetup() {
     System.out.println("Getting test class ready");
     }
     @AfterClasspublicstaticvoidtestClassCleanup() {
     System.out.println("Done with tests");
     }
     @TestpublicvoidtestAdd() {
     calculator =newCalculator();
     int total = calculator.add(4, 5);
     assertEquals("Calculator is not adding correctly", 9, total);
     }
     @TestpublicvoidtestDiff() {
     calculator =newCalculator();
     int total = calculator.diff(9, 2);
     assertEquals("Calculator is not subtracting correctly", 7, total);
     }
     @TestpublicvoidtestDiv() {
     calculator =newCalculator();
     double total = calculator.div(9, 3);
     assertEquals("Calculator is not dividing correctly", 3.0, total, 0.0);
     }
    }

    超越JUnit基础

    CalculatorTest.java

    @Ignore@Test(expected=java.lang.ArithmeticException.class)publicvoid testDivWithZeroDivisor() {
     calculator =newCalculator();
     double total = calculator.div(9, 0);
     assertEquals("Calculator is not handling division by zero correctly", 0.0, total, 0.0);
    }

    断言

    JUnit为所有基本类型和对象以及数组( 基元或者对象) 提供重载断言方法。 参数顺序是期望值后跟实际值。 第一个参数可以是在失败时输出的字符串消息。 有一个稍微不同的断言,assertThat包含可选失败消息。实际值和Matcher对象的参数。 请注意,预期和实际与其他 assert 方法相反。

    AssertTests.java

    publicclassAssertTests {
     @TestpublicvoidtestAssertArrayEquals() {
     byte[] expected ="trial".getBytes();
     byte[] actual ="trial".getBytes();
     assertArrayEquals("failure - byte arrays not same", expected, actual);
     }
     @TestpublicvoidtestAssertEquals() {
     assertEquals("failure - strings are not equal", "text", "text");
     }
     @TestpublicvoidtestAssertFalse() {
     assertFalse("failure - should be false", false);
     }
     @TestpublicvoidtestAssertNotNull() {
     assertNotNull("should not be null", newObject());
     }
     @TestpublicvoidtestAssertNotSame() {
     assertNotSame("should not be same Object", newObject(), newObject());
     }
     @TestpublicvoidtestAssertNull() {
     assertNull("should be null", null);
     }
     @TestpublicvoidtestAssertSame() {
     Integer aNumber =Integer.valueOf(768);
     assertSame("should be same", aNumber, aNumber);
     }
     // JUnit Matchers assertThat@TestpublicvoidtestAssertThatBothContainsString() {
     assertThat("albumen", both(containsString("a")).and(containsString("b")));
     }
     @TestpublicvoidtestAssertThatHasItems() {
     assertThat(Arrays.asList("one", "two", "three"), hasItems("one", "three"));
     }
     @TestpublicvoidtestAssertThatEveryItemContainsString() {
     assertThat(Arrays.asList(newString[] { "fun", "ban", "net" }), everyItem(containsString("n")));
     }
     // Core Hamcrest Matchers with assertThat@TestpublicvoidtestAssertThatHamcrestCoreMatchers() {
     assertThat("good", allOf(equalTo("good"), startsWith("good")));
     assertThat("good", not(allOf(equalTo("bad"), equalTo("good"))));
     assertThat("good", anyOf(equalTo("bad"), equalTo("good")));
     assertThat(7, not(CombinableMatcher.<Integer> either(equalTo(3)).or(equalTo(4))));
     assertThat(newObject(), not(sameInstance(newObject())));
     }
     @TestpublicvoidtestAssertTrue() {
     assertTrue("failure - should be true", true);
     }
    }

    Hamcrest

    HamcrestTest.java

    publicclassHamcrestTest {
     @TestpublicvoidtestWithAsserts() {
     List<String> list = generateStingList();
     assertTrue(list.contains("android"));
     assertTrue(list.contains("context"));
     assertTrue(list.size() >4);
     assertTrue(list.size() <13);
     }
     @TestpublicvoidtestWithBigAssert() {
     List<String> list = generateStingList();
     assertTrue(list.contains("android") && list.contains("context") && list.size() >3&& list.size() <12);
     }
     @TestpublicvoidtestWithHamcrest() {
     List<String> list = generateStingList();
     assertThat(list, (hasItems("android", "context")));
     assertThat(list, allOf(hasSize(greaterThan(3)), hasSize(lessThan(12))));
     }
     @TestpublicvoidtestFailureWithAsserts() {
     List<String> list = generateStingList();
     assertTrue(list.contains("android"));
     assertTrue(list.contains("service"));
     assertTrue(list.size() >3);
     assertTrue(list.size() <12);
     }
     @TestpublicvoidtestFailureWithHamcrest() {
     List<String> list = generateStingList();
     assertThat(list, (hasItems("android", "service")));
     assertThat(list, allOf(hasSize(greaterThan(3)), hasSize(lessThan(12))));
     }
     @TestpublicvoidtestTypeSafety() {
     // assertThat("123", equalTo(123));// assertThat(123, equalTo("123")); }
     privateList<String>generateStingList() {
     String[] sentence = {"android", "context", "service", "manifest", "layout", "resource", "broadcast", "receiver", "gradle"};
     returnArrays.asList(sentence);
     }
    }

    规则

    CalculatorWithTestName.java

    publicclassCalculatorWithTestName {
     @RulepublicTestName name =newTestName();
     @TestpublicvoidtestAdd() {
     Calculator calculator =newCalculator();
     int total = calculator.add(4, 5);
     assertEquals(name.getMethodName() +" adding incorrectly", 9, total);
     }
     @TestpublicvoidtestDiff() {
     Calculator calculator =newCalculator();
     int total = calculator.diff(12, 7);
     assertEquals(name.getMethodName() +" subtracting incorrectly", 5, total);
     }
    }

    RESTMock

    RESTMock是一个工作在 square/mockwebserver 之上的图书馆。 它允许你指定 Hamcrest 匹配到 MATCH HTTP请求,并指定返回什么响应。 这是很简单的:

    RESTMockServer.whenGET(pathContains("users/defunkt"))
    . thenReturnFile(200, "users/defunkt.json");
    步骤 1: 启动服务器

    在测试应用程序启动之前,最好启动服务器,只有很少的方法:

    ) RESTMockTestRunner

    为了简单,你只需在UI测试中使用预定义的RESTMockTestRunner 即可。 它扩展了 AndroidJUnitRunner:

    defaultConfig {
     ...
     testInstrumentationRunner 'io.appflate.restmock.android.RESTMockTestRunner' }
    ) RESTMockServerStarter

    如果你有自定义测试 runner 并且不能扩展 RESTMockTestRunner,那么你可以直接调用 RESTMockServerStarter。 实际上 RESTMockTestRunner 也在做同样的事情:

    publicclassMyAppTestRunnerextendsAndroidJUnitRunner {
     ...@OverridepublicvoidonCreate(Bundlearguments) {
     super.onCreate(arguments);
     RESTMockServerStarter.startSync(newAndroidAssetsFileParser(getContext()),newAndroidLogger());
     ... }
     ...}
    步骤:指定模拟 ) 文件

    默认情况下,RESTMockTestRunner 使用 AndroidAssetsFileParser 作为模拟文件解析器,它从 assets 文件夹读取文件。 要使RESTMock可见,你必须将它们放在项目中的正确文件夹中,例如:

    
    .../src/androidTest/assets/users/defunkt.json
    
    
    
    

    可以像这样访问:

    RESTMockServer.whenGET(pathContains("users/defunkt"))
    . thenReturnFile(200, "users/defunkt.json");
    ) 字符串

    如果要返回的响应很简单,则只需指定一个字符串:

    RESTMockServer.whenGET(pathContains("users/defunkt"))
    . thenReturnString(200, "{}");
    ) MockResponse

    如果希望对响应有 GREATER 控制,可以通过 MockResponse

    RESTMockServer.whenGET(pathContains("users/defunkt")).thenReturn(newMockResponse().setBody("").setResponseCode(401).addHeader("Header","Value"));
    步骤 3: 请求匹配器

    你可以使用 RequestMatchers util类中的一些预定义匹配器,也可以创建自己的。 记住从 RequestMatcher 扩展

    步骤 4: 指定API端点

    最重要的步骤是,为了使应用程序与tmodel通信,必须将它指定为所有API调用的端点。 因此,你可以使用 RESTMockServer.getUrl()。 如果你使用 Retrofit,那么它就像下面这样简单:

    RestAdapter adapter =newRestAdapter.Builder()
    . baseUrl(RESTMockServer.getUrl())
     .... build();
    请求验证

    可以验证调用了哪些请求,以及感谢 RequestsVerifier的次数。 你所要做的就是调用其中的一个:

    //cheks if the GET request was invoked exactly 2 timesRequestsVerifier.verifyGET(pathEndsWith("users")).exactly(2);//cheks if the GET request was invoked at least 3 timesRequestsVerifier.verifyGET(pathEndsWith("users")).atLeast(3);//cheks if the GET request was invoked exactly 1 timeRequestsVerifier.verifyGET(pathEndsWith("users")).invoked();//cheks if the GET request was never invokedRequestsVerifier.verifyGET(pathEndsWith("users")).never();
    日志记录

    RESTMock支持日志记录事件。你只需要为RESTMock提供 RESTMockLogger的实现。 对于 Android,已经实现了一个 AndroidLogger。 你所要做的就是使用 RESTMockTestRunner 或者 call

    RESTMockServerStarter.startSync(newAndroidAssetsFileParser(getContext()),newAndroidLogger());

    或者

    RESTMockServer.enableLogging(RESTMockLogger)RESTMockServer.disableLogging()

    Android

    Android测试规则

    测试 Android Activity的规则

    MainActivityTestRule.java

    publicclassMainActivityTestRule<A extendsActivity> extends ActivityTestRule<A> {
     publicMainActivityTestRule(Class<A>activityClass) {
     super(activityClass);
     }
     @OverrideprotectedIntentgetActivityIntent() {
     Log.e("MainActivityTestRule", "Prepare the activity's intent");
     returnsuper.getActivityIntent();
     }
     @OverrideprotectedvoidbeforeActivityLaunched() {
     Log.e("MainActivityTestRule", "Execute before the activity is launched");
     super.beforeActivityLaunched();
     }
     @OverrideprotectedvoidafterActivityLaunched() {
     Log.e("MainActivityTestRule", "Execute after the activity has been launched");
     super.afterActivityLaunched();
     }
     @OverrideprotectedvoidafterActivityFinished() {
     Log.e("MainActivityTestRule", "Cleanup after it has finished");
     super.afterActivityFinished();
     }
     @OverridepublicAlaunchActivity(IntentstartIntent) {
     Log.e("MainActivityTestRule", "Launching the activity");
     returnsuper.launchActivity(startIntent);
     }
    }
    测试Android服务的规则

    SampleServiceTestRule.java

    publicclassSampleServiceTestRuleextendsServiceTestRule {
     @OverridepublicvoidstartService(Intentintent) throwsTimeoutException {
     Log.e("SampleServiceTestRule", "start the service");
     super.startService(intent);
     }
     @OverridepublicIBinderbindService(Intentintent) throwsTimeoutException {
     Log.e("SampleServiceTestRule", "binding the service");
     returnsuper.bindService(intent);
     }
     @OverrideprotectedvoidbeforeService() {
     Log.e("SampleServiceTestRule", "work before the service starts");
     super.beforeService();
     }
     @OverrideprotectedvoidafterService() {
     Log.e("SampleServiceTestRule", "work after the service has started");
     super.afterService();
     }
    }

    Android设备测试

    测试 Android Activity

    MainActivityTest.java

    @RunWith(AndroidJUnit4.class)publicclassMainActivityTest {
     @RulepublicMainActivityTestRule<MainActivity> mainActivityActivityTestRule =newMainActivityTestRule<MainActivity>(MainActivity.class);
     @TestpublicvoidtestUI() {
     Activity activity = mainActivityActivityTestRule.getActivity();
     assertNotNull(activity.findViewById(R.id.text_hello));
     TextView helloView = (TextView) activity.findViewById(R.id.text_hello);
     assertTrue(helloView.isShown());
     assertEquals("Hello World!", helloView.getText());
     assertEquals(InstrumentationRegistry.getTargetContext().getString(R.string.hello_world), helloView.getText());
     assertNull(activity.findViewById(android.R.id.button1));
     }
    }
    测试Android服务

    SampleServiceTest

    @RunWith(AndroidJUnit4.class)publicclassSampleServiceTest {
     @RulepublicSampleServiceTestRule myServiceRule =newSampleServiceTestRule();
     @TestpublicvoidtestService() throwsTimeoutException {
     myServiceRule.startService(newIntent(InstrumentationRegistry.getTargetContext(), SampleService.class));
     }
     @TestpublicvoidtestBoundService() throwsTimeoutException {
     IBinder binder = myServiceRule.bindService(
     newIntent(InstrumentationRegistry.getTargetContext(), SampleService.class));
     SampleService service = ((SampleService.LocalBinder) binder).getService();
     // Do work with the service assertNotNull("Bound service is null", service);
     }
    }

    测试筛选

    MainActivityTest.java

    @Test@RequiresDevicepublicvoid testRequiresDevice() {
     Log.d("Test Filters", "This test requires a device");
     Activity activity = activityTestRule.getActivity();
     assertNotNull("MainActivity is not available", activity);
    }@Test@SdkSuppress(minSdkVersion=30)publicvoid testMinSdkVersion() {
     Log.d("Test Filters", "Checking for min sdk> = 30");
     Activity activity = activityTestRule.getActivity();
     assertNotNull("MainActivity is not available", activity);
    }@Test@SdkSuppress(minSdkVersion=Build.VERSION_CODES.LOLLIPOP)publicvoid testMinBuild() {
     Log.d("Test Filters", "Checking for min build> Lollipop");
     Activity activity = activityTestRule.getActivity();
     assertNotNull("MainActivity is not available", activity);
    }@Test@SmallTestpublicvoid testSmallTest() {
     Log.d("Test Filters", "this is a small test");
     Activity activity = activityTestRule.getActivity();
     assertNotNull("MainActivity is not available", activity);
    }@Test@LargeTestpublicvoid testLargeTest() {
     Log.d("Test Filters", "This is a large test");
     Activity activity = activityTestRule.getActivity();
     assertNotNull("MainActivity is not available", activity);
    }

    咖啡

    MainActivityTest.java

    @Testpublicvoid testEspresso() {
     ViewInteraction interaction = onView(allOf(withId(R.id.editText),
     withText("this is a test"),
     hasFocus()));
     interaction.perform(replaceText("how about some new text"));
     ViewInteraction interaction2 = onView(allOf(withId(R.id.editText),
     withText("how about some new text")));
     interaction2.check(matches(hasFocus()));
    }@Testpublicvoid testEspressoSimplified() {
     onView(allOf(withId(R.id.editText),
     withText("this is a test"),
     hasFocus())).perform(replaceText("how about some new text"));
     onView(allOf(withId(R.id.editText),
     withText("how about some new text"))).check(matches(hasFocus()));
    }

    Robolectric

    MainActivityRoboelectricTest.java

    @RunWith(RobolectricGradleTestRunner.class)@Config(constants=BuildConfig.class)publicclassMainActivityRoboelectricTest {
     privateMainActivity activity;
     @Beforepublicvoidsetup() {
     activity =Robolectric.setupActivity(MainActivity.class);
     }
     @TestpublicvoidclickButton() {
     Button button = (Button) activity.findViewById(R.id.button);
     assertNotNull("test button could not be found", button);
     assertTrue("button does not contain text 'Click Me!'", "Click Me".equals(button.getText()));
     }
    }

    Robotium

    MainActivityRobotiumTest.java

    publicclassMainActivityRobotiumTest {
     privateSolo solo;
     @RulepublicActivityTestRule<MainActivity> activityTestRule =newActivityTestRule<>(MainActivity.class);
     publicvoidsetUp() {
     solo =newSolo(InstrumentationRegistry.getInstrumentation(),
     activityTestRule.getActivity());
     }
     publicvoidtearDown() {
     solo.finishOpenedActivities();
     }
     @TestpublicvoidtestPushClickMe() {
     solo.waitForActivity(MainActivity.class);
     solo.assertCurrentActivity("MainActivity is not displayed", MainActivity.class);
     assertTrue("This is a test in EditText is not displayed",
     solo.searchText("this is a test"));
     solo.clickOnButton("Click Me");
     assertTrue("You clicked me text is not displayed in the EditText",
     solo.searchText("you clicked me!"));
     }
    }

    UI测试和用户界面 Automator

    MainActivityTest

    @Testpublicvoid testPressBackButton() {
     UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).pressBack();
    }@Test@Ignorepublicvoid testUiDevice() throws RemoteException {
     UiDevice device =UiDevice.getInstance(
     InstrumentationRegistry.getInstrumentation());
     if (device.isScreenOn()) {
     device.setOrientationLeft();
     device.openNotification();
     }
    }@Testpublicvoid testUiAutomatorAPI() throws UiObjectNotFoundException, InterruptedException {
     UiDevice device =UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
     UiSelector editTextSelector =newUiSelector().className("android.widget.EditText").text("this is a test").focusable(true);
     UiObject editTextWidget = device.findObject(editTextSelector);
     editTextWidget.setText("this is new text");
     Thread.sleep(2000);
     UiSelector buttonSelector =newUiSelector().className("android.widget.Button").text("Click Me").clickable(true);
     UiObject buttonWidget = device.findObject(buttonSelector);
     buttonWidget.click();
     Thread.sleep(2000);
    }

    sampletest.py

    # Imports the monkeyrunner modulesfrom com.android.monkeyrunner import MonkeyRunner, MonkeyDevice, MonkeyImage# Alert the user a MonkeyRunner script is about to executeMonkeyRunner.alert("Monkeyrunner about to execute","Monkeyrunner","OK")# Connects to the current emulatoremulator= MonkeyRunner.waitForConnection()# Install the Android app package and test packageemulator.installPackage('./app/build/outputs/apk/app-debug-unaligned.apk')
    emulator.installPackage('./app/build/outputs/apk/app-debug-androidTest-unaligned.apk')# sets a variable with the package's internal namepackage ='in.ravidsrk.sample'# sets a variable with the name of an Activity in the packageactivity ='in.ravidsrk.sample.MainActivity'# sets the name of the component to startrunComponent = package +'/'+ activity# Runs the componentemulator.startActivity(runComponent)# wait for the screen to fully come upMonkeyRunner.sleep(2.0)# Takes a screenshotsnapshot = emulator.takeSnapshot()# Writes the screenshot to a filesnapshot.writeToFile('mainactivity.png','png')# Alert the user a testing is about to be run by MonkeyRunnerMonkeyRunner.alert("Instrumented test about to execute","Monkeyrunner","OK")#kick off the instrumented testemulator.shell('am instrument -w in.ravidsrk.sample.test/android.support.test.runner.AndroidJUnitRunner')# return to the emulator home screenemulator.press('KEYCODE_HOME','DOWN_AND_UP')

    引用

    贡献者

    这个项目已经存在,因为所有的人都。

    支持者

    感谢我们所有的支持者 !



    文章标签:COM  test  测试  REF  Complete  Refer  Android 测试  

    Copyright © 2011 HelpLib All rights reserved.    知识分享协议 京ICP备05059198号-3  |  如果智培  |  酷兔英语