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).
Related
The part of a method that I am trying to test is as follows:
- (void)configureTableFooterView {
dispatch_async(dispatch_get_main_queue(), ^{
self.tableView.tableFooterView = nil;
if ([self.parser.resultSet isLastPage]) {
return;
}
});
}
I have written the unit test as follows:
- (void)testTableFooterViewConfigurationAfterLastPageLoaded {
id mockTableView = OCMClassMock([GMGFlatTableView class]);
OCMExpect([mockTableView setTableFooterView:[OCMArg isNil]]);
id resultSet = OCMClassMock([GMGResultSetInfo class]);
OCMStub([resultSet isLastPage]).andReturn(YES);
OCMStub([self.mockParser resultSet]).andReturn(resultSet);
id partialMockSUT = OCMPartialMock(self.sut);
OCMStub([partialMockSUT tableView]).andReturn(mockTableView);
[self.sut configureTableFooterView];
OCMVerifyAllWithDelay(mockTableView, 2.0);
//OCMVerifyAllWithDelay(partialMockSUT, 2.0);
}
I have another test in the same class which is testing the same things from with in the dispatch_async call on the main thread. The test expectations and verification setup in that test match this one. While that test passes, this one gets stuck in an infinite loop at the delayed verification step.
Interestingly, if I only run this 1 test, it passes with out any problems. Its only when this test is run with other tests that I see the problem.
UPDATE:
In unit test, execute the block passed in queue with dispatch_asyc
This is a much more relevant post. However, this fails almost in the exact same way as the original test method:
- (void)testTableFooterViewConfigurationAfterLastPageLoaded {
id mockTableView = OCMClassMock([GMGFlatTableView class]);
OCMExpect([mockTableView setTableFooterView:[OCMArg isNil]]);
id resultSet = OCMClassMock([GMGResultSetInfo class]);
OCMStub([resultSet isLastPage]).andReturn(YES);
OCMStub([self.mockParser resultSet]).andReturn(resultSet);
id partialMockSUT = OCMPartialMock(self.sut);
OCMStub([partialMockSUT tableView]).andReturn(mockTableView);
[self.sut configureTableFooterView];
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01]];
OCMVerifyAll(mockTableView);
}
The line with NSRunLoop crashes with EXC_BAD_ACCESS when run as suite but runs fine alone!
You can make class wrapper around dispatch_async, and pass it as dependency. Also you can make fake wrapper, and pass it in tests. If you interested in, I can provide much more detailed explanation.
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
}];
}
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.
I am trying to test that a timer object is stopped after a level is completed.. I have the following code:
-(void)advanceLevel {
int nextLevelId = self.currentLevel.id + 1;
self.currentLevel = [[Level alloc] initWithIdentifier:nextLevelId];
[self.timer stop];
[self prepareLevel];
}
...
The prepareLevel method resets the timer value and calls "start" on it--- so in order to test that advanceLevel actually stops the timer, I need to overwrite the prepareLevel method.
So in my unit test, I did the following:
-(void)testItStopsTheTimer {
[timer start];
id mockGame = [OCMockObject partialMockForObject:game];
[[[mockGame stub] andReturn:nil] prepareLevel];
[game advanceLevel];
STAssertFalse(timer.active, nil);
}
Which results in XCode saying "testItStopsTheTimer (Gametests) failed. Ended up in subclass forwarder for Game-0x12383060......."
So, is it not possible to stub out an existing method and replace it with nothingness?
What you're trying to do is definitely possible with OCMock.
What is the method signature for prepareLevel? If it returns void, your mock setup should be:
[[mockGame stub] prepareLevel];
not:
[[[mockGame stub] andReturn:nil] prepareLevel];
What you are trying to do is possible with OCMock. In your test code one lines stands out:
id mockGame = [OCMockObject partialMockForObject:game];
The question is, where does "game" come from? Is the same instance used in multiple tests? The error you are seeing can be caused by the following sequence: you are using expect on a partial mock, the expected method is called, then you are called the method again, but now there's no expectation left and the partial mock doesn't know what to do.
UPDATE: I have just changed OCMock so that in such cases the mock simply forwards the method to the real object. See: https://github.com/erikdoe/ocmock/commit/e03d4fe74465b4fe3fa33552e036de8986f8dec2
I am new to Objective-C and I am trying to start right away with TestDrivenDevelopment, since I find it really assuring, when at least the tests do pass.
Before that I made some Tutorials in Java where I got a little understanding for TDD. Brett Schucherts Video-Tutorials where he goes step by step through coding a full RPNCalculator is a gold mine to learn the stuff by watching him in action.
To reduce code duplication there is for instance this nice thing where you do:
#Before
public void init() {
/* Stuff that which will be set up before the call of each test method/*
}
which is then called before each test you have in your TestClass in Java.
This I want to realize in Objective-C + Xcode. I should mention, that I am using Xcode 4.3 (latest version) and that I am using the built in TestFramework.
The only thing I found in the web that came near what I am looking for was this answer on SO.
Unfortunately I am not able to reproduce the described way of doing things. A minimal runnable example and/or a more detailed explanation for a Newcomer would would be awesome and well appreciated!
By the way, sorry for the bad english. Still learning the language. :-)
Edit:
Here is a minimal example which does not work. Perhaps someone can tell me what is wrong. Xcode seems not to be able to recognize board inside the body of the test methods.
#import <SenTestingKit/SenTestingKit.h>
#interface ABoardShould : SenTestCase
#end
#import "ABoardShould.h"
#implementation ABoardShould
- (void)setUp
{
[super setUp];
int rowCount = 6;
int columnCount = 7;
Board *board = [[Board alloc] initWithShapeRowCount:rowCount andColumnCount:columnCount];
}
- (void)tearDown
{
// Tear-down code here.
[super tearDown];
}
- (void)testHaveItsShapeSetWhenInitialised {
STAssertEquals([board rowCount], rowCount, #"");
STAssertEquals([board columnCount], columnCount, #"");
}
- (void)testHaveTheDimensionsOfItsBoardMatchTheGivenShape {
NSMutableArray *expectedFields = [[NSMutableArray alloc] initWithCapacity:columnCount*rowCount];
for(int i=0; i < (rowCount*columnCount); i++) [expectedFields addObject: [NSNumber numberWithInt: 0]];
STAssertEquals([expectedFields count], [[board fields] count], #"");
}
The setup and teardown methods of your test case class must be called setUp and tearDown. This is described in Xcode Unit Testing Guide: Writing Test Case Methods.
In your example, board is a local variable in the setUp method. You need to make it an instance variable.