NullPointerException от Toast при модульном тестировании

Я тестирую функцию моего класса Registration, однако каждый раз, когда я ее запускаю, внутри первого оператора if возникает исключение NullPointerException:

pass.show()

Я подозреваю, что это как-то связано с не тестированием приложения с помощью эмулятора. Как я могу предотвратить ошибки при создании Toast, когда я тестирую это?

Функция в классе регистрации:

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));
}

3 ответа

Решение

Вам нужно будет отделить Toast от вашей функции. Чтобы сделать это, выполните следующий шаг, где я использую ваш показанный пример и реализую их точно так, как вам нужно. Надеется, что ответ на ваш запрос, и вы могли бы выполнить.

Сначала создайте интерфейс для печати (тост)

interface Printer {
    void print(String message);
}

Тогда сделай свой Signup класс реализует Printer отделить Toast от вашей функции. Я верю твоему Signup является Activity,

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();
    }
}

Затем удалите Toast в вашем checkInput и выполнить печать через 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;
    }
}

Примечание: какая функция вызывает checkInput от вашего класса нужно будет отправить в Printerчто на самом деле this если это в Signup класс например

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

Теперь в вашем тесте, просто внедрите макет 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

Если вы хотите использовать UnitTesting, то вам нужно отделить код для Android-приложений (например, Toast).

Одним из способов является создание интерфейса для отображения Toast (или обработки любого компонента, связанного с Android), чтобы вы могли его высмеивать.

Например:

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

И внедрите этот интерфейс в свою деятельность:

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();
    }

И наконец, ваш класс Registration должен знать об этом интерфейсе, чтобы вы могли передать его через его конструктор:

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;
}

И вам нужно будет смоделировать этот интерфейс в вашем тесте и передать его конструктору класса

Вместо того, чтобы передавать Toast по переменной "pass", просто введите "Toast" и щелкните по второму предложению. Я также удаляю ваш возврат.

Это проблема, которую я видел в вашем коде:

Toast pass = 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();
   }
}
Другие вопросы по тегам