Rostyslav Druzhchenko
Rostyslav Druzhchenko

Reputation: 3773

How to replace async calls with mocks and predefined answers?

I have simple class for perform network stuff. It's a singleton and it encapsulates NSOperationQueue inside it. When class' user calls some method to getting data from network, this class creates proper instance of operation class inherited from NSOperation sets up it and adds to queue for performing. Obviously, that performing is making asynchronously in separated threads. After getting data from network NSOperation inherited object notifies my network class and it notifies interested delegates about data getting finished or error.

Question is, how can I make unit tests for checking network class' logic? Also, I don't actually want to test server side behavior. I just want to replace actual async call to server with mock and predefined answers to after test handlers' behavior. I want to check how are my classes work, not server side. I understand commonly logic for testing stuff like that but I little bit confused with using OCMock for it. Best answer will be code example. I'm using OCUnit and OCMock in my project for unit testing.

Also any articles or github links will be perfect.

Upvotes: 1

Views: 527

Answers (2)

Erik Doernenburg
Erik Doernenburg

Reputation: 3016

If all the asynchronous calls go through an internal method in your class, you can simply create a partial mock on your object and use stub/expect on that method. You can then call the public methods as normal and use the mock to verify that the internal method is called. Using the partial mock stops the real implementation from being called, so no network activity should occur.

As to the other half, the call-backs from the asynchronous operation, simply call the method that would be called directly from your tests, then check that your class does the right thing, either by checking its state with OCUnit asserts, or, if it in turn uses callbacks, with another mock.

Upvotes: 3

Chris Wagner
Chris Wagner

Reputation: 21013

So I know this is regarding OCMock... but I thought I'd put it out there that I do this successfully with Kiwi and it looks like this.

it(@"should refresh the client's temporary API key if it is stale before sending the request", ^{
    ISLDataServiceAdd *addRequest = [ISLDataServiceAdd withRecord:@{ISLFieldContact_FirstName: @"Jason"} table:ISLTableContact];

    [[clientMock shouldEventually] receive:@selector(apiKey) andReturn:VALID_API_KEY];
    [[clientMock shouldEventually] receive:@selector(hasTemporaryAPIKey) andReturn:theValue(YES)];
    [[clientMock shouldEventually] receive:@selector(isTemporaryAPIKeyStale) andReturn:theValue(YES)];
    [[clientMock shouldEventually] receive:@selector(refreshTemporaryAPIKeyAndWait:)];

    [addRequest sendRequestUsingClient:clientMock completion:nil failure:nil];
});

sendRequestUsingClient:completion:failure: is an asynchronous call, so by using shouldEventually with Kiwi, it knows that it needs to wait some time (default is 1 second) before those selectors will be called.

Upvotes: 1

Related Questions