I'm working on an iOS app whose primary purpose is communication with a set of remote webservices. For integration testing, I'd like to be able to run my app against some sort of fake webservices that have a predictable result.
So far I've seen two suggestions:
Create a webserver that serves static results to the client (for example here).
Implement different webservice communication code, that based on a compile time flag would call either webservices or code that would load responses from a local file (example and another one).
I'm curious what the community thinks about each of this approaches and whether there are any tools out there to support this workflow.
Update: Let me provide a specific example then. I have a login form that takes a username and password. I would like to check two conditions:
wronguser#blahblah.com getting login denied and
rightuser#blahblah.com logging in successfully.
So I need some code to check the username parameter and throw an appropriate response at me. Hopefully that's all the logic that I need in the "fake webservice". How do I manage this cleanly?
I'd suggest to use Nocilla. Nocilla is a library for stubbing HTTP requests with a simple DSL.
Let's say that you want to return a 404 from google.com. All you have to do is:
stubRequest(#"GET", "http://www.google.com").andReturn(404); // Yes, it's ObjC
After that, any HTTP to google.com will return a 404.
A more complete example, where you want to match a POST with a certain body and headers and return a canned response:
stubRequest(#"POST", #"https://api.example.com/dogs.json").
withHeaders(#{#"Accept": #"application/json", #"X-CUSTOM-HEADER": #"abcf2fbc6abgf"}).
withBody(#"{\"name\":\"foo\"}").
andReturn(201).
withHeaders(#{#"Content-Type": #"application/json"}).
withBody(#"{\"ok\":true}");
You can match any request and fake any response. Check the README for more details.
The benefits of using Nocilla over other solutions are:
It's fast. No HTTP servers to run. Your tests will run really fast.
No crazy dependencies to manage. On top of that, you can use CocoaPods.
It's well tested.
Great DSL that will make your code really easy to understand and maintain.
The main limitation is that it only works with HTTP frameworks built on top of NSURLConnection, like AFNetworking, MKNetworkKit or plain NSURLConnection.
Hope this helps. If you need anything else, I'm here to help.
I am assuming you are using Objective-C. For Objective-C OCMock is widely used for mocking/unit testing (your second option).
I used OCMock for the last time more than a year ago, but as far as I remember it is a fully-fledged mocking framework and can do all the things that are described below.
One important thing about mocks is that you can use as much or as little of the actual functionality of your objects. You can create an 'empty' mock (which will have all the methods is your object, but will do nothing) and override just the methods you need in your test. This is usually done when testing other objects that rely on the mock.
Or you can create a mock that will act as your real object behaves, and stub out some methods that you do not want to test at that level (e.g. - methods that actually access the database, require network connection, etc.). This is usually done when you are testing the mocked object itself.
It is important to understand that you do not create mocks once and for all. Every test can create mocks for the same objects anew based on what is being tested.
Another important thing about mocks is that you can 'record' scenarious (sequences of calls) and your 'expectations' about them (which methods behind the scenes should be called, with which parameters, and in which order), then 'replay' the scenario - the test will fail if the expectations were not met. This is the main difference between classical and mockist TDD. It has its pros and cons (see Martin Fowler's article).
Let's now consider your specific example (I'll be using pseudo-syntax that looks more like C++ or Java rather than Objective C):
Let's say you have an object of class LoginForm that represents the login information entered. It has (among others) methods setName(String),setPassword(String), bool authenticateUser(), and Authenticator* getAuthenticator().
You also have an object of class Authenticator which has (among others) methods bool isRegistered(String user), bool authenticate(String user, String password), and bool isAuthenticated(String user).
Here's how you can test some simple scenarios:
Create MockLoginForm mock with all methods empty except for the four mentioned above. The first three methods will be using actual LoginForm implementation; getAuthenticator() will be stubbed out to return MockAuthenticator.
Create MockAuthenticator mock that will use some fake database (such as an internal data structure or a file) to implement its three methods. The database will contain only one tuple: ('rightuser','rightpassword').
TestUserNotRegistered
Replay scenario:
MockLoginForm.setName('wronuser');
MockLoginForm.setPassword('foo');
MockLoginForm.authenticate();
Expectations:
getAuthenticator() is called
MockAuthenticator.isRegistered('wrognuser') is called and returns 'false'
TestWrongPassword
Replay scenario:
MockLoginForm.setName('rightuser');
MockLoginForm.setPassword('foo');
MockLoginForm.authenticate();
Expectations:
getAuthenticator() is called
MockAuthenticator.isRegistered('rightuser') is called and returns 'true'
MockAuthenticator.authenticate('rightuser','foo') is called and returns 'false'
TestLoginOk
Replay scenario:
MockLoginForm.setName('rightuser');
MockLoginForm.setPassword('rightpassword');
MockLoginForm.authenticate();
result = MockAuthenticator.isAuthenticated('rightuser')
Expectations:
getAuthenticator() is called
MockAuthenticator.isRegistered('rightuser') is called and returns 'true'
MockAuthenticator.authenticate('rightuser','rightpassword') is called and returns 'true'
result is 'true'
I hope this helps.
You can make a mock web service quite effectively with a NSURLProtocol subclass:
Header:
#interface MyMockWebServiceURLProtocol : NSURLProtocol
#end
Implementation:
#implementation MyMockWebServiceURLProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
return [[[request URL] scheme] isEqualToString:#"mymock"];
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
return request;
}
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b
{
return [[a URL] isEqual:[b URL]];
}
- (void)startLoading
{
NSURLRequest *request = [self request];
id <NSURLProtocolClient> client = [self client];
NSURL *url = request.URL;
NSString *host = url.host;
NSString *path = url.path;
NSString *mockResultPath = nil;
/* set mockResultPath here … */
NSString *fileURL = [[NSBundle mainBundle] URLForResource:mockResultPath withExtension:nil];
[client URLProtocol:self
wasRedirectedToRequest:[NSURLRequest requestWithURL:fileURL]
redirectResponse:[[NSURLResponse alloc] initWithURL:url
MIMEType:#"application/json"
expectedContentLength:0
textEncodingName:nil]];
[client URLProtocolDidFinishLoading:self];
}
- (void)stopLoading
{
}
#end
The interesting routine is -startLoading, in which you should process the request and locate the static file corresponding to the response in the app bundle before redirecting the client to that file URL.
You install the protocol with
[NSURLProtocol registerClass:[MyMockWebServiceURLProtocol class]];
And reference it with URLs like
mymock://mockhost/mockpath?mockquery
This is considerably simpler than implementing a real webservice either on a remote machine or locally within the app; the tradeoff is that simulating HTTP response headers is much more difficult.
OHTTPStubs is a pretty great framework for doing what you want that's gained a lot of traction. From their github readme:
OHTTPStubs is a library designed to stub your network requests very easily. It can help you:
Test your apps with fake network data (stubbed from file) and simulate slow networks, to check your application behavior in bad network conditions
Write Unit Tests that use fake network data from your fixtures.
It works with NSURLConnection, new iOS7/OSX.9's NSURLSession, AFNetworking (both 1.x and 2.x), or any networking framework that use Cocoa's URL Loading System.
OHHTTPStubs headers are fully documented using Appledoc-like / Headerdoc-like comments in the header files. You can also read the online documentation here.
Here's an example:
[OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
return [request.URL.host isEqualToString:#"mywebservice.com"];
} withStubResponse:^OHHTTPStubsResponse*(NSURLRequest *request) {
// Stub it with our "wsresponse.json" stub file
NSString* fixture = OHPathForFileInBundle(#"wsresponse.json",nil);
return [OHHTTPStubsResponse responseWithFileAtPath:fixture
statusCode:200 headers:#{#"Content-Type":#"text/json"}];
}];
You can find additional usage examples on the wiki page.
As far as option 1, I have done this in the past using CocoaHTTPServer and embedding the server directly in an OCUnit test:
https://github.com/robbiehanson/CocoaHTTPServer
I put up the code for using this in a unit test here:
https://github.com/quellish/UnitTestHTTPServer
After all, HTTP is by design just request/response.
Mocking a web service, wether by creating a mock HTTP server or creating a mock web service in code, is going to be about the same amount of work. If you have X code paths to test, you have at least X code paths to handle in your mock.
For option 2, to mock the web service you would not be communicating with the web service, you would be instead be using the mock object which has known responses.
[MyCoolWebService performLogin:username withPassword:password]
would become, in your test
[MyMockWebService performLogin:username withPassword:password]
The key point being that MyCoolWebService and MyMockWebService implement the same contract (in objective-c, this would be a Protocol). OCMock has plenty of documentation to get you started.
For an integration test though, you should be testing against the real web service, such as a QA/staging environment. What you are actually describing sounds more like functional testing than integration testing.
Related
I have a code fragment which I want to unit test , this code depends upon callback from network stack on event of receipt of data. The network calls are made through a library its basically amazon iOT library, thus I am not directly interacting with iOS network framework but this library. I want to unit test this code, not sure if its possible if yes how.
Attached is the code in question
static func subscribeForData(completionCallBack:((String,NSDictionary)->())?,errorCallBack:((NSError)->())?) {
let iotDataManager = AWSIoTDataManager.default()
let defaults = UserDefaults.standard
let login = .....
iotDataManager.subscribe(toTopic: "testNode/device/"+login, qoS: .messageDeliveryAttemptedAtLeastOnce, messageCallback: {
(payload) ->Void in
let stringValue = NSString(data: payload, encoding: String.Encoding.utf8.rawValue)!
})
}
The best way you can achieve this is through Dependency Injection (DI).
DI can be used as a mean to inject both the real networking code and the "mocked" one.
In order to deal with DI you need to modify your code. In particular an instance of AWSIoTDataManager should be passed to subscribeForData method instead of hardcoding it (why do you have a static method?).
There are different approaches in order to deal with this. One is described for example in The complete guide to Network Unit Testing in Swift. I think that if you read it, you will acquire something new useful for the feature.
Your "mocked" class (I put with "" since it could be defined as a stub or a spy) would have the same API provided by the real one.
As I stated previously, Unit Tests should be fast and should NOT depend on databases, real networks requests and so on.
Just to give a bit of backdrop: I recently started working on unit tests for the code that had been developed and maintained by some other company. My intention is to write the test cases without touching the application code .
Now coming to the problem: How can one get a reference to the CoreData stack of the application within the unit tests' target. After every service call, the response is stored in the persistent store of CoreData. I want to validate it from the unit tests class. I tried accessing the CoreData stack from unit tests but for some reason it was coming as nil after the code got compiled. Here's the piece of code in my setUp method.
- (void)setUpDataBase
{
RZCoreDataStack *stack = [[RZCoreDataStack alloc] initWithModelName:#"Skillsoft"
configuration:nil
storeType:NSSQLiteStoreType
storeURL:nil
options:RZCoreDataStackOptionDeleteDatabaseIfUnreadable];
NSAssert(stack != nil, #"Failed to create CoreData stack");
stack.mainManagedObjectContext.parentContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;
[RZCoreDataStack setDefaultStack:stack];
RZCoreDataStack *setStack = [RZCoreDataStack defaultStack];
NSLog(#"setStack Details >>> %#",setStack.description);
}
+ (RZCoreDataStack *)defaultStack
{
if ( s_defaultStack == nil ) {
RZVLogInfo(#"The default stack has been accessed without being configured. Creating a new default stack with the default options.");
// // s_defaultStack = [[RZCoreDataStack alloc] initWithModelName:nil
// configuration:nil
s_defaultStack = [[RZCoreDataStack alloc] initWithModelName:#"Skillsoft"
configuration:nil
storeType:NSSQLiteStoreType
storeURL:nil
options:RZCoreDataStackOptionDeleteDatabaseIfUnreadable];
s_defaultStack.mainManagedObjectContext.parentContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;
}
return s_defaultStack;
}`
The default stack is a static variable and is coming as nil ... I am assuming that 2 sets of static variables are being created one in application scope and other in unit tests scope ... I just want to write unit test case for a method in UIView controller that, in turn, makes a service call in scope of another layer. I want to validate it by getting a reference to CoreData in app and check if the data is being persisted properly... What is the right way to proceed with that ...
I have seen a couple of StackOverflow posts which suggests to create one's own CoreData stack in the setUp method, but with that I would need to persist the data in the same stack which I don't want to do at this moment ..
I could see that app delegate gets called when your run the unit test cases which in turn initialises quite a few objects in my project ..In order to avoid that , i have made a change in the main file itself to call testing app delegate instead of app's main delegate based on the target .. So all the initializations of the objects are being done in the unit tests..
Please suggest me the right approach to proceed...
P.S For CoreData access, the application uses a third party library named RZVinyl, which kinda made things even more complex for me ..
P.P.S My English is weak, so please let me know if I'm not clear in my explanation. I will update the problem statement..
What I want to implement is as follow:
A-app (calling app) : request the return value of a-string sent as parameter : request(a-string) -> b-string.
B-app (plug-in installed separately by me or others, it plays the role of dictionary or database ) : search a-string from database and return the result (b-string).
With successful experiences of plug-in on android and with Apple's confident rhetoric of plug-in, I thought plug-in, of course, run on iOS. After a lot of hard work, however, I finally found out:
* Note : The creation and use of loadable bundles is not supported in iOS.*
Nonetheless, not giving up, I finally made it with custom URl and pasteboard:
A-app : write a-string and false state to pasteboard & call B-app via custom URL.
B-app : viewDidLoad runs following func and thereafter exit program ; func { read pasteboard and search from database & write the result(b-string) and true state to pasteboard }
A-app : while-loop detects whether state is false or true. if true, catch b-string from pasteboard.
Anyway it works but it's too long thus almost useless. Do you have any idea for better solutions? Why doesn't Apple allow plug-in for iOS? Any responses are welcome. Thank you.
I can't answer why Apple doesn't allow plug-ins, but I can offer some advice on what you're trying to achieve.
The common pattern for sending data back to your application is to implement a callback url, so the A-app would also implement a custom URI and add that to the uri sent to B-app.
B-app would then process the uri as you have already implemented, but then instead of exiting, it simply sends the data you requested in the uri passed to it.
See http://x-callback-url.com for more details and example implementations.
I'm currently making block based wrappers around some methods of the Dropbox, Google Drive, and Box.com SDKs. I've successfully wrapped upload functionality into all three, and they all work nicely. The wrappers have a "common" interface that conform to a universal spec among the three services, which is why I'm doing this.
Dropbox is the only one that runs on delegate callbacks. I have no issues with the upload classes. However, I'm trying to implement some download classes in the same manner. I'm running into an issue so far that my returned instances are getting immediately deallocated before any of the delegate methods in the wrapper are called. First, check out the method structure of the _working_ "Upload" class:
+(id)uploaderWithPaths:(NSMutableDictionary *)paths
progress:(JAUploadProgressBlock)progBlock
completed:(JAUploadCompletedBlock)compBlock
failed:(JAUploadFailedBlock)failBlock;
-(void)upload;
//in .m
return [[[self class] alloc]initWithPaths:paths
progress:progBlock
completed:compBlock
failed:failBlock];
-(void)upload {
//setup Dropbox Rest Client
//configure delegate, etc
[dbRestClient doUpload...];
}
//delegate stuff
..onProgress {
self.progressBlock(progress);
..onComplete {
self.completedBlock(info);
..onFailed {
self.failedBlock(error);
Thus, instantiating and performing an upload goes like this:
JAUploadProgressBlock progress = ^(CGFloat percentage) {
NSLog(#"%.2f",percentage); }
JAUploadCompletedBlock completed = ^(NSMutableDictionary *info) {
NSLog(#"Success: %#",info); }
JAUploadFailedBlock failed = ^(NSError *error) {
NSLog(#"Failed: %#",[error localizedDescription];
JAUploadWrapperDropbox *uploader
= [JAUploadWrapperDropbox uploaderWithPaths:paths
progress:progress
completed:completed
failed:failed];
[uploader upload];
In the upload wrapper class, the instance does not get deallocated immediately, the delegate messages inside the wrapper get called, my block callbacks fire, and everything goes as planned.
However, my Download wrapper class, which is modeled exactly like the upload wrapper,
gets deallocated before any of the delegate methods in the wrapper are called.
This class is not conducive to being a property, because it's not suited for re-use. It's really meant to be used once or as one offs. Using a strong property fixes the issue, but like I said, I don't need to use a property for my upload wrapper class.
What am I doing wrong?
You can check this project out at https://github.com/justinxxvii/JAOperationWrapper
I did make a commit of the Download wrapper but the headers do not have any documentation yet.
I've spent all afternoon trawling the web for tutorials and examples of using OCMock but I've still no idea if its possible and if so how to use it in combination with NSURLConnection to set up test code to simulate a server sending data in response to HTTP POSTs or GETs.
Does anybody know of a handy tutorial for doing this?
UPDATE: I subsequently came across this:
how to unit test a NSURLConnection Delegate?
Yes, but I've found another package, OHHTTPStubs, to be a LOT easier for doing exactly what you're looking for:
https://github.com/AliSoftware/OHHTTPStubs
[OHHTTPStubs shouldStubRequestsPassingTest:^BOOL(NSURLRequest *request) {
return [request.URL.path isEqualToString:#"/api/oauth/access_token"];
} withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) {
return [OHHTTPStubsResponse responseWithFile:#"auth_login_passing.json" contentType:#"text/json" responseTime:OHHTTPStubsDownloadSpeedWifi];
}];