我做了以下课程来接收Firebase消息并创建通知。我已经通过将Firebase消息发送到应用程序进行了测试,并且效果很好。但是,我需要编写单元测试来测试此功能。但是我为此编写的2个单元测试都失败了。
onMessageReceived()
接收RemoteMessage
对象中的数据。然后,它查找键的值,type
并根据键的值是0还是1,调用buildNotificationBigText()
或buildNotificationBigPicture()
。因此,我编写了2种测试方法。在测试方法中,我创建了一个RemoteMessage
对象并将其传递给onMessageReceived()
。但是,这些测试无法正常工作,并且出现以下错误:
java.lang.NullPointerException: Attempt to invoke virtual method 'void in.ac.bits_pilani.goa.ard.services.HandleFirebaseMessages.onMessageReceived(com.google.firebase.messaging.RemoteMessage)' on a null object reference at in.ac.bits_pilani.goa.ard.activities.MainActivityTest.handleFirebaseMessageBigPicture(MainActivityTest.java:90) java.lang.NullPointerException: Attempt to invoke virtual method 'void in.ac.bits_pilani.goa.ard.services.HandleFirebaseMessages.onMessageReceived(com.google.firebase.messaging.RemoteMessage)' on a null object reference at in.ac.bits_pilani.goa.ard.activities.MainActivityTest.handleFirebaseMessagesBigText(MainActivityTest.java:80)
HandleFirebaseMessages.java
public class HandleFirebaseMessages extends FirebaseMessagingService { /** * default id for notification in case specific id is not given in data. */ final int default_id = 42; NotificationCompat.Builder builder ; /** * contains bigTitle value of data in onMessageReceived(). */ String bigTitle; /** * is assigned the bigSummaryText value of data in onMessageReceived(). */ String bigSummaryText; public HandleFirebaseMessages() { } /** * returns the bitmap image from the url given. * @param src image url * @return bitmap image at url */ public static Bitmap getBitmapFromURL(final String src) { try { final URL url = new URL(src); final HttpURLConnection cOnnection= (HttpURLConnection) url.openConnection(); connection.setDoInput(true); connection.connect(); final InputStream input = connection.getInputStream(); return BitmapFactory.decodeStream(input); } catch (final IOException e) { // Log exception Log.e("TAG", "fucked man " + e.getMessage()); return null; } } /** * creates the notification when type is 1 or big text. * @param data data to be put in notification */ public void buildNotificationBigText(final Mapdata) { Log.e("Tag4", "entered buildNotificationBigText"); //final String bigTitle = data.get("bigTitle"); final String bigSubTitle = data.get("bigSubTitle"); //final String bigSummaryText = data.get("bigSummaryText"); final NotificationCompat.BigTextStyle notificatiOnBigText= new NotificationCompat.BigTextStyle(); if (bigTitle != null) { notificationBigText.setBigContentTitle(bigTitle); } if (bigSubTitle != null) { notificationBigText.bigText(bigSubTitle); } if (bigSummaryText != null) { notificationBigText.setSummaryText(bigSummaryText); } builder.setStyle(notificationBigText); } /** * creates the notification when type is 2 or big picture. * @param data data to be put in notification */ public void buildNotificationBigPicture(final Map data) { Log.e("Tag3", "entered buildNotificationBigPicture"); final String imageUrl = data.get("imageUrl"); //final String bigSummaryText = data.get("bigSummaryText"); //final String bigTitle = data.get("bigTitle"); final NotificationCompat.BigPictureStyle notificatiOnBigPicture= new NotificationCompat.BigPictureStyle(); if (imageUrl != null) { final Bitmap image = getBitmapFromURL(imageUrl); if (image != null) { notificationBigPicture.bigPicture(image); } else { //TODO Image is null bt url wasn;t! } } if (bigSummaryText != null) { notificationBigPicture.setSummaryText(bigSummaryText); } if (bigTitle != null) { notificationBigPicture.setBigContentTitle(bigTitle); } //TODO icon builder.setStyle(notificationBigPicture); } @Override public void onMessageReceived(final RemoteMessage remoteMessage) { super.onMessageReceived(remoteMessage); Log.e("Tag1", "onMessageReceived() started"); final Map data = remoteMessage.getData(); if (data == null) { return; } bigTitle = data.get("bigTitle"); bigSummaryText = data.get("bigSummaryText"); final String type = data.get("type"); final String id = data.get("id"); final String smallTitle = data.get("smallTitle"); final String smallSubTitle = data.get("smallSubTitle"); //final String cOntentInfo= data.get("contentInfo"); //final String ticker = data.get("ticker"); final String link = data.get("link"); final String className = data.get("className"); Log.e("Tag2", "type = " + type); builder = new NotificationCompat.Builder(this); if (type != null) { if (type.compareTo("1") == 0) { //Large Text Style corresponds to "1" buildNotificationBigText(data); } else if (type.compareTo("2") == 0) { //BigPicture style specific buildNotificationBigPicture(data); } } //General things to be added for any kind of notification if (smallTitle != null) { builder.setContentTitle(smallTitle); } if (smallSubTitle != null) { builder.setContentText(smallSubTitle); } int notificatiOnId= default_id; if (id != null) { notificatiOnId= Integer.parseInt(id); } builder.setContentIntent(addWebsiteLinkPendingIntent(notificationId, link, className)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { builder.setCategory(Notification.CATEGORY_SOCIAL); } builder.setSmallIcon(R.drawable.ic_stat); builder.setColor(ContextCompat.getColor(this, R.color.colorPrimary)); builder.setAutoCancel(true); final NotificationManagerCompat mNotificatiOnManager= NotificationManagerCompat.from(this); mNotificationManager.notify(notificationId, builder.build()); } /** * returns the intent for the webpage or activity to open from the notification. * @param id notification id * @param link webpage link * @param className class for the activity to open * @return PendingIntent for the webpage ot activity */ private PendingIntent addWebsiteLinkPendingIntent(final int id, final String link, final String className) { Intent intent; if (link != null) { //TODO Change to ChromeCustomTabs later intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link)); } else if (className != null) { try { intent = new Intent(this, Class.forName("com.csatimes.dojma." + className)); //TODO check for page number } catch (final ClassNotFoundException e) { intent = new Intent(this, MainActivity.class); } intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); } else { intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION); } return PendingIntent.getActivity( this, id, intent, PendingIntent.FLAG_CANCEL_CURRENT); } }
MainActivityTests.java:
@RunWith(AndroidJUnit4.class) public class MainActivityTest { private Context context; @Mock private HandleFirebaseMessages handleFirebaseMessages; @Rule public ActivityTestRuleactivityTestRule = new ActivityTestRule<>(MainActivity.class); @Before public void init() { cOntext= InstrumentationRegistry.getTargetContext(); //MockitoAnnotations.initMocks(this); //handleFirebaseMessages = new HandleFirebaseMessages(); } @Test public void handleFirebaseMessagesBigText() { HandleFirebaseMessages handleFirebaseMessages=new HandleFirebaseMessages(); RemoteMessage remoteMessage = new RemoteMessage.Builder("token").addData("type","1").build(); handleFirebaseMessages.onMessageReceived(remoteMessage); Map data = new HashMap<>() ; data.put("type","1"); Mockito.verify(handleFirebaseMessages).buildNotificationBigText(data); } @Test public void handleFirebaseMessagesBigPicture() { HandleFirebaseMessages handleFirebaseMessages=new HandleFirebaseMessages(); RemoteMessage remoteMessage = new RemoteMessage.Builder("token").addData("type","2").build(); handleFirebaseMessages.onMessageReceived(remoteMessage); Map data = new HashMap<>() ; data.put("type","2"); Mockito.verify(handleFirebaseMessages).buildNotificationBigPicture(data); } }
Jakub Szczyg.. 5
建议在测试时限制要测试的依赖项。您可以采用不依赖FirebaseMessagingService的方式来重构代码。
例如,您可以将其提取为独立于其他类的方法(最好与不依赖FirebaseMessagingService的其他类(以及HandleFirebaseMessages中的其他方法))分离,而不是将其逻辑放在onMessageReceived()中。它甚至可以进行结构化,使其依赖于纯Java代码,因此不需要任何工具即可运行测试。
这样,您将只测试代码,而不测试其他依赖项,这将使测试变得更加容易。
建议在测试时限制要测试的依赖项。您可以采用不依赖FirebaseMessagingService的方式来重构代码。
例如,您可以将其提取为独立于其他类的方法(最好与不依赖FirebaseMessagingService的其他类(以及HandleFirebaseMessages中的其他方法))分离,而不是将其逻辑放在onMessageReceived()中。它甚至可以进行结构化,使其依赖于纯Java代码,因此不需要任何工具即可运行测试。
这样,您将只测试代码,而不测试其他依赖项,这将使测试变得更加容易。