简体   繁体   中英

How to write test cases for singleton XCTest Framework xcode 7

I just started to learn to write unit testing using XCTest with xcode 7.2 and I think this Xcode does not support OCMock framework. I know nothing about this frame work .I now only XCTest Framework. Here my question is How to write cases for singleton objects. Here I used some code with Dependency injection making the property and all.

@property (nonatomic, strong) UIApplication *application;

- (UIApplication*)application
{
   if (!_application)
   {
       _application = [UIApplication sharedApplication];
   }
   return _application;
}

- (IBAction)pushNotificationsSwitchWasToggled:(id)sender
{
   UISwitch *toggleSwitch = sender;
   if (toggleSwitch.on)
   {
       [self.application registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert)];
   }
   else
   {
       [self.application unregisterForRemoteNotifications];
   }
}

Test

If want to test the above method

- (void)testToggleOnRegistersForPushNotifications_On
{
   //given

   SettingsViewController *sut = [[SettingsViewController alloc] init];
   sut.application =[UIApplication sharedApplication];
//Here only one object is created ? not new right.
   [sut.pushNotificationsSwitch setOn:YES];

   //when
   [sut pushNotificationsSwitchWasToggled:sut.pushNotificationsSwitch];
//here I am expecting code whether registerForRemoteNotificationTypes method is called or not.

 }

1)Here when we use singleton in any place object is created only one time . In test case I don't want to use real [UIApplication sharedApplication] . I want one dummy or fake object to test.

2)How do we very whether remote notification is registered or not (in general how to very whether method is called or not ?). not with [UIApplication sharedApplication] you can explain me with any singleton example with NSUSerDefault (giving input and comparing result with expected.)

In your test, you want to inject something else in place of the real UIApplication. That "something else" can be anything, as long as it answers the methods you need. We don't even have to use a mock object framework. For example:

@interface MockApplication : NSObject
@property (nonatomic, assign, readonly) NSUInteger registerForRemoteNotificationTypesCount;
@property (nonatomic, strong, readonly) NSMutableArray *remoteNotificationTypes;
@property (nonatomic, assign, readonly) NSUInteger unregisterForRemoteNotificationsCount;
@end

@implementation MockApplication

- (instancetype)init
{
    self = [super init];
    if (self)
    {
        _remoteNotificationTypes = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)registerForRemoteNotificationTypes:(UIRemoteNotificationType)types
{
    self.registerForRemoteNotificationTypesCount += 1;
    [self.remoteNotificationTypes addObject:@(types)];
}

- (void)unregisterForRemoteNotifications
{
    self.unregisterForRemoteNotificationsCount += 1;
}

@end

Then in your test code:

MockApplication *mockApplication = [[MockApplication alloc] init];
sut.application = (id)mockApplication;

At the end of your test, you can now query the mockApplication to see what was called, and with what arguments.

Once you understand this principle, then you can save time by using mock object frameworks such as OCMock or OCMockito .

I have a screencast that shows how mock to NSUserDefaults using OCMockito. Actually, it's more than that, because it covers Test Driven Development: See Objective-C TDD: How to Get Started

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM