编写 android 测试单元该做的和不该做的事
创始人
2024-03-01 18:49:15
0

在本文中, 我将根据我的实际经验,为大家阐述一个编写测试用例的最佳实践。在本文中我将使用 Espresso 编码, 但是它们可以用到单元测试和 仪器测试 ( instrumentation test ) 当中。基于以上目的,我们来研究一个新闻程序。

以下内容纯属虚构,如有雷同纯属巧合 :P

一个新闻 APP 应该会有以下这些 activity。

  • 语言选择 - 当用户第一次打开软件, 他必须至少选择一种语言。选择后,选项保存在共享偏好中,用户跳转到新闻列表 activity。
  • 新闻列表 - 当用户来到新闻列表 activity,将发送一个包含语言参数的请求到服务器,并将服务器返回的内容显示在 recycler view 上(包含有新闻列表的 id, news_list)。 如果共享偏好中未存语言参数,或者服务器没有返回一个成功消息, 就会弹出一个错误对话框并且 recycler view 将不可见。如果用户只选择了一种语言,新闻列表 activity 有个 “Change your Language” 的按钮,或者如果用户选择多种语言,则按钮为 “Change your Languages” 。 (我对天发誓这是一个虚构的 APP 软件)
  • 新闻细节 - 如同名字所述, 当用户点选新闻列表项时将启动这个 activity。

这个 APP 功能已经足够,,让我们深入研究下为新闻列表 activity 编写的测试用例。 这是我第一次写的代码。

/*
    Click on the first news item.
    It should open NewsDetailActivity
     */
    @Test
    public void testClickOnAnyNewsItem() {
        onView(allOf(withId(R.id.news_list), isDisplayed())).perform(RecyclerViewActions
                .actionOnItemAtPosition(1, click()));
        intended(hasComponent(NewsDetailsActivity.class.getName()));
    }


  /**
   * To test the correct text on the button
   */
  @Test
  public void testChangeLanguageFeature() {
    int count = UserPreferenceUtil.getSelectedLanguagesCount();
    if (count == 1) {
      onView(withText("Choose your Language")).check(matches(isDisplayed()));
    } else if (count > 1) {
      onView(withText("Choose your Languages")).check(matches(isDisplayed()));
    }
 ?}

仔细想想测试什么

在第一个测试用例 testClickOnAnyNewsItem(), 如果服务器没有返回成功信息,测试用例将会返回失败,因为 recycler view 是不可见的。但是这个测试用例的目的并非如此。 不管该用例为 PASS 还是 FAIL,它的最低要求是 recycler view 总是可见的, 如果因某种原因,recycler view 不可见,那么测试用例不应视为 FAILED。正确的测试代码应该像下面这个样子。

 /*
  Click on any news item.
  It should open NewsDetailActivity
   */
  @Test
  public void testClickOnAnyNewsItem() {
    try {
            /*To test this case, we need to have recyclerView present. If we don't have the
            recyclerview present either due to the presence of error_screen, then we should consider
            this test case successful. The test case should be unsuccesful only when we click on a
            news item and it doesn't open NewsDetail activity
            */
      ViewInteraction viewInteraction = onView(withId(R.id.news_list));
      viewInteraction.check(matches(isDisplayed()));
    } catch (NoMatchingViewException e) {
      return;
    } catch (AssertionFailedError e) {
      return;
    }
    //在这里我们确信,news_list的 recyclerview 对用户是可见的。
    onView(allOf(withId(R.id.news_list), isDisplayed())).perform(RecyclerViewActions
        .actionOnItemAtPosition(1, click()));
    intended(hasComponent(NewsDetailsActivity.class.getName()));
  }
}

一个测试用例本身应该是完整的

当我开始测试, 我通常按如下顺序测试 activity:

  • 语言选择
  • 新闻列表
  • 新闻细节

因为我首先测试语言选择 activity,在测试 NewsList activity 之前,总有一种语言已经是选择好了的。但是当我先测试新闻列表 activity 时,测试用例开始返回错误信息。原因很简单 - 没有选择语言,recycler view 不会显示。注意, 测试用例的执行顺序不能影响测试结果。 因此在运行测试用例之前, 语言选项必须是保存在共享偏好中的。在本例中,测试用例独立于语言选择 activity 的测试。

@Rule
  public ActivityTestRule activityTestRule =
      new ActivityTestRule(TopicsActivity.class, false, false);

  /*
  Click on any news item.
  It should open NewsDetailActivity
   */
  @Test
  public void testClickOnAnyNewsItem() {
    UserPreferenceUtil.saveUserPrimaryLanguage("english");
    Intent intent = new Intent();
    activityTestRule.launchActivity(intent);
    try {
      ViewInteraction viewInteraction = onView(withId(R.id.news_list));
      viewInteraction.check(matches(isDisplayed()));
    } catch (NoMatchingViewException e) {
      return;
    } catch (AssertionFailedError e) {
      return;
    }
    onView(allOf(withId(R.id.news_list), isDisplayed())).perform(RecyclerViewActions
        .actionOnItemAtPosition(1, click()));
    intended(hasComponent(NewsDetailsActivity.class.getName()));
 ?}

在测试用例中避免使用条件代码

现在在第二个测试用例 testChangeLanguageFeature() 中,我们获取到用户选择语言的个数,基于这个数目,我们写了 if-else 条件来进行测试。 但是 if-else 条件应该写在你的代码当中,而不是测试代码里。每一个条件应该单独测试。 因此,在本例中,不是只写一条测试用例,而是要写如下两个测试用例。

/**
   * To test the correct text on the button when only one language is selected.
   */
  @Test
  public void testChangeLanguageFeatureForSingeLanguage() {
    //Other initializations
    UserPreferenceUtil.saveSelectedLanguagesCount(1);
    Intent intent = new Intent();
    activityTestRule.launchActivity(intent);
      onView(withText("Choose your Language")).check(matches(isDisplayed()));
  }

  /**
   * To test the correct text on the button when more than one language is selected.
   */
  @Test
  public void testChangeLanguageFeatureForMultipleLanguages() {
    //Other initializations
    UserPreferenceUtil.saveSelectedLanguagesCount(5); //Write anything greater than 1.
    Intent intent = new Intent();
    activityTestRule.launchActivity(intent);
    onView(withText("Choose your Languages")).check(matches(isDisplayed()));
  }

测试用例应该独立于外部因素

在大多数应用中,我们与外部网络或者数据库进行交互。一个测试用例运行时可以向服务器发送一个请求,并获取成功或失败的返回信息。但是不能因从服务器获取到失败信息,就认为测试用例没有通过。这样想这个问题 - 如果测试用例失败,然后我们修改客户端代码,以便测试用例通过。 但是在本例中, 我们要在客户端进行任何更改吗?- NO

但是你应该也无法完全避免要测试网络请求和响应。由于服务器是一个外部代理,我们可以设想一个场景,发送一些可能导致程序崩溃的错误响应。因此,你写的测试用例应该覆盖所有可能来自服务器的响应,甚至包括服务器决不会发出的响应。这样可以覆盖所有代码,并能保证应用可以处理所有响应,而不会崩溃。

正确的编写测试用例与编写这些测试代码同等重要。

感谢你阅读此文章。希望对测试用例写的更好有所帮助。你可以在 LinkedIn 上联系我。还可以在这里阅读我的其他文章。

获取更多资讯请关注我们, 我们发新文章时您将获得通知。


via: https://blog.mindorks.com/the-dos-and-don-ts-of-writing-test-cases-in-android-70f1b5dab3e1

作者:Anshul Jain 译者:kokialoves 校对:jasminepeng

本文由 LCTT 原创编译,Linux中国 荣誉推出

相关内容

HttpRunner3.x...
在httprunner中,测试用例有三层结构...
2025-05-29 11:49:53
不知何故,测试用例0既不输...
这个问题可以通过使用条件判断语句来解决。下面是一个示例代码:def...
2025-01-12 12:01:28
不知道如何使用unitte...
要在Python中使用unittest模块编写工作日的测试用例,可...
2025-01-12 06:00:55
不要在 Catch2 测试...
在Catch2测试用例中,如果需要使用组合生成器(例如Genera...
2025-01-11 01:01:08
不同测试用例之间的打印格式...
以下是一个解决方法的代码示例:import unittestcla...
2025-01-08 09:31:08

热门资讯

Helix:高级 Linux ... 说到 基于终端的文本编辑器,通常 Vim、Emacs 和 Nano 受到了关注。这并不意味着没有其他...
使用 KRAWL 扫描 Kub... 用 KRAWL 脚本来识别 Kubernetes Pod 和容器中的错误。当你使用 Kubernet...
JStock:Linux 上不... 如果你在股票市场做投资,那么你可能非常清楚投资组合管理计划有多重要。管理投资组合的目标是依据你能承受...
Epic 游戏商店现在可在 S... 现在可以在 Steam Deck 上运行 Epic 游戏商店了,几乎无懈可击! 但是,它是非官方的。...
《Apex 英雄》正式可在 S... 《Apex 英雄》现已通过 Steam Deck 验证,这使其成为支持 Linux 的顶级多人游戏之...
从 Yum 更新中排除特定/某... 作为系统更新的一部分,你也许需要在基于 Red Hat 系统中由于应用依赖排除一些软件包。如果是,如...
通过 SaltStack 管理... 我在搜索Puppet的替代品时,偶然间碰到了Salt。我喜欢puppet,但是我又爱上Salt了:)...
如何在 Github 上创建一... 学习如何复刻一个仓库,进行更改,并要求维护人员审查并合并它。你知道如何使用 git 了,你有一个 G...
Opera 浏览器内置的 VP... 昨天我们报道过 Opera 浏览器内置了 VPN 服务,用户打开它可以防止他们的在线活动被窥视。不过...
如何检查你的 Linux 系统... 不知道在使用哪个初始化系统?以下是方法。每个主流 Linux 发行版(包括 Ubuntu、Fedor...