Skip to content

Commit 04561fe

Browse files
FIRMessagingRemoteNotificationsProxy - test only public API with no assumptions on implementation (#2672)
1 parent 2e8b918 commit 04561fe

File tree

4 files changed

+179
-113
lines changed

4 files changed

+179
-113
lines changed

Example/Messaging/Tests/FIRMessagingRemoteNotificationsProxyTest.m

Lines changed: 172 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -24,38 +24,32 @@
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

Comments
 (0)