A Matcher Framework for Objective-C/Cocoa
Unit testing is just something I never seem to be able to get my head around but I can see why its important and can be a huge time saver (if you know what you're doing). I am hoping that someone can point me in the right direction.
I have the following UIViewController
QBElectricityBaseVC.h
@interface QBElectricityBaseVC : QBStateVC
@property (nonatomic, strong) QBElectricityUsage *electricityUsage;
@property (nonatomic, assign) CGFloat tabBarHeight;
- (void)updateElectricityUsage;
@end
QBElectricityBaseVC.m
@implementation QBElectricityBaseVC
- (instancetype)init
{
self = [super init];
if (self) {
self.tabBarItem = [[UITabBarItem alloc] initWithTitle:NSLocalizedString(@"electricity_title", nil) image:nil tag:0];
}
return self;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.notificationCenter addObserver:self selector:@selector(updateElectricityUsage)
name:kUpdatedElectricityUsageKey object:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.notificationCenter removeObserver:self];
}
- (void)updateElectricityUsage
{
self.electricityUsage = [self.stateManager electricityUsage];
}
- (CGFloat)tabBarHeight
{
return self.tabBarController.tabBar.frame.size.height;
}
@end
What should I test?
- An observer for
kUpdatedElectricityUsageKey
is added
self.electricityUsage
becomes an instance of QBElectricityUsage
- A
tabBarHeight
is returned
- An observer for
kUpdatedElectricityUsageKey
is removed
Am I missing anything I should test or testing something I really shouldn't?
How do I test?
So I am trying to do this using Specta and Expexta. If I need to mock anything I would be using OCMockito.
I really don't know how to test the observer is added/removed. I see the following in the Expexta documentation but not sure if its relevant/how to use it:
expect(^{ /* code */ }).to.notify(@"NotificationName"); passes if a given block of code generates an NSNotification named NotificationName.
expect(^{ /* code */ }).to.notify(notification); passes if a given block of code generates an NSNotification equal to the passed notification.
To test that self.electricityUsage
becomes an instance of QBElectricityUsage
I could create a category that has a method that just pretends the notification fired and calls the updateElectricityUsage
method but is this the best way?
And as for the tabBarHeight
, should I just test that it returns a valid CGFloat
and not worry what the value is?
UPDATE
I changed my viewWillAppear
method to look like below:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self addNotificationObservers];
}
- (void)addNotificationObservers
{
[self.notificationCenter addObserver:self selector:@selector(updateElectricityUsage)
name:kUpdatedElectricityUsageKey object:nil];
}
And then I created the following test:
#import "Specs.h"
#import "QBElectricityBaseVC.h"
#import "ElectricityConstants.h"
SpecBegin(QBElectricityBaseVCSpec)
describe(@"QBElectricityBaseVC", ^{
__block QBElectricityBaseVC *electricityBaseVC;
__block NSNotificationCenter *mockNotificationCenter;
beforeEach(^{
electricityBaseVC = [QBElectricityBaseVC new];
mockNotificationCenter = mock([NSNotificationCenter class]);
electricityBaseVC.notificationCenter = mockNotificationCenter;
});
afterEach(^{
electricityBaseVC = nil;
mockNotificationCenter = nil;
});
it(@"should have a notification observer for updated electricity usage", ^{
[electricityBaseVC addNotificationObservers];
[verify(mockNotificationCenter) addObserver:electricityBaseVC selector:@selector(updateElectricityUsage)
name:kUpdatedElectricityUsageKey object:nil];
});
});
SpecEnd
That test now passes but is this the correct/best way to test this?
Source: (StackOverflow)
Is there a fluent matching API that works for Swift code? The leading Objective-C matcher candidates seem to be OCHamcrest and Expecta, both of which rely on complex macros that (as per the docs) aren't available to Swift code, e.g.
#define HC_assertThat(actual, matcher) \
HC_assertThatWithLocation(self, actual, matcher, __FILE__, __LINE__)
(OCHamcrest)
#define EXP_expect(actual) _EXP_expect(self, __LINE__, __FILE__, ^id{ return EXPObjectify((actual)); })
(Expecta)
Is there another alternative that does work with Swift, or some way to wrap one or the other of these so they can be used with Swift?
ETA: For future readers -- I looked at SwiftHamcrest (per Jon Reid's answer) but for the time being I've settled on Quick/Nimble.
Source: (StackOverflow)
I'm currently using Kiwi to write tests, but I'm interested in trying out Specta/Expecta/OCMockito (or another mocking library, if necessary).
One thing I use Kiwi for is testing that delegate methods are called after some asynchronous work. For example, this Kiwi spec stubs an HTTP request response (which is performed in a background queue), and tests that eventually the delegate is informed of a response from the request:
it(@"Sends a callback", ^{
[OHHTTPStubs stubRequestContainingString:@"start" withJSON:@{}];
id starterMock = [KWMock mockForProtocol:@protocol(HZMediationStarting)];
HZMediationStarter *starter = [[HZMediationStarter alloc] initWithStartingDelegate:starterMock];
[starter start];
[[starterMock shouldEventually] receive:@selector(startWithDictionary:fromCache:)];
});
Is there a way to achieve this with Expecta/OCMockito (or potentially another mocking library?). I can see that Expecta supports asynchronous expectations, but I'm not sure how to combine that with a mocking library like OCMockito.
Source: (StackOverflow)