I am learning OCMock for iOS testing. What's the difference between "class mock" and "partial mock", and when should you use one vs the other?
http://ocmock.org/features/
Class mocks create objects that are pure mocks of a class instance.
Partial mocks take an instance of a class an allow you to stub any of its methods.
Suppose I have these classes:
#interface Foo : NSObject
- (void)doX;
#end
#implementation
- (void)doX
{
NSLog(#"X");
}
#end
#interface Bar : NSObject
- (void)doA:(Foo *)foo;
- (void)doB;
#end
#implementation Bar
- (void)doA:(Foo *)foo
{
NSLog(#"A");
[foo doX];
[self doB];
}
- (void)doB
{
NSLog(#"B");
}
#end
I'm interested in testing Bar's doA: method. I expect it to call doX on a Foo object, then to call its own doB method. I would implement this using a class mock of a Foo and a partial mock of a Bar.
- (void)test_doA_shouldCall_doX_and_doB
{
id objectUnderTest = [OCMockObject partialMockForObject:[Bar new]];
id fooMock = [OCMockObject mockForClass:Foo.class];
[[fooMock expect] doX];
[[objectUnderTest expect] doB];
// Make the call
[objectUnderTest doA:fooMock];
[objectUnderTest verify];
[fooMock verify];
}
You see here that my partial mock allowed me to call the real method I wanted to test while mocking an internal call to another of its instance methods. Because I didn't need any of the real functionality of Foo, however, I used a class mock.
Related
I am new in OCMock.
I use dispatch_once() created a singleton class MyManager :
#implementation MyManager
+ (id)sharedInstance {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
I have a method in School class which uses the above singleton:
#implementation School
...
- (void) createLecture {
MyManager *mgr = [MyManager sharedInstance];
[mgr checkLectures];
...
}
#end
Now, I want to unit test this method, I use a partial mock of MyManager:
- (void) testCreateLecture {
// create a partially mocked instance of MyManager
id partialMockMgr = [OCMockObject partialMockForObject:[MyManager sharedInstance]];
// run method to test
[schoolToTest createLecture];
...
}
I noticed that with OCMock, after I created the partial mock of my singleton MyManager instance, when run my method under test, it automatically use the partially mocked instance.
This is a bit weird to me, since in my test case above, I only created the partial mock of MyManager instance without injecting it to MyManager class,
how does OCMock automatically force the code under test use this mocked instance when [MyManager sharedInstance] is called in the code under test ? Could someone explain to me this?
partialMockForObject mocks the object you are passing to it.
In this case you are mocking the singleton (shared) object. You don't have to inject anything because sharedInstance is always returning the same object, now mocked. It is still the same reference.
Imagine partial mocking as a simple mutation of the passed object, it doesn't create a new instance so you don't have to inject it in this specific case.
Partial Mocks are pretty cool. Behind the scenes, OCMock subclasses the class you are mocking, and any method that you stub is updated in the partial mock subclass. So all references to your mocked class will now default to the subclass (mocked implementation) instead of the superclass (your implementation). There's a good explanation in the reference on this as well.
A weird situation arises while using delegates in iOS.
A delegate method send its control to different class rather than calling class implemented same delegate method.
Example :
I am having a Dashboard which is calling a web service (a different class implemented all web service calls). In dashboard global web service class object exists with delegate self. When we call a web service the control comes to this class. So thats fine.
Now i am having another class which got push from dashboard or some time from other view controllers as well and it is also calling same web service. It is having its own web service class object globally and set delegate to self.
When I am calling web service from this class with its web service object after web service call control goes to delegate of dashboard not to this class though call has been made from this class and delegation is set to self.
This situation arises to many place where pushed child also implemented the same delegate method which its previous class has also implemented and thus instead delegation called of pushed class the method is called of the previous class.
Here is the main lines of code explaining above example :
WebServiceCaller.h
Class handles all web services caller
#import <Foundation/Foundation.h>
#import "WebserviceEnum.h"
#protocol WebServiceCallerDelegate;
#interface WebServiceCaller : NSObject
{
__unsafe_unretained id <WebServiceCallerDelegate> delegate;
NSOperationQueue * operationQueue;
}
#property(nonatomic, assign) id <WebServiceCallerDelegate> delegate;
-(void)cancelWebserviceCall;
-(void)cancelAllCalls;
#pragma mark - Class Methods
-(void)getListOfAnalyticQuestionsOfUserID:(NSString*)userid;
#end
#pragma mark - Protocol Methods
#protocol WebServiceCallerDelegate <NSObject>
#optional
-(void)getListOfAnalyticQuestionsCompletesSuccessfully:(BOOL)success WithList:(NSMutableArray*)arrQuestions WithMessage:(NSString*)message;
#end
WebServiceCaller.m
When after webservice call result will be sent to calling class.
-(void) getListOfAnalyticQuestionsOfUserID:(NSString*)userid{
NSString *url = [NSString stringWithFormat:#"%#?user_id=%#",[self getURLWithBase:BaseURL relativeURL:#"wt_question.php"],userid];
//webservice call goes here and after completion calls below method
}
-(void)getListOfAnalyticQuestionsCalledSuccessfully:(BOOL)success WithData:(id)Data WithMessage:(NSString*)errorMessage{
NSDictionary *dictResult = (NSDictionary*)Data;
NSMutableArray *arrAnalytics = nil;
NSString *message = errorMessage;
if (success) {
arrAnalytics = [dictResult objectForKey:#"items"];
message = SuccessMessage;
}
else{
if (message.length==0) {
message = NETWORKERRORMESSAGE;
}
}
if(self.delegate!=nil && [(id)[self delegate] respondsToSelector:#selector(getListOfAnalyticQuestionsCompletesSuccessfully:WithList:WithMessage:)])
{
[(id)[self delegate] getListOfAnalyticQuestionsCompletesSuccessfully:success WithList:arrAnalytics WithMessage:message];
}
}
Code snippets from calling class suppose class B, same implementation has been done in class A also from which B has been pushed so every thing is working fine instead of sending control to class B control is sent to class A (webServiceCaller object is created in class A in same manner as in class B and calling same web service, This is just an example same thing happened for other web services too. which is implemented by both pushed and its previous class.)
#import "WebServiceCaller.h"
#define numberOfRecordsPerPage 15
#interface AnalyisViewController ()<UITableViewDataSource,UITableViewDelegate,WebServiceCallerDelegate>
{
NSInteger currentIndex;
NSDictionary *dictQuesList;
NSInteger totalPages;
NSMutableArray *arrQuestionList;
WebServiceCaller *webServiceCaller;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
webServiceCaller = [[WebServiceCaller alloc]init];
webServiceCaller.delegate = self;
[self showQuestions];
}
-(void)showQuestions{
[ProgressHUD show:#"Loading..." Interaction:NO];
[webServiceCaller cancelAllCalls];
[webServiceCaller getListOfAnalyticQuestionsOfUserID:userid];
}
#pragma mark - Webservice delegate
-(void) getListOfAnalyticQuestionsCompletesSuccessfully:(BOOL)success WithList:(NSMutableArray*)arrQuestions WithMessage:(NSString*)message{
//Handles my stuff
}
#end
Any idea of this weird situation.
Based on the documentation of OCMock, it should be possible to test class methods, but I may be misunderstanding what is and isn't possible with OCMock. Take the following example method I wish to test:
- (void)methodToTest {
[SVProgressHUD dismiss];
}
I'd like to test that dismiss is called on SVProgressHUD. I currently use the following test, but that doesn't seem to do the trick.
- (void)testMethodToTest {
// Create Mock Progress HUD
id mockProgressHUD = OCMClassMock([SVProgressHUD class]);
// Configure Mock Progress HUD
OCMStub(ClassMethod([mockProgressHUD dismiss]));
// Invoke Method to Test
[object methodToTest];
OCMVerify([mockProgressHUD dismiss]);
}
Is it possible with OCMock to test whether dismiss is called on SVProgressHUD?
Your test passes for me. Perhaps it's something subtle in your implementation? What is object? Here's my simplified version:
#interface Foo : NSObject
+ (void)dismiss;
#end
#implementation Foo
+ (void)dismiss
{
NSLog(#"Dismiss!");
}
#end
#interface Bar : NSObject
- (void)methodToTest;
#end
#implementation Bar
- (void)methodToTest
{
[Foo dismiss];
}
#end
- (void)testClassMock
{
Bar *bar = [Bar new];
id mockFoo = OCMClassMock([Foo class]);
OCMStub(ClassMethod([mockFoo dismiss]));
[bar methodToTest];
OCMVerify([mockFoo dismiss]);
}
I'm trying to deal with OCMock. I created simple class MyClass.
#interface MyClass : NSObject
- (NSString *)simpleMethod;
#end
#implementation MyClass
- (NSString *)simpleMethod {
[self method];
return #"simple";
}
- (void)method {
NSLog(#"ABC");
}
#end
What I want to check is if method method was invoked when simpleMethod has been called. Now I've got following code but it doesn't work:
- (void)testMethodInvoked
{
id mock = [OCMockObject mockForClass:[MyClass class]];
[[mock stub] simpleMethod];
SEL selector = NSSelectorFromString(#"method");
[[mock expect] methodForSelector:selector];
[mock verify];
}
How should I test this case? I think that is pretty easy to do, but I have no idea how solve this problem.
How to create mock and call method simpleMethod which invoke method method?
Current log:
<unknown>:0: error: -[OCMockTestTests testOne] : OCMockObject[MyClass]: expected method was not invoked: methodForSelector:#selector(method)
You never actually create an object of the class that you want to test. Also, you have to expect first, then invoke the method:
- (void)testMethodInvoked
{
// first create an object that you want to test:
MyClass *object = [[MyClass alloc] init];
// create a partial mock for that object
id mock = [OCMockObject partialMockForObject:object];
// tell the mock object what you expect
[[mock expect] method];
// call the actual method on the mock object
[mock simpleMethod];
// and finally verify
[mock verify];
}
I sometimes find it useful to test "private" methods / implementations -- perhaps don't call it a unit test if that breaks some kind of orthodoxy -- but for a complex implementation I may want to verify behavior on a more granular level than through the external interface.
In any event, I will expose class extension methods by creating a category in the test class:
#interface MyClass (ExposeForTest)
- (void)method;
#end
- (void)testMyClass
{
id mock = [OCMockObject mockForClass:MyClass.class];
[[mock expect] method];
[mock simpleMethod];
}
I have a protocol like this :
#import <Foundation/Foundation.h>
#protocol StoreDisplayerDelegate <NSObject>
-(void) changeActionForObjectWithId:(NSString *)objectID ;
#end
and i have a callass with conforms to the precedent protocol StoreDisplayerDelegate
#interface ShelfVC : UIViewController :<StoreDisplayerDelegate>
....
#implementation ShelfVC
...
- (void)viewDidLoad {
...
DownloadManager *manager = [DownloadManager sharedInstance];
[manager setStoreDisplayerDelegate:self];
....
}
#pragma mark StoreDisplayerDelegate methods
-(void) changeActionForObjectWithId:(NSString *)objectID {
......
}
#end
And in my code ( in the same class) sometimes i am calling the delegate methods to do something form me, for example :
- (void)anOtherMethod{
[self changeActionForObjectWithId:nil];
}
My Questions
1. is : When object is a delegate for an other object, is the methods implemented by the delegate called only by the other object ( witch have a reference for it ) ? i mean by this, for example in the code i have shown should the methode changeActionForObjectWithId: just called by the downLoad manager or can i use it in the inernal of my class like this :
is what i am doing cleaning or bad design of using Delegate pattern ?
I hope that it is clear.
Your delegate method name sounds like a command.
-(void)changeActionForObjectWithId:(NSString *)objectID;
It sounds like your StoreDisplayer is telling delegate to do something. The fact that you are also tempted to call that method from within the ViewController confirms it.
That is not the delegate pattern. The delegate pattern is for a class to inform a delegate of a change, or to ask the delegate for some information. The delegating class (StoreDisplayer?) shouldn't know about what any particular delegate does, so it shouldn't be able to give it direct specific commands. Only delegate generic behaviour to it.
Delegate method look more like these examples:
-(BOOL)actionShouldChangeForStoreDisplayer:(StoreDisplayer*)storeDisplayer;
-(void)actionWillChangeForStoreDisplayer:(StoreDisplayer*)storeDisplayer objectId:(NSString *)objectId;
-(void)actionDidChangeForStoreDisplayer:(StoreDisplayer*)storeDisplayer objectId:(NSString *)objectId;
I'm not saying those are what you need, but they should give you the idea.
When your delegate methods look like this, clearly you will not be tempted to call them from anything other than the class that's doing the delegation (StoreDisplayer).
In my project I have the same situation for different purpose. In my opinion the delegate method must be called only by the other object, because it is its method.
If you need to do certain action in the delegate method, its better to create another private method to performing the action and call it from the delegate methods.
Some example. Instead of doing this:
- (void)anOtherMethod {
[self changeActionForObjectWithId:nil];
}
- (void)changeActionForObjectWithId:(NSString *)objectID {
< some actions >
}
I do this:
- (void)privateMethod{
< some actions >
}
- (void)anOtherMethod {
[self privateMethod];
}
- (void)changeActionForObjectWithId:(NSString *)objectID {
[self privateMethod];
}
Why do this? Because you have to think to the delegate methods to an "extension" to your base object: if you delete the "changeActionForObjectWithId" (becuase, for example, you don't need the delegate anymore after a refactoring) the code will continue to work.
A delegate is a protocol that allows an object to perform certain actions.
In your example:
#interface ShelfVC : UIViewController : <StoreDisplayerDelegate>
This tells the compiler that UIViewController is going to implement some methods in the StoreDisplayerDelegate protocol. How the object that applicable to the StoreDisplayerDelegate behave would depend on the protocol in the delegate methods.
You got a little confused with delegates and protocols. a delegate is a design-pattern wich uses a protocol.
a protocol only defines methods and properties expected to be implemented by another (unspecific) class. from where this methods/properties get accessed doesn't matter.