Mockito при создании объекта принадлежит частному методу другого класса [duplicate]

    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "CHANNEL_ID")
            ........

    NotificationManager mNotificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        CharSequence name = "Hello";// The user-visible name of the channel.
        int importance = NotificationManager.IMPORTANCE_HIGH;
        NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
        mNotificationManager.createNotificationChannel(mChannel);
    }
    mNotificationManager.notify(notificationId, notificationBuilder.build());
37
задан Mureinik 15 January 2016 в 08:02
поделиться

6 ответов

В будущем я бы рекомендовал ответить Eran Harel (рефакторинг перемещения new на завод, который можно издеваться). Но если вы не хотите менять исходный код, используйте очень удобную и уникальную функцию: шпионы. Из документации :

Вы можете создавать шпионы реальных объектов. Когда вы используете шпиона, тогда вызываются реальные методы (если только метод не был заглушен).

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

В вашем случае вы должны написать:

TestedClass tc = spy(new TestedClass());
LoginContext lcMock = mock(LoginContext.class);
when(tc.login(anyString(), anyString())).thenReturn(lcMock);
42
ответ дан Tomasz Nurkiewicz 3 September 2018 в 16:04
поделиться

В ситуациях, когда тестируемый класс может быть изменен, и когда желательно избегать манипулирования байтовым кодом, чтобы ускорить работу или свести к минимуму зависимость от третьей стороны, вот мой вопрос о том, как использовать фабрику для извлечения new операция.

public class TestedClass {

    interface PojoFactory { Pojo getNewPojo(); }

    private final PojoFactory factory;

    /** For use in production - nothing needs to change. */
    public TestedClass() {
        this.factory = new PojoFactory() {
            @Override
            public Pojo getNewPojo() {
                return new Pojo();
            }
        };
    }

    /** For use in testing - provide a pojo factory. */
    public TestedClass(PojoFactory factory) {
        this.factory = factory;
    }

    public void doSomething() {
        Pojo pojo = this.factory.getNewPojo();
        anythingCouldHappen(pojo);
    }
}

При этом ваше тестирование, подтверждение и проверка вызовов объекта Pojo просты:

public  void testSomething() {
    Pojo testPojo = new Pojo();
    TestedClass target = new TestedClass(new TestedClass.PojoFactory() {
                @Override
                public Pojo getNewPojo() {
                    return testPojo;
                }
            });
    target.doSomething();
    assertThat(testPojo.isLifeStillBeautiful(), is(true));
}

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

По причинам SOLID вы, вероятно, захотите поместить интерфейс PojoFactory в класс Pojo, а также на фабрику.

public class Pojo {

    interface PojoFactory { Pojo getNewPojo(); }

    public static final PojoFactory productionFactory = 
        new PojoFactory() {
            @Override 
            public Pojo getNewPojo() {
                return new Pojo();
            }
        };
0
ответ дан Adam 3 September 2018 в 16:04
поделиться

Вы можете использовать фабрику для создания контекста входа. Затем вы можете издеваться над фабрикой и возвращать все, что хотите, для своего теста.

public class TestedClass {
  private final LoginContextFactory loginContextFactory;

  public TestedClass(final LoginContextFactory loginContextFactory) {
    this.loginContextFactory = loginContextFactory;
  }

  public LoginContext login(String user, String password) {
    LoginContext lc = loginContextFactory.createLoginContext();
  }
}

public interface LoginContextFactory {
  public LoginContext createLoginContext();
}
18
ответ дан Eran Harel 3 September 2018 в 16:04
поделиться

Я все для решения Eran Harel, и в тех случаях, когда это невозможно, предложение Томаша Нуркевича о шпионаже превосходно. Однако стоит отметить, что бывают ситуации, когда ни один из них не применим. Например. если метод login был бит «beefier»:

public class TestedClass {
    public LoginContext login(String user, String password) {
        LoginContext lc = new LoginContext("login", callbackHandler);
        lc.doThis();
        lc.doThat();
    }
}

... и это был старый код, который нельзя было реорганизовать, чтобы извлечь инициализацию нового LoginContext в его собственный метод и примените одно из вышеупомянутых решений.

Для полноты, стоит упомянуть третий метод - используя PowerMock , чтобы ввести макет объекта при вызове оператора new. Однако PowerMock не является серебряной пулей. Он работает, применяя манипуляции с байт-кодом в классах, которые он издевается, что может быть изворотливой практикой, если тестируемые классы используют манипуляцию или отражение кода байта и, по крайней мере, из моего личного опыта, как известно, приводят к результату, связанному с тестированием. Опять же, если других опций нет, единственная опция должна быть хорошей опцией:

@RunWith(PowerMockRunner.class)
@PrepareForTest(TestedClass.class)
public class TestedClassTest {

    @Test
    public void testLogin() {
        LoginContext lcMock = mock(LoginContext.class);
        whenNew(LoginContext.class).withArguments(anyString(), anyString()).thenReturn(lcMock);
        TestedClass tc = new TestedClass();
        tc.login ("something", "something else");
        // test the login's logic
    }
}
24
ответ дан Haldean Brown 3 September 2018 в 16:04
поделиться

Не то, что я знаю, но как насчет того, чтобы делать что-то подобное, когда вы создаете экземпляр TestedClass, который вы хотите проверить:

TestedClass toTest = new TestedClass() {
    public LoginContext login(String user, String password) {
        //return mocked LoginContext
    }
};

Другой вариант - использовать Mockito для создания экземпляра из TestedClass и пусть посмеянный экземпляр возвращает LoginContext.

2
ответ дан Kaj 3 September 2018 в 16:04
поделиться
    public class TestedClass {
    public LoginContext login(String user, String password) {
        LoginContext lc = new LoginContext("login", callbackHandler);
        lc.doThis();
        lc.doThat();
    }
  }

- Класс тестирования:

    @RunWith(PowerMockRunner.class)
    @PrepareForTest(TestedClass.class)
    public class TestedClassTest {

        @Test
        public void testLogin() {
            LoginContext lcMock = mock(LoginContext.class);
            whenNew(LoginContext.class).withArguments(anyString(), anyString()).thenReturn(lcMock);
//comment: this is giving mock object ( lcMock )
            TestedClass tc = new TestedClass();
            tc.login ("something", "something else"); ///  testing this method.
            // test the login's logic
        }
    }

При вызове фактического метода tc.login ("something", "something else"); из testLogin () {- этот LoginContext lc установлен в значение null и бросает NPE при вызове lc.doThis();

2
ответ дан sunjavax 3 September 2018 в 16:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: