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());
В будущем я бы рекомендовал ответить Eran Harel (рефакторинг перемещения new
на завод, который можно издеваться). Но если вы не хотите менять исходный код, используйте очень удобную и уникальную функцию: шпионы. Из документации :
Вы можете создавать шпионы реальных объектов. Когда вы используете шпиона, тогда вызываются реальные методы (если только метод не был заглушен).
Настоящие шпионы следует использовать осторожно и иногда, например, при работе с устаревшим кодом.
blockquote>В вашем случае вы должны написать:
TestedClass tc = spy(new TestedClass()); LoginContext lcMock = mock(LoginContext.class); when(tc.login(anyString(), anyString())).thenReturn(lcMock);
В ситуациях, когда тестируемый класс может быть изменен, и когда желательно избегать манипулирования байтовым кодом, чтобы ускорить работу или свести к минимуму зависимость от третьей стороны, вот мой вопрос о том, как использовать фабрику для извлечения 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();
}
};
Вы можете использовать фабрику для создания контекста входа. Затем вы можете издеваться над фабрикой и возвращать все, что хотите, для своего теста.
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();
}
Я все для решения 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
}
}
Не то, что я знаю, но как насчет того, чтобы делать что-то подобное, когда вы создаете экземпляр TestedClass, который вы хотите проверить:
TestedClass toTest = new TestedClass() {
public LoginContext login(String user, String password) {
//return mocked LoginContext
}
};
Другой вариант - использовать Mockito для создания экземпляра из TestedClass и пусть посмеянный экземпляр возвращает LoginContext.
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();