How to mock class methods in objective c to use with TDD - objective-c

I am using a third party framework to send data to a server. I am mocking that third party framework to isolate and test how my code interacts with it. This is to avoid to avoid waiting to get back data from the network while unit testing, and because I want to test the error handling code that I've written.
I am not using OCMock or anything like it, I am writing my own mock object by subclassing the 3rd party framework.
My method looks like this-
- (void)loginWithCredentials:(NSDictionary *)credentials
{
NSDictionary *credentials = [self credentials];
NSString *username = [credentials objectForKey:kUserCredintialUsername];
NSString *password = [credentials objectForKey:kUserCredintialPassword];
[ThirdPartyClass loginWithUsername:username
andPassword:password
block:^(ThirdPartyClass *user, NSError *error){
if (user) {
NSLog(#"We logged in");
}
else if (error) {
NSLog(#"%#", [error errorString]);
}
}];
}
What I'd like to do is to is to call loginWithUsername:andPassword:block: on my mock in my unit tests. The current method is, obviously, untestable because it doesn't follow "tell, don't ask" (I'm not telling loginWithCredentials: which class to call loginWithUser...block: on). The only way that I can think to solve this is to add a class variable to my instance or class or add an argument to loginWithCredentials: so I can substitute my mock in. The production code doesn't gain any clarity by doing so - it is already being written as an adapter to the 3rd party library. Instead, I would prefer to try to refactor the loginWithCredentials: method.

There is an HTTP stubbing library at https://github.com/AliSoftware/OHHTTPStubs. It works by catching outgoing requests and returning file data. It also allows rudimentary simulation of loading speeds for different types of networks.

Related

CFNetwork error handling in Swift

I have a block of objective-c code that responsible for handling HTTP errors by checking the NSError code error. The code sends the error message back to a delegate (unless it's a error code that the app ignores, such as cancelled requests)
failure:^(NSURLSessionTask *task, NSError *error, id responseObject) {
NSString *errorMessage = [self getErrorMessage:responseObject withError:error];
if (error.code!=kCFURLErrorCancelled &&
error.code!=kCFURLErrorCannotFindHost &&
error.code!=kCFURLErrorCannotConnectToHost &&
error.code!=kCFURLErrorNetworkConnectionLost &&
error.code!=kCFURLErrorDNSLookupFailed &&
error.code!=kCFURLErrorNotConnectedToInternet &&
error.code!=kCFURLErrorTimedOut) {
if ([self.delegate respondsToSelector:#selector(didFailed:)]) {
[self.delegate didFailed:errorMessage];
}
}
if (completionBlock != nil) completionBlock(NO);
}];
I have several questions / issues related to this code block.
Is is sufficient to just check the error code? The framework I'm using might return a different type of errors and I'm not sure those error codes are unique.
How would I go and write the same code in Swift? I did find the error codes definitions under CFNetworkErrors enum. However, the error.code & the value from the CFNetworkErrors enum cannot be compared directly, as they have different types. Is it possible to cast error.code to a CFNetworkErrors and then compare the error codes?
Can I safely switch to use the NSURL errors such as NSURLErrorCancelled? Is there a 1-to-1 mapping between the CFNetworks errors and NSURL errors?
It is probably not sufficient to check the error code. You almost certainly need to check the HTTP status code as well, unless the delegate method you're calling already does that. An NSError typically tells you about transport-level failures, whereas the status code tells you about server-side failures, such as a file not existing.
No idea. That question is mostly unrelated to networking, so you should probably ask a separate question for it, and tag it as a Swift question. :-)
Yes, you should be using the Foundation codes. The values should always be identical, but there are likely to be more Foundation error codes than CF codes going forward, as not all of the Foundation-level functionality is exposed publicly at the CF layer.

Monitoring Reachability with AFNetworking in unit test fails

In a project I am working I have implemented the HTTP Manager Reachability example.
When I run the actual app, it goes inside the block and from there to the switch:
[manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
In addition, when I call ...reachabilityManager] isReachable method returns true as expected.
The problem occurs when I try to unit test a class method I wrote that uses ...reachabilityManager] isReachable as a precondition - it returns false and what is weird that during a debug I have noticed that it doesn't go inside the above block, it skips it.
Of course, in the actual app it goes inside the block.
I have even tried to mock the class that implements the HTTP Manager Reachability example using OCMock in the unit test but it gave me the same result:
// NetworkClass implements the example
NetworkClass *networkClass = [[NetworkClass alloc] init];
id mockedNetworkClass = OCMPartialMock(networkClass);
// startNetworkMonitoring method implements the whole example above
[mockedNetworkClass startNetworkMonitoring];
// Giving enough time for AFNetworking to finish
[NSThread sleepForTimeInterval:60.0f];
EDIT1:
Looks like semaphore/XCTestExpectation won't help, the problem is in AFNetworkReachabilityManager::startMonitoring:
The only way that we could get the callback we want is inside startMonitoring method at dispatch_async(dispatch_get_main_queue(), ^{
callback(status);
But it runs outside the unit test even if we use semaphore/XCTestExpectation as mentioned.
Still looking for soultions..
EDIT2:
I was trying to follow the objc.io for Testing Asynchronous Code but it seems to be missing some code and some of the explanations are lacking of integration details.
I'd imagine that sleeping the thread is causing issues.
Try using the expectations API documented in Writing Tests of Asynchronous Operations.
Something along the lines of this should get you started (note this is more of a demonstration of the expectations API rather than a complete working test case):
- (void)testReachability {
XCTestExpectation *expectation = [self expectationWithDescription:#"Wait for reachability"];
[manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
...
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:10 handler:^(NSError * _Nullable error) {
// timed out waiting for reachability
}];
}

Working with Parse and unit test

I'm learning unit testing and it's all very new to for me. I wonder if there are any Parse developers who can show example on how to properly unit test PFObject and PFUser for example.
As I said I'm really new to unit testing and this is what I have done:
- (void)setUp {
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
// Test object
self.testObject = [PFObject objectWithClassName:#"TestObject"];
self.testObject[#"testString"] = #"test string";
self.testObject[#"testNumber"] = #12;
self.testObject[#"testBool"] = [NSNumber numberWithBool:YES];
self.testObject[#"testDate"] = [NSDate date];
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testSaveUser {
[self.testObject saveInBackground];
}
Maybe I'm approaching this the wrong way, the test passes but it's not saving anything to my Parse app online.
I've done some basic unit testing tutorials (some calculations and expectations), but now I would like to learn on how to unit test with external web services, in this case Parse. I can't seem to find any info on this in the Parse documentation or when Googling. So all help is appreciated.
Where do you set the application id and client key? Maybe that's the problem. I'd recommend to clone your productive app in the parse webinterface and copy your appdelegate. In the copied appdelegate you can set the test app-id/clientkey.
Beside that, I encounter the following problems:
1) Your tests do not have a assert-statement. They will always pass.
2) I'd recommend to not use the async version of the parse methods.
3) Why do you test the framework? Let parse developers do that and test your code (imo).

Detecting if a method is being called from a block

I'm using AFNetworking's AFHTTPClient class to communicate with a Rails backend.
When creating objects, I want to run several API calls on the server using a batch API (I'm using batch_api in case you were wondering).
To nicely extend AFHTTPClient I was thinking of having an API that would look like this:
[[[SPRRailsClient] sharedInstance] batchOperations:^ {
NSMutableURLRequest *request = [[SPRRailsAPIClient sharedClient]
requestWithMethod:#"GET"
path:myPath
parameters:parameters];
AFHTTPRequestOperation *operation = [[SPRRailsAPIClient sharedClient]
HTTPRequestOperationWithRequest:request
success:nil
failure:nil];
}];
The trick would be to override SPRRailsClient (my AFHTTPClient subclass) so when requestWithMethod:path:parameters: and HTTPRequestOperationWithMethod:success:failure: are called within a batchOperations block if behaves in a different way (queues things, or reruns a different subclass of AFOperation.
The neat thing of this design is that it would allow me to keep existing code and only wrap it within the block in order for some calls to be executed in "batch mode".
My question is: how can I detect that a method is being called from a block? I need requestWithMethod:path:parameters: to detect that, and:
If called from a batchOperations block, behave in a different way.
If not called from a batchOperations block just call super.
I know it would be simpler to just add two additional methods to SPRRailsClient, but I thought this looked better.
Also, I figure it's possible, since some methods of UIView animations behave in a different way when called from within an animation block, and NSManangedObjectContext's performBlock: is probably doing something similar as well.
The only way I can think of would be to enqueue the batchOpperations blocks on a named queue, then check the queue name when executing the blocks. This would look something like:
In the top of your .m file, or in your method, either works.
static dispatch_queue_t myQueue;
Then within batchOpperations,
if (!myQueue){
myQueue = dispatch_queue_create("com.mydomain.myapp.myqueue", NULL);
}
dispatch_async(myQueue, batchOpperation);
Then within when your sharedClient methods are called, check the queue:
if ([[NSString stringWithCString:dispatch_queue_get_label(dispatch_get_current_queue()) encoding:NSUTF8StringEncoding] isEqualToString:#"com..."]){
something....
}
else{
[super ??];
}

How to test delegates asynchronously with Kiwi

H guys,
I have been trying for ages to find some good examples on how to use Kiwi testing to test delegate methods, asynchronously.
I have a manager class that defines the protocols for testing, with a pass and fail method returned in the delegate. Can anyone provide sample code on how to do this? Can I make the test class itself implement the to call the methods on the manager?
Thanks guys
You can do like in example
SPEC_BEGIN(IFStackOverflowRequestSpec)
describe(#"IFStackOverflowRequestSpec", ^
{
context(#"question request", ^
{
__block IFViewController *controller = nil;
beforeEach(^
{
controller = [IFViewController mock];
});
it(#"should conform StackOverflowRequestDelegate protocol", ^
{
[[controller should] conformToProtocol:#protocol(StackOverflowRequestDelegate)];
});
it(#"should recieve receivedJSON", ^
{
NSString *questionsUrlString = #"http://api.stackoverflow.com/1.1/search?tagged=iphone&pagesize=20";
IFStackOverflowRequest *request = [[IFStackOverflowRequest alloc] initWithDelegate:controller urlString:questionsUrlString];
[[request fetchQestions] start];
[[[controller shouldEventuallyBeforeTimingOutAfter(3)] receive] receivedJSON:any()];
});
it(#"should recieve fetchFailedWithError", ^
{
NSString *fakeUrl = #"asda";
IFStackOverflowRequest *request = [[IFStackOverflowRequest alloc] initWithDelegate:controller urlString:fakeUrl];
[[request fetchQestions] start];
[[[controller shouldEventuallyBeforeTimingOutAfter(1)] receive] fetchFailedWithError:any()];
});
});
});
Full example can be founded on this link.
You can do what I think you're trying to achieve by creating a mock object that stands in for the delegate, and then checking that the mock object receives the delegate callbacks that you expect. So the process would look like:
create a mock object that conforms to the delegate protocol:
id delegateMock = [KWMock mockForProtocol:#protocol(YourProtocol)];
set the mock as the delegate of your manager class:
[manager setDelegate:delegateMock];
create an object containing the data that will be returned by your manager class:
NSString *response = #"foo";
set the assertion that the mock should eventually be called with the method and response object (in this case, I'm expecting to receive managerRepliedWithResponse and foo)
[[[delegateMock shouldEventually] receive] managerRepliedWithResponse:response];
call the method under test:
[manager performMyMethod];
The key is setting the expectation before you call the method, and using shouldEventually which delays the assertion being checked and gives the manager object time to perform the method.
There's a range of expectations you can also use that are listed on the Kiwi wiki - https://github.com/allending/Kiwi/wiki/Expectations
I've written the process up in more detail in a post on my site, albeit that it's more specifically geared-up to the situation I was dealing with.