Cannot get to NSErrors from unit tests - objective-c

I am setting up unit tests to run methods that should generate an NSError. For some reason, I can't get to the NSError from the unit tests. I created a sample method to test this, and it still doesn't work. What am I doing wrong?
Implementation file:
- (BOOL)createAnError:(NSError **)error {
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
[errorDetail setValue:NSLocalizedString(#"This error should be testable", #"")
forKey:NSLocalizedDescriptionKey];
[errorDetail setObject:self
forKey:NSValidationObjectErrorKey];
NSError *cannotDeleteError = [NSError errorWithDomain:#"myErrorDomain"
code:12345
userInfo:errorDetail];
if (*error = nil)
*error = cannotDeleteError;
return NO;
}
Unit Test:
- (void)testNSErrors {
Unit *myObj = [NSEntityDescription insertNewObjectForEntityForName:#"TestObject"
inManagedObjectContext:managedObjectContext];
NSError *error = nil;
STAssertFalse([myObj createAnError:&error], #"This method should return NO");
STAssertEquals([error code], 12345, #"The error code is incorrect. (error = %#)", error);
}
The error I'm seeing in the build results is: error: -[LogicTests testNSErrors] : '0' should be equal to '12345': The error code is incorrect. (error = (null)).
So why is this happening? Am I creating the NSError incorrectly, testing for it incorrectly, or both?
Thank you!

You are not creating the error correctly. In your sample method, you test for if (*error = nil). This is an assignment. What you really mean to say is: if (*error == nil), which uses the equality operator.
Change that and you should get a positive result.

Related

NSError: passing error in method

I'm not sure if this is correct or not:
- (void)parseSomething:(id)targetObject error:(NSError **)error {
NSError *parserError = nil;
[myParser parse:targetObject error:&parserError];
if (parserError != nil) {
*error = parserError;
}
}
the line:
*error = parserError;
I set error in parameter to be a local error, is it done correctly?
or should I do:
error = &parserError;
instead?
You need to make sure error isn't nil before you try to dereference it. Also, there is no need for the local NSError. It'd write that code this way:
- (void)parseSomething:(id)targetObject error:(NSError **)error {
[myParser parse:targetObject error:error];
}
But if you really wanted the locale variable (or for demonstration purposes). then this:
- (void)parseSomething:(id)targetObject error:(NSError **)error {
NSError *parserError = nil;
[myParser parse:targetObject error:&parserError];
if (error && parserError) {
*error = parserError;
}
}
Also, most methods that have an NSError out parameter like this usually have a BOOL return value or some other return value to indicate success or not. You shouldn't rely in the error parameter to indicate whether there was an error or not.
- (BOOL)parseSomething:(id)targetObject error:(NSError **)error {
NSError *parserError = nil;
BOOL ok = [myParser parse:targetObject error:&parserError];
if (error && parserError) {
*error = parserError;
}
return ok;
}
In this case you would probably want to pass the error further without intermediate variables. However, you need to be sure the myParser object handles the error parameter properly.
- (void)parseSomething:(id)targetObject error:(NSError **)error
{
[myParser parse:targetObject error:error];
}
In case you want to handle it here, you need to make sure the pointer to pointer error points to something, otherwise you will have crash when dereferencing it.
For example: the method was called like this
[object parseSomething:targetObject error:NULL];
The following line leads to crash:
*error = parserError;
Correct code would look like this:
- (void)parseSomething:(id)targetObject error:(NSError**)error
{
NSError* parserError = nil;
[myParser parse:targetObject error:&parserError];
if (error != nil)
{
*error = parserError;
}
}
Not a problem when parserError is nil, but when error is nil.

UnitTesting: assertThat arguments incompatible type or pointer to integer without a cast

I am trying to set up my project TDD, so I've got the following unit test:
- (void)testOnDoesUsernameExistsShouldReturnFalseWhenInvalidJSONResponseFromService {
id mock = [OCMockObject partialMockForObject:(NSObject *) (id <ServiceHelperProtocol>) self.serviceHelper];
[[[mock stub] andReturn:#"invalid-json-response"] get:[OCMArg any]];
assertThat([self.signUpService doesUsernameExist:#"testusername"], true);
}
This test runs against this method:
- (BOOL)doesUsernameExist:(NSString *)userName {
NSString *url = [[NSString alloc] initWithFormat:#"%#/%#/api/signup.username_exists/?username=%#", kAPPLICATION_HOST, kAPPLICATION_NAME, userName];
NSString *responseString = [self.serviceHelper get:url];
if (responseString != nil && ![responseString isEqualToString:#""]) {
#try {
NSDictionary *dictionary = [responseString JSONValue]; // auto-released
NSString *usernameExists = [dictionary objectForKey:#"username_exists"];
return usernameExists != nil && ![usernameExists isEqualToString:#"null"];
}
#catch (NSException *e) {
NSLog(#"Unable to parse JSON: %#", e.description);
}
}
[responseString release];
[url release];
return false;
}
When I run this test, I get the following warning on line 94 (the assertThat line):
passing argument 2 of 'HC_assertThatWithLocation' makes pointer from integer without a cast
When I substitute the macro I get this line of code:
HC_assertThatWithLocation(self, [self.signUpService doesUsernameExist:#"testusername"], HC_is(0), "_file_name_", 0);
For some reason my test fails, but I can't figure out what is wrong. Is the test case wrong, is the implemented method wrong or does it has something to do with the OCMock framework in conjunction with the OCHamcrest matchers ?
I'm using Xcode 4.2 with both OCMock 1.66 and OCHamcrest 1.7.
Any thoughts ?

NSPredicate - Unable to generate SQL for predicate, I wonder why?

I have already solved my problem [Blindly] without understanding root cause. But I would rather understand a concept from a professional. So could you please tell me why below identical code one works but another doesn't.
Code 1: Doesn't work
//Above code omitted...
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"gender == m"]; //NOTICE HERE
[request setPredicate:predicate];
NSError *error = nil;
self.people = [self.managedObjectContext executeFetchRequest:request error:&error];
//Below code omitted...
Code 2: Does work
//Above code omitted...
NSString *type = #"m";
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"gender == %#",type]; //NOTICE HERE
[request setPredicate:predicate];
NSError *error = nil;
self.people = [self.managedObjectContext executeFetchRequest:request error:&error];
//Below code omitted...
Forgot to tell about what error I got, I got SIGABRT on below line, When I executed Code 1.
self.people = [self.managedObjectContext executeFetchRequest:request error:&error];
And one more thing, in GCC error was it cannot format predicate because of "gender == m".
Enlighten me!!
Thanks
See the predicate programming guide (heading "Literals"). You can use literals in your string but you have to enclose them in quotes, so
NSPredicate * predicate = [NSPredicate predicateWithFormat:#"gender == 'm'"];
Would have worked. When predicateWithFormat adds in the argument, it knows it is a string. When you just have m in there, it doesn't know what to do with it, hence the error.
example with swift
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let fetchRequest = NSFetchRequest(entityName:"Words")
fetchRequest.predicate = NSPredicate(format: "letter == '\(letter)'")
var error: NSError?
let fetchedResults =
managedContext.executeFetchRequest(fetchRequest,
error: &error) as? [NSManagedObject]
if let results = fetchedResults {
println(results)
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}

HTTP server works in Cocoa application but not test case -- run loop issue?

I'm trying to add a GHUnit test case to this SimpleHTTPServer example. The example include a Cocoa application that works fine for me. But I can't duplicate the behavior in a test case.
Here is the test class:
#import <GHUnit/GHUnit.h>
#import "SimpleHTTPServer.h"
#interface ServerTest : GHTestCase
{
SimpleHTTPServer *server;
}
#end
#implementation ServerTest
-(void)setUpClass
{
[[NSRunLoop currentRunLoop] run];
}
- (NSString*)requestToURL:(NSString*)urlString error:(NSError**)error
{
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:1];
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:error];
NSString *page = nil;
if (error == nil)
{
NSStringEncoding responseEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)[response textEncodingName]));
page = [[NSString alloc] initWithData:data encoding:responseEncoding];
[page autorelease];
}
return page;
}
- (void)testPortReuse
{
unsigned int port = 50001;
NSError *error = nil;
NSString *path, *url;
server = [[SimpleHTTPServer alloc] initWithTCPPort:port delegate:self];
sleep(10);
path = #"/x/y/z";
url = [NSString stringWithFormat:#"http://localhost:%u%#", port, path];
[self requestToURL:url error:&error];
GHAssertNil(error, #"%# : %#", url, error);
[server release];
}
- (void)processURL:(NSURL *)path connection:(SimpleHTTPConnection *)connection
{
NSLog(#"processURL");
}
- (void)stopProcessing
{
NSLog(#"stopProcessing");
}
#end
I've tried sending requests via NSURLRequest and also (during the sleep) via a web browser. The delegate methods -processURL and -stopProcessing are never called. The problem seems to be that [fileHandle acceptConnectionInBackgroundAndNotify] in SimpleHTTPServer -initWithTCPPort:delegate: is not causing any NSFileHandleConnectionAcceptedNotifications to reach the NSNotificationCenter -- so I suspect a problem involving run loops.
The problem seems to be with the NSFileHandle, not the NSNotificationCenter, because when [nc postNotificationName:NSFileHandleConnectionAcceptedNotification object:nil] is added to the end of initWithTCPPort:delegate:, the NSNotificationCenter does get the notification.
if (error == nil)
That should be:
if (data != nil)
error here is the passed-in pointer to an NSError* - it will only be nil if the caller passed nil instead of a reference to an NSError* object, which isn't what your -testPortReuse method does.
It would also be incorrect to dereference it (as in if (*error == nil)), because error arguments are not guaranteed to be set to nil upon error. The return value indicates an error condition, and the value returned in the error argument is only meaningful or reliable if there is an error. Always check the return value to determine if an error happened, then check the error parameter for details only if something did in fact go wrong.
In other words, as it's written above, your -requestToURL:error: method is incapable of handling success. Much like Charlie Sheen. :-)

In Objective-C, I'm trying to encapsulate multiple error-able calls and "return" the most useful error

I put "return" in quotes because I don't want to literally return it. I want to do it similar to how you pass a pointer-to-a-pointer for [NSString stringWithContentsOfFile:usedEncoding:error:].
I would like to make parseFiles:error return nil and have the error reference that was passed in contain the first or second error, depending on which one failed. It seems like a Cocoa way to do it?
EDIT: Sorry, I should've been more clear about where I was having the problem. If the first path is bogus, it functions as I want. (I get the error instance outside and it prints.) If the first path is legit, as it filler string below implies, I get EXC_BAD_ACCESS.
But now I fixed it. I need to refer to it as *error inside the parseFiles:error: method and use == nil when checking if it failed. I thought I could just to if (error)...
EDIT 2 Ok, it doesn't work. I'm getting EXC_BAD_ACCESS. I'm not sure what I'm doing wrong with the conditions that check for the errors.
#implementation PassingError
- (id)init {
self = [super init];
NSError *error;
[self parseFiles:#"/untitled.py" error:&error];
if (error != nil) {
NSLog(#"I failed because: %#", error);
}
return self;
}
// Wraps with reading errors.
- (NSString *)parseFiles:(NSString *)path error:(NSError **)error {
NSStringEncoding enc1;
NSString *contents1 = [NSString stringWithContentsOfFile:path
usedEncoding:&enc1 error:*&error];
// there was a read error
// I need an asterisk here...
if (*error != nil) {
// ...and also one here
NSLog(#"FIRST ERROR: %#", *error);
return nil;
}
// here is where you'd do something that might cause another error,
// I'll just try and read a second file for simplicity
NSStringEncoding enc2;
NSString *contents2 = [NSString stringWithContentsOfFile:#"/untitled.py"
usedEncoding:&enc2 error:*&error];
// there was a SECOND error
if (*error != nil) {
NSLog(#"SECOND ERROR: %#", *error);
return nil;
}
// return both or whatever
return [NSArray arrayWithObjects:contents1, contents2, nil];
}
#end
Passing pointers around in Objective-C can get confusing. I remember having trouble grasping what needed to be done. When you have a method like this:
- (BOOL) saveValuesAndReturnError:(NSError **) error
{
BOOL success = [self doSomethingImportant];
if (!success && error)
{
// Unsuccessful and error is a valid ptr-to-ptr-to-NSError.
// Basically, someone has given us the address of a (NSError *).
// We can modify what that pointer points to here.
*error = [NSError errorWithDomain:#"myDomain" code:100 userInfo:nil];
}
return success;
}
This is intended to be invoked like this:
// If the caller doesn't care that it failed:
[someObject saveValuesAndReturnError:NULL];
// Or, if the caller wants to get error information on failure
NSError *anError = nil;
BOOL success;
// pass address of our (NSError *)
success = [someObject saveValuesAndReturnError:&anError];
if (!success)
{
// anError now points to an NSError object, despite being initialised to nil,
// because we passed the address of our NSError ptr, the method was able to
// change where `anError` points to.
NSLog (#"An error occurred while saving values: %#", anError);
}
Perhaps a very relevant read in this case is a CIMGF blog post covering exactly this topic.
However...
I remember reading a while ago that methods that return errors via method arguments such as stringWithContentsOfFile:usedEncoding:error: make no guarantee not to modify the error argument for success. In other words, you cannot rely on the value of the error parameter if the method succeeded. In your particular case, it may be better to do:
- (NSString *)parseFiles:(NSString *)path error:(NSError **)error {
NSStringEncoding enc1, enc2;
NSError *innerError;
NSString *contents1 = [NSString stringWithContentsOfFile:path
usedEncoding:&enc1
error:&innerError];
if (contents1 == nil)
{
if (error) *error = innerError;
return nil;
}
NSString *contents2 = [NSString stringWithContentsOfFile:#"/untitled.py"
usedEncoding:&enc2
error:&innerError];
if (contents2 == nil)
{
if (error) *error = innerError;
return nil;
}
// do whatever with contents1 and contents2
}