Android单元测试– JUnit4

时间:2020-02-23 14:29:22  来源:igfitidea点击:

在本教程中,我们将讨论Android单元测试,该测试构成了Android应用程序开发的组成部分。
我们将使用JUnit4具体实施本地单元测试。

Android单元测试

顾名思义,单元测试就是测试代码的每个单元。

要构建可靠的应用程序,必须进行单元测试。
这是构建高质量应用程序时的重要元素。

单元测试由测试用例组成,这些用例用于检查代码的业务逻辑。
很多时候,当您被要求或者计划在一个正在运行的应用程序中添加功能时,只是意识到它破坏了代码的其他部分。
每次重构代码或者其中添加新内容时,都无法手动执行所有测试。
这就是单元测试为我们提供帮助的地方。

它执行快速的自动测试,并在任何测试失败时提醒您。
您可以快速找出问题所在。

一般而言,测试大致分为以下几种类型:

  • 单元测试
  • 整合测试
  • UI测试

单元测试是最小的(单独地)并且执行时间最少。

以下是量化Google文档中每种测试类型的图示:

以下是Android中使用的一些测试框架:

  • JUnit
  • Mockito
  • Powermock
  • Robolectric
  • Espresso
  • Hamcrest

每当您启动一个新的Android Studio项目时,build.gradle(也称为Expresso Dependency)中已经存在JUnit依赖关系。

在您的Android Studio项目中,以下是src文件夹中的三个重要软件包:

  • app/src/main/java /-主Java源代码文件夹。

  • app/src/test/java /-本地单元测试文件夹。

  • app/src/androidTest/java /-仪器测试文件夹。

test文件夹是编写JUnit4测试用例的位置。

本地单元测试不能具有Android API。
测试文件夹类仅在JVM上编译和运行。

仪器测试在Android设备或者仿真器上运行。

为了创建测试,我们需要使用TestCase扩展类,或者在方法上方添加注解@Test。
TestCase主要用于JUnit3。
展望未来,我们只需要设置注释即可。

让我们创建一个新的Android Studio项目,其中编写第一个单元测试。

在以下部分中,我们创建了一个基本应用程序,其中将检查字符串是否为有效的电子邮件地址。
为此,我们还将在"活动"中创建一个EditText。

通过编写单元测试,我们将了解如何通过涵盖各种最终条件来改善应用程序逻辑。

代码

让我们为" activity_main.xml"编写代码。
布局如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".MainActivity">

  <EditText
      android:id="@+id/inEmail"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:inputType="textEmailAddress"
      android:text="Enter your email here"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toTopOf="parent" 

  <Button
      android:id="@+id/button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="16dp"
      android:text="CHECK"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/inEmail" 

</android.support.constraint.ConstraintLayout>

构建测试用例并完成TDD(测试驱动开发)之后,我们将在稍后查看MainActivity.java代码。

Utils.java类中的代码是:

package com.theitroad.androidunittestingjunit4;

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Utils {

  private static final int MILLIS = 1000;

  public static boolean checkEmailForValidity(String email) {

      Matcher matcher = VALID_EMAIL_ADDRESS_REGEX.matcher(email);
      return matcher.find();

  }

  private static final Pattern VALID_EMAIL_ADDRESS_REGEX =
          Pattern.compile("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE);

  public static Date calendarDate(long epocSeconds) {
      Calendar c = Calendar.
              getInstance(TimeZone.getTimeZone("UTC"));
      c.setTimeInMillis(epocSeconds * MILLIS);
      return c.getTime();
  }

}

现在让我们为这两种方法编写单元测试:test/java文件夹中的checkEmailForValidity和calendarDate。

单元测试用例1

创建一个新的Java文件UtilsTest.java并添加以下代码:

package com.theitroad.androidunittestingjunit4;
import org.junit.Assert;
import org.junit.Test;

import java.util.Date;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;

public class UtilsTest {

  @Test
  public void testIsEmailValid() {
      String testEmail = "[email protected]";
      Assert.assertThat(String.format("Email Validity Test failed for %s ", testEmail), Utils.checkEmailForValidity(testEmail), is(true));
  }

  @Test
  public void testCheckDateWasConvertedCorrectly() {
      long inMillis = System.currentTimeMillis();
      Date date = Utils.calendarDate(inMillis);
      assertEquals("Date time in millis is wrong",
              inMillis * 100, date.getTime());
  }

}

在第一个测试中,我们调用在main/java文件夹中定义的方法checkEmailForValidity。
我们通过一个测试字符串来检查assertThat方法内部的有效性。

在第二个测试用例中,我们故意将timeInMillis乘以100而不是1000,从而错误地将timeInMillis转换为以秒为单位的时间。
这里我们使用assertEquals函数。

您可以通过gradle运行单元测试方法,也可以单击它们旁边的运行图标。
使用gradle只需在Android Studio的终端上执行命令gradlew test即可。

让我们看一下每种方法运行时的输出。

除了上述两个assert方法外,还有很多其他方法:

看起来" assertThat"和" assertEquals"具有类似的方法定义。
两者都有一个可选的第一个参数,它是测试失败时显示的消息,后跟预期和实际值。

具有讽刺意味的是,assertThat和assertEquals彼此完全不同。

assertThat与assertEquals

assertThat包含了Hamcrest库,该库提高了代码的可读性。
Hamcrest库由称为匹配器的静态方法组成。

让我们比较一下这两种方法的语法:

assertEquals(expected, actual);

assertThat(actual, is(equalTo(expected)));

assertThat首先具有实际值。
多亏了is方法,它提高了可读性。
在assertEquals方法中,您可以很容易混淆并互换实际和期望的参数位置。

断言是安全且简短的类型。
示例:假定foo是以下代码中的对象实例

assertTrue(foo.contains("someValue") && foo.contains("anotherValue"));

用assertThat编写时,同样的事情变成:

assertThat(foo, hasItems("someValue", "anotherValue"));

因此,assertThat应该是其他方法的首选方法。

回到我们的应用程序,让我们添加另一个测试用例。

单元测试用例2

@Test
  public void testEmailValidityPartTwo() {
      String testEmail = "   [email protected]  ";
      Assert.assertThat(String.format("Email Validity Test failed for %s ", testEmail), Utils.checkEmailForValidity(testEmail), is(true));
  }

其中我们在测试字符串之外添加了白色间距。
显然,这将失败。

这提醒我们在checkEmailForValidity方法中修剪白色间距。
我们可以在Utils.java类中的字符串上设置trim()方法。

public static boolean checkEmailForValidity(String email) {

      email = email.trim();

      Matcher matcher = VALID_EMAIL_ADDRESS_REGEX.matcher(email);
      return matcher.find();

  }

您的MainActivity.java代码如下所示:

package com.theitroad.androidunittestingjunit4;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      final EditText editText = findViewById(R.id.inEmail);
      Button button = findViewById(R.id.button);

      button.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {
              boolean isValid = Utils.checkEmailForValidity(editText.getText().toString());

              if (isValid) {
                  Toast.makeText(getApplicationContext(), "Email is valid", Toast.LENGTH_LONG).show();
              } else {
                  Toast.makeText(getApplicationContext(), "Email not valid", Toast.LENGTH_LONG).show();
              }
          }
      });
  }
}

无需运行您的应用程序来测试电子邮件是否有效,我们只需运行我们之前编写的JVM测试即可。