2424#import " FIRMessaging.h"
2525#import " FIRMessagingRemoteNotificationsProxy.h"
2626
27- #pragma mark - Expose Internal Methods for Testing
28- // Expose some internal properties and methods here, in order to test
29- @interface FIRMessagingRemoteNotificationsProxy ()
30-
31- @property (readonly , nonatomic ) BOOL didSwizzleMethods;
32- @property (readonly , nonatomic ) BOOL didSwizzleAppDelegateMethods;
33-
34- @property (readonly , nonatomic ) BOOL hasSwizzledUserNotificationDelegate;
35- @property (readonly , nonatomic ) BOOL isObservingUserNotificationDelegateChanges;
36-
37- @property (strong , readonly , nonatomic ) id userNotificationCenter;
38- @property (strong , readonly , nonatomic ) id currentUserNotificationCenterDelegate;
39-
40- + (instancetype )sharedProxy ;
41-
42- - (BOOL )swizzleAppDelegateMethods : (id <UIApplicationDelegate>)appDelegate ;
43- - (void )listenForDelegateChangesInUserNotificationCenter : (id )notificationCenter ;
44- - (void )swizzleUserNotificationCenterDelegate : (id )delegate ;
45- - (void )unswizzleUserNotificationCenterDelegate : (id )delegate ;
46-
47- void FCM_swizzle_appDidReceiveRemoteNotification (id self,
48- SEL _cmd,
49- UIApplication *app,
50- NSDictionary *userInfo);
51- void FCM_swizzle_appDidReceiveRemoteNotificationWithHandler (
52- id self, SEL _cmd, UIApplication *app, NSDictionary *userInfo,
53- void (^handler)(UIBackgroundFetchResult));
54- void FCM_swizzle_willPresentNotificationWithHandler (
55- id self, SEL _cmd, id center, id notification, void (^handler)(NSUInteger ));
56- void FCM_swizzle_didReceiveNotificationResponseWithHandler (
57- id self, SEL _cmd, id center, id response, void (^handler)());
27+ #pragma mark - Invalid App Delegate or UNNotificationCenter
5828
29+ @interface RandomObject : NSObject
30+ @property (nonatomic , weak ) id delegate;
31+ @end
32+ @implementation RandomObject
33+ - (void )application : (UIApplication *)application
34+ didReceiveRemoteNotification : (NSDictionary *)userInfo
35+ fetchCompletionHandler : (void (^)(UIBackgroundFetchResult))completionHandler {
36+ }
37+
38+ #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
39+ - (void )userNotificationCenter : (UNUserNotificationCenter *)center
40+ willPresentNotification : (UNNotification *)notification
41+ withCompletionHandler : (void (^)(UNNotificationPresentationOptions options))
42+ completionHandler {
43+ }
44+
45+ #if TARGET_OS_IOS
46+ - (void )userNotificationCenter:(UNUserNotificationCenter *)center
47+ didReceiveNotificationResponse:(UNNotificationResponse *)response
48+ withCompletionHandler:(void (^)(void ))completionHandler {
49+ }
50+ #endif // TARGET_OS_IOS
51+
52+ #endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
5953@end
6054
6155#pragma mark - Incomplete App Delegate
@@ -104,7 +98,9 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center
10498 self.willPresentWasCalled = YES ;
10599}
106100#if TARGET_OS_IOS
107- - (void )userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void ))completionHandler {
101+ - (void )userNotificationCenter:(UNUserNotificationCenter *)center
102+ didReceiveNotificationResponse:(UNNotificationResponse *)response
103+ withCompletionHandler:(void (^)(void ))completionHandler {
108104 self.didReceiveResponseWasCalled = YES ;
109105}
110106#endif // TARGET_OS_IOS
@@ -116,36 +112,42 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNoti
116112@interface FIRMessagingRemoteNotificationsProxyTest : XCTestCase
117113
118114@property (nonatomic , strong ) FIRMessagingRemoteNotificationsProxy *proxy;
119- @property (nonatomic , strong ) id mockProxy;
120115@property (nonatomic , strong ) id mockProxyClass;
121- @property (nonatomic , strong ) id mockMessagingClass;
116+ @property (nonatomic , strong ) id mockSharedApplication;
117+ @property (nonatomic , strong ) id mockMessaging;
118+ @property (nonatomic , strong ) id mockUserNotificationCenter;
122119
123120@end
124121
125122@implementation FIRMessagingRemoteNotificationsProxyTest
126123
127124- (void )setUp {
128125 [super setUp ];
126+ _mockSharedApplication = OCMPartialMock ([UIApplication sharedApplication ]);
127+
128+ _mockMessaging = OCMClassMock ([FIRMessaging class ]);
129+ OCMStub ([_mockMessaging messaging ]).andReturn (_mockMessaging);
130+
129131 _proxy = [[FIRMessagingRemoteNotificationsProxy alloc ] init ];
130- _mockProxy = OCMPartialMock (_proxy);
131132 _mockProxyClass = OCMClassMock ([FIRMessagingRemoteNotificationsProxy class ]);
132- // Update +sharedProxy to always return our partial mock of FIRMessagingRemoteNotificationsProxy
133- OCMStub ([_mockProxyClass sharedProxy ]).andReturn (_mockProxy);
134- // Many of our swizzled methods call [FIRMessaging messaging], but we don't need it,
135- // so just stub it to return nil
136- _mockMessagingClass = OCMClassMock ([FIRMessaging class ]);
137- OCMStub ([_mockMessagingClass messaging ]).andReturn (nil );
133+ // Update +sharedProxy to always return our test instance
134+ OCMStub ([_mockProxyClass sharedProxy ]).andReturn (self.proxy );
135+
136+ #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
137+ _mockUserNotificationCenter = OCMClassMock ([UNUserNotificationCenter class ]);
138+ OCMStub ([_mockUserNotificationCenter currentNotificationCenter ]).andReturn (_mockUserNotificationCenter);
139+ #endif
138140}
139141
140142- (void )tearDown {
141- [_mockMessagingClass stopMocking ];
142- _mockMessagingClass = nil ;
143-
144143 [_mockProxyClass stopMocking ];
145144 _mockProxyClass = nil ;
146145
147- [_mockProxy stopMocking ];
148- _mockProxy = nil ;
146+ [_mockMessaging stopMocking ];
147+ _mockMessaging = nil ;
148+
149+ [_mockSharedApplication stopMocking ];
150+ _mockSharedApplication = nil ;
149151
150152 _proxy = nil ;
151153 [super tearDown ];
@@ -154,30 +156,36 @@ - (void)tearDown {
154156#pragma mark - Method Swizzling Tests
155157
156158- (void )testSwizzlingNonAppDelegate {
157- id randomObject = @" Random Object that is not an App Delegate" ;
158- [self .proxy swizzleAppDelegateMethods: randomObject];
159- XCTAssertFalse (self.proxy .didSwizzleAppDelegateMethods );
160- }
159+ RandomObject *invalidAppDelegate = [[RandomObject alloc ] init ];
160+ [OCMStub ([self .mockSharedApplication delegate ]) andReturn: invalidAppDelegate];
161+ [self .proxy swizzleMethodsIfPossible ];
161162
162- - (void )testSwizzlingAppDelegate {
163- IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc ] init ];
164- [self .proxy swizzleAppDelegateMethods: incompleteAppDelegate];
165- XCTAssertTrue (self.proxy .didSwizzleAppDelegateMethods );
163+ OCMReject ([self .mockMessaging appDidReceiveMessage: [OCMArg any ]]);
164+
165+ [invalidAppDelegate application: self .mockSharedApplication
166+ didReceiveRemoteNotification: @{}
167+ fetchCompletionHandler: ^(UIBackgroundFetchResult result) {}];
166168}
167169
168170- (void )testSwizzledIncompleteAppDelegateRemoteNotificationMethod {
169- IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc ] init ];
170- [self .mockProxy swizzleAppDelegateMethods: incompleteAppDelegate];
171+ IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc ] init ];
172+ [OCMStub ([self .mockSharedApplication delegate ]) andReturn: incompleteAppDelegate];
173+ [self .proxy swizzleMethodsIfPossible ];
174+
175+ NSDictionary *notification = @{@" test" : @" " };
176+ OCMExpect ([self .mockMessaging appDidReceiveMessage: notification]);
171177
172- [incompleteAppDelegate application: OCMClassMock ([UIApplication class ])
173- didReceiveRemoteNotification: @{} ];
174- // Verify our swizzled method was called
175- OCMVerify (FCM_swizzle_appDidReceiveRemoteNotification) ;
178+ [incompleteAppDelegate application: self .mockSharedApplication
179+ didReceiveRemoteNotification: notification ];
180+
181+ [ self .mockMessaging verify ] ;
176182}
177183
178184- (void )testIncompleteAppDelegateRemoteNotificationWithFetchHandlerMethod {
179185 IncompleteAppDelegate *incompleteAppDelegate = [[IncompleteAppDelegate alloc ] init ];
180- [self .mockProxy swizzleAppDelegateMethods: incompleteAppDelegate];
186+ [OCMStub ([self .mockSharedApplication delegate ]) andReturn: incompleteAppDelegate];
187+ [self .proxy swizzleMethodsIfPossible ];
188+
181189 SEL remoteNotificationWithFetchHandler =
182190 @selector (application:didReceiveRemoteNotification:fetchCompletionHandler: );
183191 XCTAssertFalse ([incompleteAppDelegate respondsToSelector: remoteNotificationWithFetchHandler]);
@@ -188,43 +196,87 @@ - (void)testIncompleteAppDelegateRemoteNotificationWithFetchHandlerMethod {
188196- (void )testSwizzledAppDelegateRemoteNotificationMethods {
189197#if TARGET_OS_IOS
190198 FakeAppDelegate *appDelegate = [[FakeAppDelegate alloc ] init ];
191- [self .mockProxy swizzleAppDelegateMethods: appDelegate];
192- [appDelegate application: OCMClassMock ([UIApplication class ]) didReceiveRemoteNotification: @{}];
199+ [OCMStub ([self .mockSharedApplication delegate ]) andReturn: appDelegate];
200+ [self .proxy swizzleMethodsIfPossible ];
201+
202+ NSDictionary *notification = @{@" test" : @" " };
203+
204+ // Test application:didReceiveRemoteNotification:
205+
193206 // Verify our swizzled method was called
194- OCMVerify (FCM_swizzle_appDidReceiveRemoteNotification);
207+ OCMExpect ([self .mockMessaging appDidReceiveMessage: notification]);
208+
209+ // Call the method
210+ [appDelegate application: self .mockSharedApplication
211+ didReceiveRemoteNotification: notification];
212+
195213 // Verify our original method was called
196214 XCTAssertTrue (appDelegate.remoteNotificationMethodWasCalled );
215+ [self .mockMessaging verify ];
216+
217+ // Test application:didReceiveRemoteNotification:fetchCompletionHandler:
218+
219+ // Verify our swizzled method was called
220+ OCMExpect ([self .mockMessaging appDidReceiveMessage: notification]);
197221
198- // Now call the remote notification with handler method
199222 [appDelegate application: OCMClassMock ([UIApplication class ])
200- didReceiveRemoteNotification: @{}
223+ didReceiveRemoteNotification: notification
201224 fetchCompletionHandler: ^(UIBackgroundFetchResult result) {}];
202- // Verify our swizzled method was called
203- OCMVerify (FCM_swizzle_appDidReceiveRemoteNotificationWithHandler);
225+
204226 // Verify our original method was called
205227 XCTAssertTrue (appDelegate.remoteNotificationWithFetchHandlerWasCalled );
206- #endif
207228
229+ [self .mockMessaging verify ];
230+ #endif
208231}
209232
210233#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
211234
212235- (void )testListeningForDelegateChangesOnInvalidUserNotificationCenter {
213- id randomObject = @" Random Object that is not a User Notification Center" ;
214- [self .proxy listenForDelegateChangesInUserNotificationCenter: randomObject];
215- XCTAssertFalse (self.proxy .isObservingUserNotificationDelegateChanges );
236+ RandomObject *invalidNotificationCenter = [[RandomObject alloc ] init ];
237+ OCMStub ([self .mockUserNotificationCenter currentNotificationCenter ]).andReturn (invalidNotificationCenter);
238+ [self .proxy swizzleMethodsIfPossible ];
239+
240+ OCMReject ([self .mockMessaging appDidReceiveMessage: [OCMArg any ]]);
241+
242+ [(id <UNUserNotificationCenterDelegate >)invalidNotificationCenter.delegate
243+ userNotificationCenter: self .mockUserNotificationCenter
244+ willPresentNotification: OCMClassMock ([UNNotification class ])
245+ withCompletionHandler: ^(UNNotificationPresentationOptions options) {
246+ }];
216247}
217248
218249- (void )testSwizzlingInvalidUserNotificationCenterDelegate {
219- id randomObject = @" Random Object that is not a User Notification Center Delegate" ;
220- [self .proxy swizzleUserNotificationCenterDelegate: randomObject];
221- XCTAssertFalse (self.proxy .hasSwizzledUserNotificationDelegate );
250+ RandomObject *invalidDelegate = [[RandomObject alloc ] init ];
251+ OCMStub ([self .mockUserNotificationCenter delegate ]).andReturn (invalidDelegate);
252+ [self .proxy swizzleMethodsIfPossible ];
253+
254+ OCMReject ([self .mockMessaging appDidReceiveMessage: [OCMArg any ]]);
255+
256+ [invalidDelegate
257+ userNotificationCenter: self .mockUserNotificationCenter
258+ willPresentNotification: OCMClassMock ([UNNotification class ])
259+ withCompletionHandler: ^(UNNotificationPresentationOptions options) {
260+ }];
222261}
223262
224263- (void )testSwizzlingUserNotificationsCenterDelegate {
225264 FakeUserNotificationCenterDelegate *delegate = [[FakeUserNotificationCenterDelegate alloc ] init ];
226- [self .proxy swizzleUserNotificationCenterDelegate: delegate];
227- XCTAssertTrue (self.proxy .hasSwizzledUserNotificationDelegate );
265+ OCMStub ([self .mockUserNotificationCenter delegate ]).andReturn (delegate);
266+ [self .proxy swizzleMethodsIfPossible ];
267+
268+ NSDictionary *message = @{@" message" : @" " };
269+ id notification = [self userNotificationWithMessage: message];
270+
271+ OCMExpect ([self .mockMessaging appDidReceiveMessage: message]);
272+
273+ [delegate
274+ userNotificationCenter: self .mockUserNotificationCenter
275+ willPresentNotification: notification
276+ withCompletionHandler: ^(UNNotificationPresentationOptions options) {
277+ }];
278+
279+ [self .mockMessaging verify ];
228280}
229281
230282// Use a fake delegate that doesn't actually implement the needed delegate method.
@@ -237,7 +289,8 @@ - (void)testIncompleteUserNotificationCenterDelegateMethod {
237289 }
238290 IncompleteUserNotificationCenterDelegate *delegate =
239291 [[IncompleteUserNotificationCenterDelegate alloc ] init ];
240- [self .mockProxy swizzleUserNotificationCenterDelegate: delegate];
292+ OCMStub ([self .mockUserNotificationCenter delegate ]).andReturn (delegate);
293+ [self .proxy swizzleMethodsIfPossible ];
241294 // Because the incomplete delete does not implement either of the optional delegate methods, we
242295 // should swizzle nothing. If we had swizzled them, then respondsToSelector: would return YES
243296 // even though the delegate does not implement the methods.
@@ -250,55 +303,67 @@ - (void)testIncompleteUserNotificationCenterDelegateMethod {
250303
251304// Use an object that does actually implement the optional methods. Both should be called.
252305- (void )testSwizzledUserNotificationsCenterDelegate {
253- // Early exit if running on pre iOS 10
254- if (![UNNotification class ]) {
255- return ;
256- }
257306 FakeUserNotificationCenterDelegate *delegate = [[FakeUserNotificationCenterDelegate alloc ] init ];
258- [self .mockProxy swizzleUserNotificationCenterDelegate: delegate];
307+ OCMStub ([self .mockUserNotificationCenter delegate ]).andReturn (delegate);
308+ [self .proxy swizzleMethodsIfPossible ];
309+
310+ NSDictionary *message = @{@" message" : @" " };
311+
312+ // Verify userNotificationCenter:willPresentNotification:withCompletionHandler:
313+ OCMExpect ([self .mockMessaging appDidReceiveMessage: message]);
314+
259315 // Invoking delegate method should also invoke our swizzled method
260316 // The swizzled method uses the +sharedProxy, which should be
261- // returning our mocked proxy.
317+ // returning our proxy.
262318 // Use non-nil, proper classes, otherwise our SDK bails out.
263- [delegate userNotificationCenter: OCMClassMock ([ UNUserNotificationCenter class ])
264- willPresentNotification: [self generateMockNotification ]
319+ [delegate userNotificationCenter: self .mockUserNotificationCenter
320+ willPresentNotification: [self userNotificationWithMessage: message ]
265321 withCompletionHandler: ^(NSUInteger options) {}];
266- // Verify our swizzled method was called
267- OCMVerify (FCM_swizzle_willPresentNotificationWithHandler);
322+
268323 // Verify our original method was called
269324 XCTAssertTrue (delegate.willPresentWasCalled );
325+
326+ // Verify our swizzled method was called
327+ [self .mockMessaging verify ];
328+
270329#if TARGET_OS_IOS
271- [delegate userNotificationCenter: OCMClassMock ([UNUserNotificationCenter class ])
272- didReceiveNotificationResponse: [self generateMockNotificationResponse ]
330+ // Verify userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:
331+
332+ OCMExpect ([self .mockMessaging appDidReceiveMessage: message]);
333+
334+ [delegate userNotificationCenter: self .mockUserNotificationCenter
335+ didReceiveNotificationResponse: [self userNotificationResponseWithMessage: message]
273336 withCompletionHandler: ^{}];
274- // Verify our swizzled method was called
275- OCMVerify (FCM_swizzle_appDidReceiveRemoteNotificationWithHandler);
337+
276338 // Verify our original method was called
277339 XCTAssertTrue (delegate.didReceiveResponseWasCalled );
278- #endif
279- }
280340
281- - (id )generateMockNotification {
282- // Stub out: notification.request.content.userInfo
283- id mockNotification = OCMClassMock ([UNNotification class ]);
284- id mockRequest = OCMClassMock ([UNNotificationRequest class ]);
285- id mockContent = OCMClassMock ([UNNotificationContent class ]);
286- OCMStub ([mockContent userInfo ]).andReturn (@{});
287- OCMStub ([mockRequest content ]).andReturn (mockContent);
288- OCMStub ([mockNotification request ]).andReturn (mockRequest);
289- return mockNotification;
341+ // Verify our swizzled method was called
342+ [self .mockMessaging verify ];
343+ #endif // TARGET_OS_IOS
290344}
291345
292- - (id )generateMockNotificationResponse {
346+ - (id )userNotificationResponseWithMessage : ( NSDictionary *) message {
293347 // Stub out: response.[mock notification above]
294348#if TARGET_OS_IOS
295349 id mockNotificationResponse = OCMClassMock ([UNNotificationResponse class ]);
296- id mockNotification = [self generateMockNotification ];
350+ id mockNotification = [self userNotificationWithMessage: message ];
297351 OCMStub ([mockNotificationResponse notification ]).andReturn (mockNotification);
298352 return mockNotificationResponse;
299- #else
353+ #else // TARGET_OS_IOS
300354 return nil ;
301- #endif
355+ #endif // TARGET_OS_IOS
356+ }
357+
358+ - (UNNotification *)userNotificationWithMessage : (NSDictionary *)message {
359+ UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc ] init ];
360+ content.userInfo = message;
361+ id notificationRequest = OCMClassMock ([UNNotificationRequest class ]);
362+ OCMStub ([notificationRequest content ]).andReturn (content);
363+ id notification = OCMClassMock ([UNNotification class ]);
364+ OCMStub ([notification request ]).andReturn (notificationRequest);
365+
366+ return notification;
302367}
303368
304369#endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
0 commit comments