I am currently researching on how to efficiently add some unit tests to my app's ViewControllers. So far it worked pretty well until I tried to that that a specific view controller presents another one.
I am using OCMock and XCTest. The test is as follows
id partialMock = OCMPartialMock([TestViewController class]);
[partialMock doSomeStuff];
OCMVerify([partialMock presentViewController:[OCMArg any] animated:[OCMArg any] completion:[OCMArg any]]);
As you can see, I only want to verify that presentViewController was called to the tested view controller inside doSomeStuff function. Please note that the given example is a simplified version of what I currently have. Main difference being that I am verifying that the argument viewController is another mocked object.
Problem is since doSomeStuff method is not stubbed, the call is then forwarded to the real TestViewController instance, which then calls presentViewController on itself, then not firing the partialMock's verification.
Is there something I am missing? Or is it truly undoable what I am trying to achieve?
You can stub the method you want to supress by using andDo(nil) as described in 2.10: http://ocmock.org/reference/
Related
I'm new to Xcode.I'm trying to figure out how to use showViewController.My question is how to call the UIViewController that to display ? via name or a identifier? And where can I find them ? In addition, the document says The default implementation of this method calls the targetViewControllerForAction:sender: method to locate an object in the view controller hierarchy that overrides this method,what does it mean? Should I call targetViewControllerForAction:sender:fist when using showViewController?
showViewController behavior basically depends on context you're app is in.
But calling it is really simple. All you need is reference to your UIViewController object(or it's subclass).
You can do it in couple of different ways:
If you're using Storyboards, give your storyboard view controller identifier, and use API call: UIStoryboard(name, bundle).instantiateViewControllerWithIdentifier(identifier). This gives you UIViewController that you should pass as an argument to showViewController. You could find information about API here: https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIStoryboard_Class/index.html#//apple_ref/doc/uid/TP40010909-CH1-SW6
Also, you could get your UIVIewController instance directly constructing it using proper initializer.
So: not worry about this targetViewController method, just get your object (initialize it via constructor or get it from storyboard), and pass it to show method.
In case you have further questions - feel free to ask.
I'm dealing with a design Problem right now. I'm writing a Swift App, wich has a Storyboard with multiple ViewControllers and a Class HTTPRequests, wich performs GET and POST Requests on a RESTful Webservice.
My Problem right now is as following. From a view controller, I have to perform a a GET Request on the Webservice to fetch data. The received Data should be displayed trough this view controller on the specific view.
I see two possibilities to achieve this right now:
Put the Request function into the ViewController. Perform an asynchronus request and update the view in this asynchronus task as soon as data are received.
I don't really like this approach for two reasons. First, I don't want the Request function inside the ViewController, because I need the same function in multiple ViewControllers and this leads to code duplication. Second, I don't want to update the view inside the asynchronus task.
Put the Request function into the HTTPRequests (Singleton) Class. Call the requests from a ViewController and get a return value back. With this value, the ViewController is able to update its view.
I like this approach, because it's in design concerns "cleaner". My only problem is the return value. How can I return a value from an asynchronus task in Swift?
If possible please with code examples.
Or is there a better approach than those two?
Edit:
Thanks to your answers and this Blog Post I could successfully implement approach 2 with delegates.
You don't have to use a singleton, but rather you can have a separate class (model) that does all the web requests. You can implement a protocol in the model and set you view controller to be delegates. Sorry I'm on mobile so I can't include code, but I'd be glad to add it later.
I am going through an application and adding Unit Tests. The application is written using storyboards and supports iOS 6.1 and above.
I have been able to test all the usual return methods with no problem. However I am currently stumped with a certain test I want to perform:
Essentially I have a method, lets call it doLogin:
- (IBAction)doLogin:(UIButton *)sender {
// Some logic here
if ( //certain criteria to meet) {
variable = x; // important variable set here
[self performSegueWithIdentifier:#"memorableWord" sender:sender];
} else {
// handler error here
}
So I want to test that either the segue is called and that the variable is set, or that the MemorableWord view controller is loaded and the variables in there are correct. The variable set here in the doLogin method is passed through to the memorableWord segues' destination view controller in the prepareForSegue method.
I have OCMock set up and working, and I am also using XCTest as my unit testing framework. Has anyone been able to product a unit test to cover such a situation??
It seems that Google and SO are pretty bare in regards to information around this area.. lots of examples on simple basic tests that are pretty irrelevant to the more complex reality of iOS testing.
You're on the right track, your test wants to check that:
When the login button is tapped doLogin is called with the loginButton as the sender
If some criteria is YES, call performSegue
So you should actually trigger the full flow from login button down to performSegue:
- (void)testLogin {
LoginViewController *loginViewController = ...;
id loginMock = [OCMockObject partialMockForObject:loginViewController];
//here the expect call has the advantage of swallowing performSegueWithIdentifier, you can use forwardToRealObject to get it to go all the way through if necessary
[[loginMock expect] performSegueWithIdentifier:#"memorableWord" sender:loginViewController.loginButton];
//you also expect this action to be called
[[loginMock expect] doLogin:loginViewController.loginButton];
//mocking out the criteria to get through the if statement can happen on the partial mock as well
BOOL doSegue = YES;
[[[loginMock expect] andReturnValue:OCMOCK_VALUE(doSegue)] criteria];
[loginViewController.loginButton sendActionsForControlEvents:UIControlEventTouchUpInside];
[loginMock verify]; [loginMock stopMocking];
}
You'll need to implement a property for "criteria" so that there is a getter you can mock using 'expect'.
Its important to realize that 'expect' will only mock out 1 call to the getter, subsequent calls will fail with "Unexpected method invoked...". You can use 'stub' to mock it out for all calls but this means it will always return the same value.
IMHO this seems to be a testing scenario which has not properly been setup.
With unit tests you should only test units (e.g. single methods) of your application. Those units should be independent from all other parts of your application. This will guarantee you that a single function is properly tested without any side effects.
BTW: OCMock is great tool to "mock out" all parts you do not want to test and therefore create side effects.
In general your test seems to be more like an integration test
IT is the phase of software testing, in which individual software modules are combined and tested as a group.
So what would I do in your case:
I would either define an integration test, where I would properly test all parts of my view and therefore indirectly test my view controllers. Have a look at a good testing framework for this kind of scenario - KIF
Or I would perform single unit tests on the methods 'doLogin' as well as the method for calculating the criteria within your if statement. All dependencies should be mocked out which means within your doLogin test, you should even mock the criteria method...
So the only way I can see for me to unit test this is using partial mocks:
- (void)testExample
{
id loginMock = [OCMockObject partialMockForObject:self.controller];
[[loginMock expect] performSegueWithIdentifier:#"memorableWord" sender:[OCMArg any]];
[loginMock performSelectorOnMainThread:#selector(loginButton:) withObject:self.controller.loginButton waitUntilDone:YES];
[loginMock verify];
}
Of course this is only an example of the test and isn't actually the test I am performing, but hopefully demonstrates the way in which I am having to test this method in my view controller. As you can see, if the performSegueWithIdentifier is not called, the verify with cause the test to fail.
Give OCMock a read, I have just bought a book from amazon about Unit Testing iOS and its really good to read. Looking to get a TDD book too.
So in my app I have the following situation:
BackendCommunicatingClass -> (owned by) -> ModelClass -> (owned by) -> HomescreenViewController
HomescreenViewController is a delegate for ModelClass.
ModelClass is a delegate for BackendCommunicatingClass.
Also on when the app launches for the first time, I have this:
WelcomeViewController -> (owned by) -> HomescreenViewController
HomescreenViewController is delegate for WelcomeViewController.
When the user types a username and password in the WelcomeViewController, this information needs to get all the way to BackendCommunicatingClass, and then all the way back to WelcomeViewController to display error if needed. Right now I have implemented this by passing information to each class in the communication chain, until it gets to BackendCommunicatingClass. The result is a lot of duplication of protocol methods and I feel like I'm doing it wrong.
What do you think?
Well I understand it is not the clearest solution, but without seing the code, and the purpose of your program, this what I suggest.
Implement Key Value Observing (KVO) in Back End view controller, observing change in the Home View Controller. As soon as Back end controller detect change in the text field, trough a dedicated variable in Home View controller, it fires all the operation it has to do.
When back end finish, it sends a NSNotification with the result of the operation. Home view controller which you have made listening to such custom notification, react to that and display error message or other staff.
It may sounds complicated, but KVO and notification are easy to implement, and there are plenty of docs and tutorial on the net.
If there is a clear 1:1 mapping of what those delegate protocols provide AND the delegate does not deal in UI stuff that nothing except the directly owning view controller should be concerned with, you could pass the delegate along to the end of the chain and set it directly as a delegate. This is what delegates are for - being able to allow an otherwise unconcerned object to communicate with another object.
That said, depending on how strict your layering policy is, you may prefer to encapsulate the information at every step by having different delegates.
Overview
I have a iOS project that uses core data
The core data is used by view controllers as well as for notifications
Implementation
Created a singleton class for database activities called DatabaseEngine
In the appDelegate didFinishLaunchingWithOptions, DatabaseEngine is instantiated
DatabaseEngine contains properties (delegate) for the view controller and for notifications
In the viewDidLoad of the view controller I am setting the DatabaseEngine delegate to the view controller instance
Once the database is opened, the completion handler (through the delegate properties) calls the methods to setup the view controller and notifications
Concern (Timing issue)
I am concerned there might be scenario (a timing issue), where the DatabaseEngine is created first and at that moment the view controller's viewDidLoad would not be executed, and therefore the DatabaseEngine delegate would not initialized, therefore the database would execute the completionHandler but since the delegate is nil, no tasks would be done
What I have done to address the concern
Inside the view controller's viewDidLoad, I am checking if the Database is up and if the view controller is not loaded, if yes then i execute the tasks (setting up the views of the view controller) again.
Note- I am NOT using threads explicitly but based on my understanding completionHandler is executed asynchronously.
Question
I have tried it several times, and the view controller data is loaded correctly and there seems to be no timing issue. I even tried looping through a large value(to create a delay) and still there is no timing issue. I wonder why ?
Is my implementation a good design or is there a better way to do this ?
Is that the correct way to address my concern ?
Your design is a bit convoluted, but seems solid. (I prefer to have core data managed by the app delegate, but your approach is just as fine if you prefer it.)
I would, however, use the usual pattern of lazy initialization of your DatabaseEngine class. In this way, when it is needed and really does not exist, it will create itself and do the necessary initialization routines while the view controller will wait until the call to the engine returns something.
// in view controller viewDidLoad, e.g.
self.managedObjectContext = [databaseEngine managedObjectContext];
If the context is not initialized, it will happen here.
I think the best approach too is to have your app delegate manage the data. Seems like the best approach, and it is what a default CD application template does.
I would look into using MagicalRecord, which is pretty amazing if you ask me. With MagicalRecord you just call [NSManagedObjectContext MR_defaultContext]; and you get the default context just like that. MR also has amazing class methods for free like
NSArray *array = [SomeObject findAll]
which returns an array with all your CD objects. You can even set predicates, etc. and it's quite fast.