国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

Home 類庫下載 java類庫 Detailed explanation of Android unit testing and simulation testing

Detailed explanation of Android unit testing and simulation testing

Oct 31, 2016 pm 12:03 PM

Testing and Basic Specifications

Why testing is needed?

For stability, we can clearly understand whether the development is completed correctly.

It is easier to maintain and can ensure that the function is not destroyed after modifying the code.

Integrate some tools to standardize development specifications and make the code more stable (such as submitting the unit tests that need to be executed when issuing a diff through phabricator differential, which can ensure the stability of the remote code in the development process).

2. What to test?

General unit testing:

List the exceptions you want to test and verify them.

Performance testing.

Simulation test: According to the needs, test the feedback and display of the interface during the actual use of the user, as well as the application testing of some components that depend on the system architecture.

3. It should be noted that

consider readability, use expressive method names for method names, and consider using a specification for test paradigms, such as RSpec-style. The method name can be in a format, such as: [Method of test]_[Condition of test]_[Expected result].

Don’t use logical flow keywords (If/else, for, do/while, switch/case). In a test method, if you need these, split them into each separate test method.

Test the content that really needs to be tested and the situations that need to be covered. Generally, only the verification output (such as what is displayed and what the value is after a certain operation) is considered.

Consider the time consumption, Android Studio will output the time consumption by default.

There is no need to consider testing private methods. Treat private methods as black box internal components and test the public methods referenced by them; do not consider testing trivial codes such as getters or setters.

Each unit test method should be in no order; decouple as much as possible. For different test methods, there should not be a situation where Test A and Test B have timing.

4. Create a test

Select the corresponding class

Place the cursor on the class name

Press ALT + ENTER

Select Create Test in the pop-up window

Unit testing in Android Studio and Simulation test

control + shift + R (Android Studio’s default execution unit test shortcut key).

1. Local unit testing

Run the test directly on the development machine.
Consider using this type of unit test only when there are no dependencies or only simple Android library dependencies are needed.

./gradlew check

(1) Code storage

If it corresponds to different flavors or build types, add the corresponding suffix directly after test (for example, the unit test code corresponding to myFlavor should be placed in src /testMyFlavor/java below).

src/test/java

(2)Google official recommended reference

dependencies {
    // Required -- JUnit 4 framework,用于單元測試,google官方推薦
    testCompile 'junit:junit:4.12'
    // Optional -- Mockito framework,用于模擬架構(gòu),google官方推薦
    //  http://www.manongjc.com/article/1546.html
    testCompile 'org.mockito:mockito-core:1.10.19'
}

(3)JUnit

Annotation

Detailed explanation of Android unit testing and simulation testing

2. Simulation test

A test that needs to be run on an Android device or virtual machine.

Mainly used for testing: unit (unit testing related to Android SDK layer reference relationship), UI, application component integration testing (Service, Content Provider, etc.).

./gradlew connectedAndroidTest

(1) Code storage:

src/androidTest/java

(2) Google official recommendation

dependencies {
    androidTestCompile 'com.android.support:support-annotations:23.0.1'
    androidTestCompile 'com.android.support.test:runner:0.4.1'
    androidTestCompile 'com.android.support.test:rules:0.4.1'
    // Optional -- Hamcrest library
    androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
    // Optional -- UI testing with Espresso
    //  http://www.manongjc.com/article/1546.html
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
    // Optional -- UI testing with UI Automator
    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'
}

(3) Common UI tests

need to simulate the Android system environment.

Three main points:

Whether the information displayed after the UI is loaded is correct.

Whether the UI information is displayed correctly after a certain user operation.

Display the correct page for users to operate.

(4)Espresso

Google officially provides it for UI interaction testing

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;

// 對于Id為R.id.my_view的View: 觸發(fā)點擊,檢測是否顯示
onView(withId(R.id.my_view)).perform(click())               
                            .check(matches(isDisplayed()));
// 對于文本打頭是"ABC"的View: 檢測是否沒有Enable
onView(withText(startsWith("ABC"))).check(matches(not(isEnabled()));
// 按返回鍵
pressBack();
// 對于Id為R.id.button的View: 檢測內(nèi)容是否是"Start new activity"
// http://www.manongjc.com/article/1537.html
onView(withId(R.id.button)).check(matches(withText(("Start new activity"))));
// 對于Id為R.id.viewId的View: 檢測內(nèi)容是否不包含"YYZZ"
onView(withId(R.id.viewId)).check(matches(withText(not(containsString("YYZZ")))));
// 對于Id為R.id.inputField的View: 輸入"NewText",然后關(guān)閉軟鍵盤
onView(withId(R.id.inputField)).perform(typeText("NewText"), closeSoftKeyboard());
// 對于Id為R.id.inputField的View: 清除內(nèi)容
onView(withId(R.id.inputField)).perform(clearText());

Start an Intent that opens the Activity

@RunWith(AndroidJUnit4.class)
public class SecondActivityTest {
    @Rule
    public ActivityTestRule<SecondActivity> rule =
            new ActivityTestRule(SecondActivity.class, true,
                                  // 這個參數(shù)為false,不讓SecondActivity自動啟動
                                  // 如果為true,將會在所有@Before之前啟動,在最后一個@After之后關(guān)閉
                                  false);
    @Test
    public void demonstrateIntentPrep() {
        Intent intent = new Intent();
        intent.putExtra("EXTRA", "Test");
        // 啟動SecondActivity并傳入intent
        rule.launchActivity(intent);
        // 對于Id為R.id.display的View: 檢測內(nèi)容是否是"Text"
        // http://www.manongjc.com/article/1532.html
        onView(withId(R.id.display)).check(matches(withText("Test")));
    }
}

(5) Asynchronous interaction

It is recommended to turn off the animation in "Settings->Developer Options" in the device , because these animations may confuse Espresso when detecting asynchronous tasks: Window animation scale, Transition animation scale, Animator duration scale.

For AsyncTask, during testing, if an AsyncTask task is thrown after a click event is triggered, directly onView(withId(R.id.update)).perform(click()) during testing, and then directly detect it. The detection at this time is after AsyncTask#onPostExecute.

// 通過實現(xiàn)IdlingResource,block住當非空閑的時候,當空閑時進行檢測,非空閑的這段時間處理異步事情
public class IntentServiceIdlingResource implements IdlingResource {
    ResourceCallback resourceCallback;
    private Context context;

    public IntentServiceIdlingResource(Context context) { this.context = context; }

    @Override public String getName() { return IntentServiceIdlingResource.class.getName(); }

    @Override public void registerIdleTransitionCallback( ResourceCallback resourceCallback) { this.resourceCallback = resourceCallback; }

    @Override public boolean isIdleNow() {
      // 是否是空閑
      // 如果IntentService 沒有在運行,就說明異步任務(wù)結(jié)束,IntentService特質(zhì)就是啟動以后處理完Intent中的事務(wù),理解關(guān)閉自己
      // http://www.manongjc.com/article/1531.html
        boolean idle = !isIntentServiceRunning();
        if (idle && resourceCallback != null) {
          // 回調(diào)告知異步任務(wù)結(jié)束
            resourceCallback.onTransitionToIdle();
        }
        return idle;
    }

    private boolean isIntentServiceRunning() {
        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        // Get all running services
        List<ActivityManager.RunningServiceInfo> runningServices = manager.getRunningServices(Integer.MAX_VALUE);
        // check if our is running
        for (ActivityManager.RunningServiceInfo info : runningServices) {
            if (MyIntentService.class.getName().equals(info.service.getClassName())) {
                return true;
            }
        }
        return false;
    }
}

// 使用IntentServiceIdlingResource來測試,MyIntentService服務(wù)啟動結(jié)束這個異步事務(wù),之后的結(jié)果。
@RunWith(AndroidJUnit4.class)
public class IntegrationTest {

    @Rule
    public ActivityTestRule rule = new ActivityTestRule(MainActivity.class);
    IntentServiceIdlingResource idlingResource;

    @Before
    public void before() {
        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
        Context ctx = instrumentation.getTargetContext();
        idlingResource = new IntentServiceIdlingResource(ctx);
        // 注冊這個異步監(jiān)聽
        Espresso.registerIdlingResources(idlingResource);

    }
    @After
    public void after() {
        // 取消注冊這個異步監(jiān)聽
        Espresso.unregisterIdlingResources(idlingResource);

    }

    @Test
    public void runSequence() {
        // MainActivity中點擊R.id.action_settings這個View的時候,會啟動MyIntentService
        onView(withId(R.id.action_settings)).perform(click());
        // 這時候IntentServiceIdlingResource#isIdleNow會返回false,因為MyIntentService服務(wù)啟動了
        // 這個情況下,這里會block住.............
        // 直到IntentServiceIdlingResource#isIdleNow返回true,并且回調(diào)了IntentServiceIdlingResource#onTransitionToIdle
        // 這個情況下,繼續(xù)執(zhí)行,這時我們就可以測試異步結(jié)束以后的情況了。
        onView(withText("Broadcast")).check(matches(notNullValue()));
    }
}

(6) Custom matcher

// 定義
public static Matcher<View> withItemHint(String itemHintText) {
  checkArgument(!(itemHintText.equals(null)));
  return withItemHint(is(itemHintText));
}

public static Matcher<View> withItemHint(final Matcher<String> matcherText) {
  checkNotNull(matcherText);
  return new BoundedMatcher<View, EditText>(EditText.class) {

    @Override
    public void describeTo(Description description) {
      description.appendText("with item hint: " + matcherText);
    }

    @Override
    protected boolean matchesSafely(EditText editTextField) {
      // 取出hint,然后比對下是否相同
      // http://www.manongjc.com/article/1524.html
      return matcherText.matches(editTextField.getHint().toString());
    }
  };
}

// 使用
onView(withItemHint("test")).check(matches(isDisplayed()));

拓展工具

1. AssertJ Android

square/assertj-android
極大的提高可讀性。

import static org.assertj.core.api.Assertions.*;

// 斷言: view是GONE的
assertThat(view).isGone();

MyClass test = new MyClass("Frodo");
MyClass test1 = new MyClass("Sauron");
MyClass test2 = new MyClass("Jacks");

List<MyClass> testList = new ArrayList<>();
testList.add(test);
testList.add(test1);

// 斷言: test.getName()等于"Frodo"
assertThat(test.getName()).isEqualTo("Frodo");
// 斷言: test不等于test1并且在testList中
// http://www.manongjc.com/article/1519.html
assertThat(test).isNotEqualTo(test1)
                 .isIn(testList);
// 斷言: test.getName()的字符串,是由"Fro"打頭,以"do"結(jié)尾,忽略大小寫會等于"frodo"
assertThat(test.getName()).startsWith("Fro")
                            .endsWith("do")
                            .isEqualToIgnoringCase("frodo");
// 斷言: testList有2個數(shù)據(jù),包含test,test1,不包含test2
assertThat(list).hasSize(2)
                .contains(test, test1)
                .doesNotContain(test2);

// 斷言: 提取testList隊列中所有數(shù)據(jù)中的成員變量名為name的變量,并且包含name為"Frodo"與"Sauron"
//      并且不包含name為"Jacks"
assertThat(testList).extracting("name")
                    .contains("Frodo", "Sauron")
                    .doesNotContain("Jacks");

2. Hamcrest

JavaHamcrest
通過已有的通配方法,快速的對代碼條件進行測試
org.hamcrest:hamcrest-junit:(version)

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.equalTo;

// 斷言: a等于b
assertThat(a, equalTo(b));
assertThat(a, is(equalTo(b)));
assertThat(a, is(b));
// 斷言: a不等于b
assertThat(actual, is(not(equalTo(b))));

List<Integer> list = Arrays.asList(5, 2, 4);
// 斷言: list有3個數(shù)據(jù)
assertThat(list, hasSize(3));
// 斷言: list中有5,2,4,并且順序也一致
assertThat(list, contains(5, 2, 4));
// 斷言: list中包含5,2,4
assertThat(list, containsInAnyOrder(2, 4, 5));
// 斷言: list中的每一個數(shù)據(jù)都大于1
// http://www.manongjc.com/article/1507.html
assertThat(list, everyItem(greaterThan(1)));
// 斷言: fellowship中包含有成員變量"race",并且其值不是ORC
assertThat(fellowship, everyItem(hasProperty("race", is(not((ORC))))));
// 斷言: object1中與object2相同的成員變量都是相同的值
assertThat(object1, samePropertyValuesAs(object2));

Integer[] ints = new Integer[] { 7, 5, 12, 16 };
// 斷言: 數(shù)組中包含7,5,12,16
assertThat(ints, arrayContaining(7, 5, 12, 16));

(1)幾個主要的匹配器:

Detailed explanation of Android unit testing and simulation testing

(2)自定義匹配器

// 自定義
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;

public class RegexMatcher extends TypeSafeMatcher<String> {
    private final String regex;

    public RegexMatcher(final String regex) { this.regex = regex; }
    @Override
    public void describeTo(final Description description) { description.appendText("matches regular expression=`" + regex + "`"); }

    @Override
    public boolean matchesSafely(final String string) { return string.matches(regex); }

    // 上層調(diào)用的入口
    public static RegexMatcher matchesRegex(final String regex) {
        return new RegexMatcher(regex);
    }
}

// 使用
String s = "aaabbbaaa";
assertThat(s, RegexMatcher.matchesRegex("a*b*a"));

3. Mockito

Mockito
Mock對象,控制其返回值,監(jiān)控其方法的調(diào)用。
org.mockito:mockito-all:(version)

// import如相關(guān)類
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

// 創(chuàng)建一個Mock的對象
 MyClass test = mock(MyClass.class);

// 當調(diào)用test.getUniqueId()的時候返回43
when(test.getUniqueId()).thenReturn(43);
// 當調(diào)用test.compareTo()傳入任意的Int值都返回43
when(test.compareTo(anyInt())).thenReturn(43);
// 當調(diào)用test.compareTo()傳入的是Target.class類型對象時返回43
when(test.compareTo(isA(Target.class))).thenReturn(43);
// 當調(diào)用test.close()的時候,拋IOException異常
doThrow(new IOException()).when(test).close();
// 當調(diào)用test.execute()的時候,什么都不做
doNothing().when(test).execute();

// 驗證是否調(diào)用了兩次test.getUniqueId()
// http://www.manongjc.com/article/1503.html
verify(test, times(2)).getUniqueId();
// 驗證是否沒有調(diào)用過test.getUniqueId()
verify(test, never()).getUniqueId();
// 驗證是否至少調(diào)用過兩次test.getUniqueId()
verify(test, atLeast(2)).getUniqueId();
// 驗證是否最多調(diào)用過三次test.getUniqueId()
verify(test, atMost(3)).getUniqueId();
// 驗證是否這樣調(diào)用過:test.query("test string")
verify(test).query("test string");

// 通過Mockito.spy() 封裝List對象并返回將其mock的spy對象
List list = new LinkedList();
List spy = spy(list);

// 指定spy.get(0)返回"foo"
doReturn("foo").when(spy).get(0);

assertEquals("foo", spy.get(0));

對訪問方法時,傳入?yún)?shù)進行快照

import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import static org.junit.Assert.assertEquals;

@Captor
private ArgumentCaptor<Integer> captor;

@Test
public void testCapture(){
  MyClass test = mock(MyClass.class);

  test.compareTo(3, 4);
  verify(test).compareTo(captor.capture(), eq(4));

  assertEquals(3, (int)captor.getValue());

  // 需要特別注意,如果是可變數(shù)組(vargars)參數(shù),如方法 test.doSomething(String... params)
  // 此時是使用ArgumentCaptor<String>,而非ArgumentCaptor<String[]>
  ArgumentCaptor<String> varArgs = ArgumentCaptor.forClass(String.class);
  test.doSomething("param-1", "param-2");
  verify(test).doSomething(varArgs.capture());

  // 這里直接使用getAllValues()而非getValue(),來獲取可變數(shù)組參數(shù)的所有傳入?yún)?shù)
  assertThat(varArgs.getAllValues()).contains("param-1", "param-2");
}

(1)對于靜態(tài)的方法的Mock:

可以使用 PowerMock:

org.powermock:powermock-api-mockito:(version) & org.powermock:powermock-module-junit4:(version)(For PowerMockRunner.class)

@RunWith(PowerMockRunner.class)
@PrepareForTest({StaticClass1.class, StaticClass2.class})
public class MyTest {

  @Test
  public void testSomething() {
    // mock完靜態(tài)類以后,默認所有的方法都不做任何事情
    mockStatic(StaticClass1.class);
    when(StaticClass1.getStaticMethod()).andReturn("anything");

    // 驗證是否StaticClass1.getStaticMethod()這個方法被調(diào)用了一次
    verifyStatic(time(1));
    StaticClass1.getStaticMethod();

    when(StaticClass1.getStaticMethod()).andReturn("what ever");

    // 驗證是否StaticClass2.getStaticMethod()這個方法被至少調(diào)用了一次
    verifyStatic(atLeastOnce());
    StaticClass2.getStaticMethod();

    // 通過任何參數(shù)創(chuàng)建File的實力,都直接返回fileInstance對象
    whenNew(File.class).withAnyArguments().thenReturn(fileInstance);
  }
}

或者是封裝為非靜態(tài),然后用Mockito:

class FooWraper{  void someMethod() {
    Foo.someStaticMethod();
  }
}

4. Robolectric

Robolectric
讓模擬測試直接在開發(fā)機上完成,而不需要在Android系統(tǒng)上。所有需要使用到系統(tǒng)架構(gòu)庫的,如(Handler、HandlerThread)都需要使用Robolectric,或者進行模擬測試。

主要是解決模擬測試中耗時的缺陷,模擬測試需要安裝以及跑在Android系統(tǒng)上,也就是需要在Android虛擬機或者設(shè)備上面,所以十分的耗時?;旧厦看蝸韥砘鼗囟夹枰獛追昼姇r間。針對這類問題,業(yè)界其實已經(jīng)有了一個現(xiàn)成的解決方案: Pivotal實驗室推出的Robolectric。通過使用Robolectrict模擬Android系統(tǒng)核心庫的Shadow Classes的方式,我們可以像寫本地測試一樣寫這類測試,并且直接運行在工作環(huán)境的JVM上,十分方便。

5. Robotium

RobotiumTech/robotium
(Integration Tests)模擬用戶操作,事件流測試。

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
public class MyActivityTest{

@Test
  public void doSomethingTests(){
    // 獲取Application對象
    Application application = RuntimeEnvironment.application;

    // 啟動WelcomeActivity
    WelcomeActivity activity = Robolectric.setupActivity(WelcomeActivity.class);
    // 觸發(fā)activity中Id為R.id.login的View的click事件
    // http://www.manongjc.com/article/1502.html
    activity.findViewById(R.id.login).performClick();

    Intent expectedIntent = new Intent(activity, LoginActivity.class);
    // 在activity之后,啟動的Activity是否是LoginActivity
    assertThat(shadowOf(activity).getNextStartedActivity()).isEqualTo(expectedIntent);
  }
}

通過模擬用戶的操作的行為事件流進行測試,這類測試無法避免需要在虛擬機或者設(shè)備上面運行的。是一些用戶操作流程與視覺顯示強相關(guān)的很好的選擇。

?

6. Test Butler

linkedin/test-butler
避免設(shè)備/模擬器系統(tǒng)或者環(huán)境的錯誤,導致測試的失敗。

通常我們在進行UI測試的時候,會遇到由于模擬器或者設(shè)備的錯誤,如系統(tǒng)的crash、ANR、或是未預(yù)期的Wifi、CPU罷工,或者是鎖屏,這些外再環(huán)境因素導致測試不過。Test-Butler引入就是避免這些環(huán)境因素導致UI測試不過。

該庫被谷歌官方推薦過,并且收到谷歌工程師的Review。

?

拓展思路

1. Android Robots

Instrumentation Testing Robots – Jake Wharton

假如我們需要測試: 發(fā)送 $42 到 “foo@bar.com”,然后驗證是否成功。

(1)通常的做法

Detailed explanation of Android unit testing and simulation testing

Detailed explanation of Android unit testing and simulation testing

(2)Robot思想

在寫真正的UI測試的時候,只需要關(guān)注要測試什么,而不需要關(guān)注需要怎么測試,換句話說就是讓測試邏輯與View或Presenter解耦,而與數(shù)據(jù)產(chǎn)生關(guān)系。

首先通過封裝一個Robot去處理How的部分:

Detailed explanation of Android unit testing and simulation testing

然后在寫測試的時候,只關(guān)注需要測試什么:

Detailed explanation of Android unit testing and simulation testing

終的思想原理

Detailed explanation of Android unit testing and simulation testing

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undress AI Tool

Undress AI Tool

Undress images for free

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)