How to init an object with stubbed values with OCMock - objective-c

Ho do I stub a method used in the init method?
The related methods in my class:
- (id)init
{
self = [super init];
if (self) {
if (self.adConfigurationType == AdConfigurationTypeDouble) {
[self configureForDoubleConfiguration];
}
else {
[self configureForSingleConfiguration];
}
}
return self;
}
- (AdConfigurationType)adConfigurationType
{
if (adConfigurationType == NSNotFound) {
if ((random()%2)==1) {
adConfigurationType = AdConfigurationTypeSingle;
}
else {
adConfigurationType = AdConfigurationTypeDouble;
}
}
return adConfigurationType;
}
My test:
- (void)testDoubleConfigurationLayout
{
id mockController = [OCMockObject mockForClass:[AdViewController class]];
AdConfigurationType type = AdConfigurationTypeDouble;
[[[mockController stub] andReturnValue:OCMOCK_VALUE(type)] adConfigurationType];
id controller = [mockController init];
STAssertNotNil([controller smallAdRight], #"Expected a value here");
STAssertNotNil([controller smallAdRight], #"Expected a value here");
STAssertNil([controller largeAd], #"Expected nil here");
}
My result:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'OCMockObject[AdViewController]: unexpected method invoked: smallAdRight '
So how will I access the AdViewController in the OCMockObject?

If you use the mockForClass: method you will need to provided stubbed implementations for each and every method that are called in the mocked class. including your call into it with [controller smallAdRight] in your first test.
Instead you can use the niceMockForClass: method which will just ignore any messages which are not mocked.
Another alternative is to instantiate your AdViewController and then create a partial mock for it using the partialMockForObject: method. This way the internals of the controller class will do the main part of the work.
Just a though... are you trying to test the AdViewController or a class which uses it? It appears that you are trying to mock the entire class and then test if it still behaves normally. If you want to test that AdViewController behaves as expected when certain values are injected then your best option is most likely the partialMockForObject: method:
- (void)testDoubleConfigurationLayout {
AdViewController *controller = [AdViewController alloc];
id mock = [OCMockObject partialMockForObject:controller];
AdConfigurationType type = AdConfigurationTypeDouble;
[[[mock stub] andReturnValue:OCMOCK_VALUE(type)] adConfigurationType];
// You'll want to call init after the object have been stubbed
[controller init]
STAssertNotNil([controller smallAdRight], #"Expected a value here");
STAssertNotNil([controller smallAdRight], #"Expected a value here");
STAssertNil([controller largeAd], #"Expected nil here");
}

Related

How can I pass along runtime arguments to sub components using Typhoon?

Let's say I have two classes Foo and Bar. I can construct a Bar independently of Foo, but Foo needs a Bar. In Typhoon I define these two components and use them in my applicaiton however at runtime when constructing a Foo the barId that I pass in appears to be passed down to the other Bar component as a <TyphoonInjectionByRuntimeArgument: 0x15ea4670, type=Undifined> and not the NSString that I passed in.
I'm probably doing it wrong.
What should I do differently?
#implementation Assembly
-(Foo *)fooWithFooId:(NSString *)fooId andBarId:(NSString *)barId {
return [TyphoonDefinition withClass:[Foo class] configuration:^(TyphoonDefinition* definition) {
[definition useInitializer:#selector(initWithFooId:andBar:) parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:fooId];
[initializer injectParameterWith:[self barWithId:barId]];
}];
}];
}
-(Bar *)barWithBarId:(NSString *)barId {
return [TyphoonDefinition withClass:[Bar class] configuration:^(TyphoonDefinition* definition) {
[definition useInitializer:#selector(initWithBarId:) parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:barId];
}];
}];
}
#end
////////
#implementation Foo
-(instancetype) initWithFooId:(NSString *)fooId andBar:(Bar *)bar
{
self = [super init];
self.fooId = fooId;
self.bar = bar;
return self;
}
#end
#implementation Bar
-(instancetype) initWithBarId:(NSString *)barId
{
self = [super init];
self.barId = barId;
return self;
}
#end
Update:
After further review the issue appeared to be caused by using the runtime argument inside the definition to build another parameter for another definition. See the usage of NSString below:
#implementation Assembly
-(Foo *)fooWithFooId:(NSString *)fooId andBazId:(NSString *)bazId {
return [TyphoonDefinition withClass:[Foo class] configuration:^(TyphoonDefinition* definition) {
[definition useInitializer:#selector(initWithFooId:andBaz:) parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:fooId];
[initializer injectParameterWith:[self bazWithPathToBaz:[NSString stringWithFormat:#"/some/path/to/baz/%#", bazId]]];
}];
}];
}
-(Baz *)bazWithPathToBaz:(NSString *)bazPath {
return [TyphoonDefinition withClass:[Baz class] configuration:^(TyphoonDefinition* definition) {
[definition useInitializer:#selector(initWithBazPath:) parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:bazPath];
}];
}];
}
#end
When called the path passed into the initializer would come be "/some/path/to/baz/<TyphoonInjectionByRuntimeArgument: 0x15ea4670, type=Undifined>"
How are you obtaining your built instance of Foo? Your assembly code above looks OK, but keep in mind the following:
Build-time: An assembly returns instances of TyphoonDefinition. Typhoon instruments this assembly to collect the information that it needs to return built instances at runtime. This includes injecting runtime arguments with proxies (instances of TyphoonInjectionByRuntimeArgument).
Run-time: The assembly now returns built components according to the rules defined in the definitions. (Its actually a proxy pointing at a TyphoonComponentFactory).
After creating one ore more assemblies, the following is required to have the assembly return a built component:
- (void)testFoobar
{
//Pass in a not-built assembly to TyphoonComponentFactory
TyphoonComponentFactory *factory =
[TyphoonBlockComponentFactory factoryWithAssemblies:#[[Assembly assembly]]];
//Now we have a built assembly
Assembly *builtAssembly = (Assembly*) factory;
Foo *builtFoo = [builtAssembly fooWithFooId:#"1234" andBarId:#"5678"];
NSLog(#"%#", builtFoo);
}
Result: <Foo: self.fooId=1234, self.bar=<Bar: self.barId=5678>>
(or in the case of an app you could use plist-integration to bootstrap Typhoon and inject the app delegate. Note that you can inject the assembly itself into any component, either as a TyphoonComponentFactory or posing as one of your assembly interfaces. This is useful to transition from one object-graph (eg view controller) to another.)
Edit:
Although it wasn't the original intention, in most cases you can also use run-time arguments from within an assembly, and we now do this in our own apps. For example:
- (OfferManager *)offerManager
{
return [TyphoonDefinition withClass:[OfferManager class]
configuration:^(TyphoonDefinition *definition) {
[definition useInitializer:#selector(initWithEngine:)
parameters:^(TyphoonMethod *initializer) {
[initializer injectParameterWith:
[self engineWithPollingInterval:#120 cacheExpire:#3600]];
}];
}];
}
. . but currently its not possible to create a top-level definition that is the result of another definition with runtime arguments. Example:
- (Engine*) preConfiguredEngine {
return [self engineWithPollingInterval:#120 cacheExpire:#3600];
}
. . the reason is fairly simple, and if you need this we can look at it.

Mock internal calls of a another class methods

I am trying to learn unit testing with ocmock. I am finding it difficult to mock calls of another class from a class which i am unit testing.
Can some one suggest how to make mock call to KeyChainUtils class and HttpRequest class:
Code to unit test with OCMock:
#implementation UserProfileService {
+(BOOL) isValidUser
{
NSString* userId = [KeyChainUtil loadValueForKey:USER_ID]; //mock this call
bool isValidUser = NO;
if(userId && userId.length > 0){
NSDictionary* response = [HTTPDataService getJSONForURL:#"http://xtest.com/checkuserid" forRequestData:#{#"userid": userId}];
if(response && response[#"valid"]){
isValidUser = [response[#"valid"] boolValue];
}else{
NSLog(#"error in connecting to server. response => %#", response);
}
}
return isValidUser;
}
}
Starting with OCMock release 2.1, we can stub the class method. Please refer this link for more information: http://www.ocmock.org/features/
So, we can stub the class method like this:
id keyChainUtilMock = [OCMockObject mockForClass:[KeyChainUtil class]];
[[[keyChainUtilMock stub] andReturn:#"aasdf"] loadValueForKey:USER_ID];
NSString* userId = [KeyChainUtil loadValueForKey:USER_ID];
NSLog(#" stubbed value-->%#", userId);
So, after running this particular piece of code. The actual class method is not called here and the stubbed value is returned. I hope this helps you.

Calling Obj-C Code from JavaScript via Console: Arguments get dropped?

Having a heck of a time with this one.
I've got a super-simple Cocoa app containing one WebView, a WebScripting API defined in the page, and a single NSObject defined on that API. When I turn on the debugger tools (in the embedded WebView), I can see the API on the JavaScript window object, and I can see my "api" property defined on that -- but when I call the API's "get" method, the arguments aren't being serialized -- when the Obj-C method gets called, the arguments are missing. See below, which hopefully illustrates:
I've combed through the docs, I've (apparently) set the appropriate methods to expose everything that needs to be exposed, and I can see the method being called. There has to be something stupid I'm missing, but as a relative newbie to this environment, I'm not seeing it.
Thanks in advance for your help!
Have you set WebKitDeveloperExtras to YES in your default user defaults when you send -[NSUserDefaults registerDefaults:]?
Depending on what version of Xcode you're using you could be getting a known error. If you're using LLDB on anything but the most recent version, it might not be giving you the right variables in the debugger. The solution has been to use GDB instead of LLDB until Apple fixes the problem. But I think they fixed the problem in the latest version. I'd change the debugger to use GDB and see if you're getting the right variables in Xcode. (Product-> Edit Scheme...-> Run -> Debugger). I came across this problem in iOS, though, so I don't know its applicability to OSX. Worth a try anyway.
I originally came across the problem here: https://stackoverflow.com/a/9485349/1147934
I process javascript in the main thread of my app from a local file stored in the apps directory. I check for beginning and ending tokens for the js functions I am executing and whether the function contains a variable.
Hopefully this can give you some good ideas for your issue. You could also do alerts in the js to see if the values post correctly as you run the app (I am sure you thought of that already, but it's worth mentioning.) Happy coding! I hope this helps!
in the .h file define:
NSMutableString *processedCommand;
NSArray *commandArguments;
In the .m file:
// tokens
#define kOpenToken #"<%%"
#define kCloseToken #"%%>"
// this will throw
-(void)executeJScriptCommand:(NSString *)aCommand {
[self performSelectorOnMainThread:#selector(executeThisCommand:) withObject:aCommand waitUntilDone:YES];
}
// this will throw
-(NSString *)executeCommand:(NSString *)command {
NSString *aCommand = [[[command stringByReplacingOccurrencesOfString:kOpenToken withString:#""]
stringByReplacingOccurrencesOfString:kCloseToken withString:#""]
stringByTrimmingLeadingAndTrailingWhitespaces];
if ([aCommand hasPrefix:#"="])
{
// variable. get value
[self getVariableFromCommand:aCommand];
}
else {
[self executeThisCommand:aCommand];
}
NSString *returnValue = [NSString stringWithString:processedCommand];
self.processedCommand = nil;
self.commandArguments = nil;
return returnValue;
}
-(void)executeThisCommand:(NSString *)aCommand {
BOOL hasError = NO;
// clear result
self.processedCommand = nil;
self.commandArguments = nil;
BOOL isFromJS = NO;
NSString *function = nil;
NSMutableArray *commandParts = nil;
#try {
// first, break the command into its parts and extract the function that needs to be called, and the (optional) arguments
commandParts = [[NSMutableArray alloc] initWithArray:[aCommand componentsSeparatedByString:#":"]];
if ([[[commandParts objectAtIndex:0] lowercaseString] isEqualToString:#"js-call"]) {
isFromJS = YES;
[commandParts removeObjectAtIndex:0];
}
// get our function, arguments
function = [[commandParts objectAtIndex:0] retain];
[commandParts removeObjectAtIndex:0];
if ([commandParts count] > 0){
if (isFromJS == YES) {
NSString *arguments = [[commandParts objectAtIndex:0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if ([arguments length] > 0) {
self.commandArguments = [arguments JSONValue];
}
}
else {
self.commandArguments = [NSArray arrayWithArray:commandParts];
}
}
// build invoke
SEL sel = NSSelectorFromString(function);
if ([self respondsToSelector:sel]) {
[self performSelectorOnMainThread:sel withObject:nil waitUntilDone:YES];
// using invocation causes a SIGABORT because the try/catch block was not catching the exception.
// using perform selector fixed the problem (i.e., the try/catch block now correctly catches the exception, as expected)
}
else {
[appDelegate buildNewExceptionWithName:#"" andMessage:[NSString stringWithFormat:#"Object does not respond to selector %#", function]];
}
}
#catch (NSException * e) {
hasError = YES;
[self updateErrorMessage:[NSString stringWithFormat:#"Error processing command %#: %#", aCommand, [e reason]]];
}
#finally {
[function release];
[commandParts release];
}
if (hasError == YES) {
[appDelegate buildNewExceptionWithName:#"executeThisCommand" andMessage:self.errorMessage];
}
}
// this can return nil
-(NSString *)getQueryStringValue:(NSString *)name {
NSString *returnValue = nil;
if (queryString != nil) {
returnValue = [queryString objectForKey:[name lowercaseString]];
}
return returnValue;
}

OCMock of class with method called from the test method

I am trying to test a method that instantiates an instance of MFMailComposeViewController. The method being tested calls several methods of MFMailComposeViewController including setSubject:.
I want to test that setSubject is sent a specific NSString, in this case #"Test Message".
No matter what I specify for the expected string in the mock stub there is no failure.
In the Unit Test class:
#import <OCMock/OCMock.h>
- (void)testEmail {
TestClass *testInstance = [[TestClass alloc] init];
id mock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mock stub] setSubject:#"Test Message"];
[testInstance testMethod];
}
In TestClass:
- (void)testMethod {
MFMailComposeViewController *mailComposeVC = [[MFMailComposeViewController alloc] init];
[mailComposeVC setSubject:#"Bad Message"];
}
Test Suite 'Email_Tests' started at 2011-09-17 18:12:21 +0000
Test Case '-[Email_Tests testEmail]' started.
Test Case '-[Email_Tests testEmail]' passed (0.041 seconds).
The test should have failed.
I am testing this in the iOS simulator and get the same result on a device.
What am I doing wrong? Is there some way to accomplish this?
You create a mock, but never pass it to the class under test, or ask the mock to verify itself. You need some form of dependency injection to say, "Instead of using MFMailComposeViewController, use this other thing I'm giving you."
Here's one way to do that. In the class under test, instead of allocating MFMailComposeViewController directly, get it through a factory method, like this:
#interface TestClass : NSObject
- (void)testMethod;
// Factory methods
+ (id)mailComposeViewController;
#end
Here's the implementation. You were leaking, so note that the factory method returns an autoreleased object.
- (void)testMethod {
MFMailComposeViewController *mailComposeVC =
[[self class] mailComposeViewController];
[mailComposeVC setSubject:#"Bad Message"];
}
+ (id)mailComposeViewController {
return [[[MFMailComposeViewController alloc] init] autorelease];
}
Over on the testing side, we create a testing subclass that overrides the factory method so it provides whatever we want it to:
#interface TestingTestClass : TestClass
#property(nonatomic, assign) id mockMailComposeViewController;
#end
#implementation TestingTestClass
#synthesize mockMailComposeViewController;
+ (id)mailComposeViewController {
return mockMailComposeViewController;
}
#end
Now we're ready for the test. I do a few things differently:
Allocate a testing subclass rather than the actual class (and don't leak!)
Set up the mock with an expectation, not just a stub
Inject the mock into the testing subclass
Verify the mock at the end
Here's the test:
- (void) testEmail {
TestClass *testInstance = [[[TestClass alloc] init] autorelease];
id mock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mock expect] setSubject:#"Test Message"];
[testInstance setMockMailComposeViewController:mock];
[testInstance testMethod];
[mock verify];
}
For completeness, we need one final test, and that's to guarantee that the factory method in the actual class returns what we expect:
- (void)testMailComposerViewControllerShouldBeCorrectType {
STAssertTrue([[TestClass mailComposeViewController]
isKindOfClass:[MFMailComposeViewController class]], nil);
}
Jon Reid's is a reasonable approach, though it seems that making mailComposeViewController a class method complicates it. And subclassing it in your test code means you'll always get the mocked version at test time, which may not be what you want. I'd make it an instance method. Then you can use a partial mock to override it at test time:
-(void) testEmail {
TestClass *testInstance = [[[TestClass alloc] init] autorelease];
id mock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mock expect] setSubject:#"Test Message"];
id mockInstance = [OCMockObject partialMockForObject:testInstance];
[[[mockInstance stub] andReturn:mock] mailComposeViewController];
[testInstance testMethod];
[mock verify];
}
If you keep it as a class method, you might consider making it a static global and exposing a way to override it:
static MFMailComposeViewController *mailComposeViewController = nil;
-(id)mailComposeViewController {
if (!mailComposeViewController) {
mailComposeViewController = [[MFMailComposeViewController alloc] init];
}
return mailComposeViewController;
}
-(void)setMailComposeViewController:(MFMailComposeViewController *)controller {
mailComposeViewController = controller;
}
Then, your test would be similar to Jon's example:
-(void)testEmail {
TestClass *testInstance = [[[TestClass alloc] init] autorelease];
id mock = [OCMockObject mockForClass:[MFMailComposeViewController class]];
[[mock expect] setSubject:#"Test Message"];
[testInstance setMailComposeViewController:mock];
[testInstance testMethod];
[mock verify];
// clean up
[testInstance setMailComposeViewController:nil];
}

iOS Singleton Variables Not Keeping Their Values

So I'm still kind of new to Objective-C, and this was my first app that I'm now updating. The idea is this: The whole app is basically various lists of stuff. It asks the API for 15 posts, shows those with a Load More button. Click Load More, it loads 15 more, etc. The API that it loads these from has a token system with a timeout built in. Too long between requests, and you have to get a new token. So I want to have a singleton to use anywhere in my app so I can just do [APIMachine getToken] and behind the scenes, it checks if the time since the last request was too long (or this is the first request), if so, gets a new token, otherwise returns the one we already have. I'm following the singleton pattern I've found in so many places, but every time the Load More button uses [APIMachine getToken]it gets either nothing or something completely random. I had it print this stuff in the logs, and one time I even got a UITableViewCell as my token. Looks like variables are being overwritten somehow. But I really can't figure it out.
So here it is:
static PoorAPI2 *_instance;
#implementation PoorAPI2
#synthesize apiToken, timeOpened, tokenTTL;
+ (PoorAPI2*)sharedAPI
{
#synchronized(self) {
if (_instance == nil) {
_instance = [[super allocWithZone:NULL] init];
}
}
return _instance;
}
-(NSString *)API_open{
//boring code to get api token redacted
if ([doneness isEqualToString:#"success"]) {
NSDictionary *data = [json objectForKey:#"data"];
apiToken = [data objectForKey:#"api_token"];
tokenTTL = [data objectForKey:#"ttl"];
timeOpened = [NSDate date];
}else{
NSLog(#"FFFFFFFUUUUUUUUUUUU this error should be handled better.");
}
return apiToken;
}
-(BOOL)isConnectionOpen{
return ([timeOpened timeIntervalSinceNow] > tokenTTL);
}
-(NSString *)getToken{
if([self isConnectionOpen]){
return apiToken;
}else{
return [_instance API_open];
}
}
-(id)init{
if(self = [super init]){
apiToken = [[NSString alloc] initWithString:#""];
timeOpened = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
tokenTTL = 0;
}
return self;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedAPI]retain];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
#end
I can only hope I'm doing something seriously foolish and this will be a hilarious point-and-laugh-at-that-guy thread. Then at least my app will work.
In API_open, you store three objects in instance variables, but they're not objects you own, so they'll probably be gone by the time you need them and replaced by something unpredictable. You need to retain them or use proper setters.
You problem is:
static PoorAPI2 *_instance;
C, and by inheritance Objective-C, do not initialize variables. Just change to:
static PoorAPI2 *_instance = nil;
Also I am of the school that adding extra code to try to prevent the singleton from being used as a single is a total waste of time, and only give you more code with more possibilities for bugs.
So if I was you then I would remove every method from +[PoorApi2 allocWithZone:] and down. Objective-C is a dynamic language and if a client wanted to instantiate a second instance of your singleton then it would be able to do so despite all your wasted extra lines of code. At the most I would add a log like this:
-(id)init{
if (_instance) NSLog(#"WARNING: PoorAPI2 already has a shared instance.");
if(self = [super init]){
apiToken = [[NSString alloc] initWithString:#""];
timeOpened = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
tokenTTL = 0;
}
return self;
}
Creating a second instance of a singleton is a programming error and should be caught in development. Not a problem you should add extra lines of code to hide.