Модульное тестирование Android с помощью Mockito - PullRequest
0 голосов
/ 26 октября 2019

У меня есть ViewModel, в которой есть метод со следующей строкой кода:

billDate.set(!TextUtils.isEmpty(SampleApp.getInstance().getAccountManager().getDueDate()) ?
            String.format(SampleApp.getInstance().getApplicationContext().getString(R.string.due),
                    SampleApp.getInstance().getAccountManager().getBillingDueDate()) :
            SampleApp.getInstance().getApplicationContext().getString(R.string.missing_due_date));

У меня есть тестовый класс, использующий Mockito для тестирования различных методов в ViewModel. Но он не работает с NullPointerException в этой строке:

String.format(SampleApp.getInstance().getApplicationContext().getString(R.string.due),

Ниже приведен журнал:

java.lang.NullPointerException
at java.util.regex.Matcher.getTextLength(Matcher.java:1283)
at java.util.regex.Matcher.reset(Matcher.java:309)
at java.util.regex.Matcher.<init>(Matcher.java:229)
at java.util.regex.Pattern.matcher(Pattern.java:1093)
at java.util.Formatter.parse(Formatter.java:2547)
at java.util.Formatter.format(Formatter.java:2501)
at java.util.Formatter.format(Formatter.java:2455)
at java.lang.String.format(String.java:2940)

Во время выполнения теста я вижу журнал, показывающий некоторую ошибку, связанную сPattern Может кто-нибудь подсказать, как проверить метод String.format()?

Ответы [ 3 ]

1 голос
/ 04 ноября 2019

Вам не нужно проверять метод String.format. Это не ваш код, и ваша цель должна состоять в том, чтобы протестировать свой собственный код. Но ваш код использует этот метод, поэтому вам нужно протестировать ваш код. Это часть, которую вы пытаетесь проверить или смоделировать, насколько я понимаю:

String.format(SampleApp.getInstance().getApplicationContext().getString(R.string.due), SampleApp.getInstance().getAccountManager().getBillingDueDate()) 

, которая делает несколько вызовов SampleApp для получения экземпляра. Поскольку эти вызовы SampleApp.getInstance являются вызовами статических методов, вы не сможете их макетировать. Отправлено недостаточно кода для того, чтобы узнать, что такое SampleApp или что возвращает SampleApp.getInstance(), или чтобы узнать, возвращаются ли какие-либо из последующих вызовов в этом экземпляре в нулевое значение, но один из них есть. Поэтому я думаю, что для решения этой проблемы вам нужно посмотреть, что возвращает метод getInstance. Если вы не можете прикоснуться к этому коду и надеетесь только изменить ваши тестовые классы, вы не сможете проверить это с помощью mockito из-за статического метода.

Но в противном случаевам нужно будет создать способ для ваших тестов, чтобы при вызове SampleApp.getInstance возвращался объект mock в качестве экземпляра вместо любого реального экземпляра, который, как я предполагаю, он сейчас возвращает. Затем вы можете смоделировать последующие методы, такие как getApplicationContext и getString, чтобы они возвращали постоянные ответы, чтобы при вызове string.format не происходил сбой при нулевом вводе.

Одно замечание - если вы все-таки получитезаставляя статический метод getInstance возвращать макет, но убедитесь, что у вас есть надлежащая очистка, когда ваш тест завершен, чтобы вернуть его к тому, что он возвращал изначально, чтобы вы случайно не изменили что-то, что может привести к сбою другого несвязанного модульного теста. Это всегда риск, если вы изменяете что-то, возвращаемое статическим методом в модульном тесте, поскольку вы эффективно меняете его для всех тестов.

1 голос
/ 26 октября 2019

Прежде всего, вы не должны импортировать пакеты просмотра Android в вашу ViewModel. Поэтому пропустите использование таких вещей, как TextUtils внутри ViewModels.

Что касается getApplicationContext().getString(), создайте для этого интерфейс. Что-то вроде:

interface StringProvider {
    String getString(int resource);
}

Затем передайте этот интерфейс в конструкторе ViewModel и используйте его для получения нужной строки.

Когда вы инициализируете ViewModel, вы можете передать конкретную реализацию StringProvider вот так:

class StringProviderImpl implements StringProvider {
     String getString(int resource) {
        return SampleApp.getInstance().getApplicationContext().getString(resource);
     }
}

Таким образом, для ваших модульных тестов вы можете просто смоделировать StringProvider и вам не нужно беспокоиться о работе с контекстами внутри вашей ViewModel и связанного тестового кода.

0 голосов
/ 05 ноября 2019

Учитывая, что тест не пройден после того, как AccountManager уже использовался, вы должны были установить SampleApp как ложный или фальшивый.

SampleApp app = SampleApp.getInstance()
AccountManager am = app.getAccountManager();
Context context = app.getApplicationContext();
billDate.set(!TextUtils.isEmpty(am.getDueDate()) ?
    String.format(context.getString(R.string.due), am.getBillingDueDate()) :
    context.getString(R.string.missing_due_date);

Теперь вам нужно только убедиться, чтовысмеивайте Context, который вы предоставите app.getApplicationContext() или самим SampleApp, если вы используете app.getString() напрямую.

doReturn(dueFormatString).when(context).getString(R.string.due);
doReturn(dueMissingString).when(context).getString(R.string.missing_due_date);

Но в целом вы должны абстрагироваться от Context. Если вы его не используете, это упростит ваш код и, следовательно, ваше тестирование.

Также рассмотрите возможность использования context.getString() вместо String.format() для форматирования строки, загружаемой из ресурса. Это так же просто, как добавить аргументы формата в качестве параметров к вызову.

context.getString(R.string.due, am.getBillingDueDate())
...