Espresso 自动化测试(四)-中文字符的输入

前言

由于Espresso 也是用于做UI自动化测试的,所以我们难免要拿它来跟UiAutomator进行比较了。 使用过UiAutomator的都应该知道,它不支持中文的输入,为此Appium引入了专门的appium的输入法来解决这个问题,那我们来试试看Espresso是否能够支持中文呢。

中文的支持

 onView(withId(R.id.editTextUserInput)).perform(typeText("你好"));

代码很简单就是对一个文本框进行内容的输入。我们看看运行的结果

java.lang.RuntimeException: Failed to get key events for string 你好 (i.e. current IME does not understand how to translate the string into key events). As a workaround, you can use replaceText action to set the text directly in the EditText field.
at android.support.test.espresso.base.UiControllerImpl.injectString(UiControllerImpl.java:265)
at android.support.test.espresso.action.TypeTextAction.perform(TypeTextAction.java:105)
.....

好吧失败来,不开森了,难道Espresso也跟UiAutomator一样不支持中文的输入吗? 等等!! 我们好好的阅读下错误的信息

As a workaround, you can use replaceText action to set the text directly in the EditText field

原来这里已经给出了措施,直接使用replaceText 好的,我们重新试试看。

onView(withId(R.id.editTextUserInput)).perform(replaceText("你好"));

!!! 真的运行通过了,成功的输入了中文了。

不过问题还没完, 我们得搞懂typeText与replaceText的区别到底在哪里呢

查看源代码我们终于发现两者的区别了,typeText与replaceText方法分别是实例化了一个TypeTextAction以及ReplaceTextAction的对象,并且这两个类都实现了ViewAction 的接口。我们首先看看ReplaceTextAction 的perform方法的实现

@Override
  public void perform(UiController uiController, View view) {
    ((EditText) view).setText(stringToBeSet);
  }

原来如此一个直接是使用EditText的setText方法。

那下来我们再看看TypeTextAction的perform的方法实现吧。

@Override
  public void perform(UiController uiController, View view) {
    // No-op if string is empty.
    if (stringToBeTyped.length() == 0) {
      Log.w(TAG, "Supplied string is empty resulting in no-op (nothing is typed).");
      return;
    }

    if (tapToFocus) {
      // Perform a click.
      new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER, Press.FINGER)
          .perform(uiController, view);
      uiController.loopMainThreadUntilIdle();
    }

    try {
      if (!uiController.injectString(stringToBeTyped)) {
        Log.e(TAG, "Failed to type text: " + stringToBeTyped);
        throw new PerformException.Builder()
          .withActionDescription(this.getDescription())
          .withViewDescription(HumanReadables.describe(view))
          .withCause(new RuntimeException("Failed to type text: " + stringToBeTyped))
          .build();
      }
    } catch (InjectEventSecurityException e) {
      Log.e(TAG, "Failed to type text: " + stringToBeTyped);
      throw new PerformException.Builder()
        .withActionDescription(this.getDescription())
        .withViewDescription(HumanReadables.describe(view))
        .withCause(e)
        .build();
    }
  }

typeTextAction相比来说就复杂了很多了,我们重点看到

uiController.injectString(stringToBeTyped)

这里才是真正的注入字符串的地方,我们再细细一瞧

@Override
  public boolean injectString(String str) throws InjectEventSecurityException {
    checkNotNull(str);
    checkState(Looper.myLooper() == mainLooper, "Expecting to be on main thread!");
    initialize();

    // No-op if string is empty.
    if (str.length() == 0) {
      Log.w(TAG, "Supplied string is empty resulting in no-op (nothing is typed).");
      return true;
    }

    boolean eventInjected = false;
    KeyCharacterMap keyCharacterMap = getKeyCharacterMap();

    // TODO(user): Investigate why not use (as suggested in javadoc of keyCharacterMap.getEvents):
    // http://developer.android.com/reference/android/view/KeyEvent.html#KeyEvent(long,
    // java.lang.String, int, int)
    KeyEvent[] events = keyCharacterMap.getEvents(str.toCharArray());
    if (events == null) {
     throw new RuntimeException(String.format(
         "Failed to get key events for string %s (i.e. current IME does not understand how to "
          + "translate the string into key events). As a workaround, you can use replaceText action"
          + " to set the text directly in the EditText field.", str));
    }

    Log.d(TAG, String.format("Injecting string: \"%s\"", str));

    for (KeyEvent event : events) {
      checkNotNull(event, String.format("Failed to get event for character (%c) with key code (%s)",
          event.getKeyCode(), event.getUnicodeChar()));

      eventInjected = false;
      for (int attempts = 0; !eventInjected && attempts < 4; attempts++) {
        attempts++;

        // We have to change the time of an event before injecting it because
        // all KeyEvents returned by KeyCharacterMap.getEvents() have the same
        // time stamp and the system rejects too old events. Hence, it is
        // possible for an event to become stale before it is injected if it
        // takes too long to inject the preceding ones.
        event = KeyEvent.changeTimeRepeat(event, SystemClock.uptimeMillis(), 0);
        eventInjected = injectKeyEvent(event);
      }

      if (!eventInjected) {
        Log.e(TAG, String.format("Failed to inject event for character (%c) with key code (%s)",
            event.getUnicodeChar(), event.getKeyCode()));
        break;
      }
    }

    return eventInjected;
  }

其实看到这里我们大概就能明白了,typeText是通过模拟事件注入的方式,它将传入的字符串转成字符数组,再分别获取到对应的KeyEvent后直接进行注入。

实际上我们在查看typeText以及replaceText的操作现象的时候就能够发现 typeText的内容是一个个输进去的,但是replaceText是直接显示结果的,所以有时候你连操作都没看清,用例就已经跑完了。

结束

这篇主要是讲了下Espresso的中文输入,还是挺有意思的。后续还有很多类似于Espresso关于recyclerView的操作,对webview的操作。这些都是很有必要了解的。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值