单元测试时来自 Toast 的 NullPointerException

NullPointerException from Toast while Unit Testing

我正在测试我的 Signup class 的一个功能,但是每次我 运行 它在第一个 if 语句中出现 NullPointerException:

pass.show()

我怀疑这与未使用模拟器测试应用程序有关。当我测试这个时,如何防止创建 Toast 的错误?

注册函数Class:

public boolean checkInput(String username, String email, String password, String confirmpassword) {
    if (!email.contains("@") || !email.contains(".com")) {
        Toast pass = Toast.makeText(Signup.this, "Email must be valid!", Toast.LENGTH_SHORT);
        pass.show();
        return false;
    }
    if (!password.equals(confirmpassword)) {
        Toast pass = Toast.makeText(Signup.this, "Passwords don't match!", Toast.LENGTH_SHORT);
        pass.show();
        return false;
    }
    if (!(password.length() >= 6)) {
        Toast pass = Toast.makeText(Signup.this, "Password must be at least 6 characters", Toast.LENGTH_SHORT);
        pass.show();
        return false;
    }
    else {
        return true;
    }
}

测试方法:

    @Test
    public void testInvalidEmail() {

    String testUser = "testUser";
    String testEmail = "testEmail.com"; //invalid
    String testPass = "testPass";
    String testConfirmPass = "testPass";

    assertFalse(signupClass.checkInput(testUser,testEmail,testPass,testConfirmPass));
}

如果你想做的是单元测试,那么你需要为 Android 东西(比如 Toast)解耦你的代码。

一种方法是创建一个界面来显示 Toast(或处理任何 Android 相关组件),以便您可以模拟它。

例如:

public interface AndroidInteraction (){
    void showToast(Context context, String text, int length);
}

并在您的 activity:

中实现该接口
public class SignUpActivity extends AppCompatActivity implements AndroidInteraction{
    .......
    ......

    @Override
    void showToast(Context context, String text, int length){
         Toast pass = Toast.makeText(context, text, length);
         pass.show();
    }

最后你的 Signup class 应该知道那个接口,所以你可以通过它抛出它的构造函数:

AndroidInteraction androidInteraction;
public Signup (AndroidInteraction androidInteraction){
    this.androidInteraction = androidInteraction;
}

所以你的方法现在看起来像(只有一个 if 子句来展示如何使用它,而不是整个方法):

if (!password.equals(confirmpassword)) {
    androidInteraction.showToast(Signup.this, "Passwords don't match!", Toast.LENGTH_SHORT);
    return false;
}

并且您需要在测试中模拟该接口并将其传递给 class 构造函数

不要在变量 'pass' 上传递 Toast,只需直接键入 'Toast' 并单击第二个 suggestion.the 我也删除你的 return。

这是我在您的代码中看到的问题:

吐司通=Toast.makeText(.....);

如果你想把它放在变量上,这样做

Toast pass = new Toast(getContext());


pass.makeText (.....).show();

pass.makeText(....);
pass.show();

所以我的建议是选择并尝试这个:

public boolean checkInput(String username, String email, String password, String confirmpassword) {
if (!email.contains("@") || !email.contains(".com")) {
    Toast.makeText(Signup.this, "Email must be valid!", Toast.LENGTH_SHORT).show();

}
if (!password.equals(confirmpassword)) {
   Toast.makeText(Signup.this, "Passwords don't match!", Toast.LENGTH_SHORT).show();

}
if (!(password.length() >= 6)) {
    Toast pass = Toast.makeText(Signup.this, "Password must be at least 6 characters", Toast.LENGTH_SHORT).show();
   }
}

您需要将 Toast 与您的函数分离。为此,请按照以下步骤操作,我将在其中使用您显示的示例并完全根据您的需要实施它们。希望这能回答您的问题,并且您能够跟进。

首先创建一个用于打印(toasting)的界面

interface Printer {
    void print(String message);
}

然后让您的 Signup class 实现 PrinterToast 与您的函数分离。我相信你的 SignupActivity.

public class Signup extends AppCompatActivity implements Printer {

    // ... other codes ...

    @Override
    public void print(String message) {
        Toast pass = Toast.makeText(Signup.this, message, Toast.LENGTH_SHORT);
        pass.show();
    }
}

那么现在,在你的checkInput函数中删除你的Toast,并通过发送到函数

printer执行打印
public boolean checkInput(String username, String email, String password, String confirmpassword, Printer printer) {
    if (!email.contains("@") || !email.contains(".com")) {
        printer.print("Email must be valid!");
        return false;
    }
    if (!password.equals(confirmpassword)) {
        printer.print("Passwords don't match!");
        return false;
    }
    if (!(password.length() >= 6)) {
        printer.print("Password must be at least 6 characters");
        return false;
    }
    else {
        return true;
    }
}

注意:从您的 class 调用 checkInput 的任何函数都需要在 Printer 中发送,如果它在 [=16] 中,则实际上是 this =] class 例如

checkInput(testUser, testEmail, testPass, testConfirmPass, this)l;

现在在您的测试中,只需实施 Mock Printer 例如

@Test
public void testInvalidEmail(MainActivity mainActivity) {

    String testUser = "testUser";
    String testEmail = "testEmail.com"; //invalid
    String testPass = "testPass";
    String testConfirmPass = "testPass";

    assertFalse(signUp.checkInput(testUser, testEmail, testPass, testConfirmPass, new Printer() {
        @Override
        public void print(String message) {
            System.out.println(message);
        }
    }));
}

这样,您就可以按照自己的意愿完成测试,而您的应用程序仍会通过 Toast

进行打印