iphone - Testing class method using OCMock release 2.1.1 -


i trying check if class method getting invoked using ocmock. have gathered ocmock website , other answers on new ocmock release (2.1) adds support stubbing class methods.

i trying same:

detailviewcontroller:

+(bool)getboolval { return yes; } 

test case:

-(void) testclassmethod { id detailmock = [ocmockobject mockforclass:[detailviewcontroller class]];  [[[detailmock stub] andreturnvalue:ocmock_value((bool){yes})] getboolval:nil]; } 

the test running , succeeding succeeds if return no instead of yes getboolval method in detailviewcontroller. on keeping breakpoint on method, test execution not stop indicating method not called.

how check class method then? please help.

edit

just looked more closely @ latest version of ocmock , think you're correct in interpreting way mock class method you're directly testing, issue you're calling wrong signature?

the answer below general case (so useful when method under test calls out class method).

original

checking class method ocmock little tricky. what doing creating mock object called detailmock , stubbing instance method called getboolval: (by way, method prototype doesn't take argument, shouldn't passing nil -- nit-picky, if want follow apple's guidelines, recommend not using word "get" in getter (unless you're sending pointer reference set)). compilation doesn't fail because detailmock id , willing respond selector.

so how test class method? general case, you'll need swizzling. here how it.

let's @ how we'd fake nsurlconnection should able apply class well.

start extending class:

@interface fakensurlconnection : nsurlconnection  + (id)sharedinstance; + (void)setsharedinstance:(id)sharedinstance; + (void)enablemock:(id)mock; + (void)disablemock;  - (nsurlconnection *)connectionwithrequest:(nsurlrequest *)request delegate:(id<nsurlconnectiondelegate>)delegate; @end 

note i'm interested in testing connectionwithrequest:delegate , i've extended class add public instance method same signature class method. let's @ implementation:

@implementation fakensurlconnection  shared_instance_impl(fakensurlconnection);     swap_methods_impl(nsurlconnection, fakensurlconnection);     disable_mock_impl(fakensurlconnection);     enable_mock_impl(fakensurlconnection);      + (nsurlconnection *)connectionwithrequest:(nsurlrequest *)request delegate:(id<nsurlconnectiondelegate>)delegate {     return [fakensurlconnection.sharedinstance connectionwithrequest:request delegate:delegate]; } - (nsurlconnection *)connectionwithrequest:(nsurlrequest *)request delegate:(id<nsurlconnectiondelegate>)delegate { return nil; } @end 

so what's going on here? first there macros discuss below. next have overridden class method have call instance method. can use ocmock mock instance methods, having class method call instance method, can have class method call mock.

we don't want use fakensurlconnection in our real code though, want use in our testing. how can this? can swizzle class methods between nsurlconnection , fakensurlconnection. means after swizzle call nsurlconnection connectionwithrequest:delegate call fakensurlconnection connectionwithrequest:delegate. brings our macros:

#define swap_methods_impl(real, fake) \ + (void)swapmethods \ { \     method original, mock; \     unsigned int count; \     method *methodlist = class_copymethodlist(object_getclass(real.class), &count); \     (int = 0; < count; i++) \     { \         original = class_getclassmethod(real.class, method_getname(methodlist[i])); \         mock = class_getclassmethod(fake.class, method_getname(methodlist[i])); \         method_exchangeimplementations(original, mock); \     } \     free(methodlist); \ }  #define disable_mock_impl(fake) \ + (void)disablemock \ { \     if (_mockenabled) \     { \         [fake swapmethods]; \         _mockenabled = no; \     } \ }  #define enable_mock_impl(fake) \ static bool _mockenabled = no; \ + (void)enablemock:(id)mockobject; \ { \     if (!_mockenabled) \     { \         [fake setsharedinstance:mockobject]; \         [fake swapmethods]; \         _mockenabled = yes; \     } \     else \     { \         [fake disablemock]; \         [fake enablemock:mockobject]; \     } \ }  #define shared_instance_impl() \ + (id)sharedinstance \ { \     return _sharedinstance; \ }  #define set_shared_instance_impl() \ + (void)setsharedinstance:(id)sharedinstance \ { \     _sharedinstance = sharedinstance; \ } 

i'd recommend don't accidentally re-swizzle class methods. how use this?

id urlconnectionmock = [ocmockobject nicemockforclass:fakensurlconnection.class]; [fakensurlconnection enablemock:urlconnectionmock]; [_mockstodisable addobject:fakensurlconnection.class];  [[[urlconnectionmock expect] andreturn:urlconnectionmock] connectionwithrequest:ocmock_any delegate:ocmock_any]; 

that's pretty -- you've swizzled methods fake class called , call mock.

ah, 1 last thing. _mockstodisable nsmutablearray contain class object every class we've swizzled.

- (void)teardown {     (id mocktodisable in _mockstodisable)     {         [mocktodisable disablemock];     } } 

we in teardown make sure have unswizzled our class after test has run -- don't right in test because if there exception not test code may executed teardown be.

there may other mock technologies make simpler, though i've found it's not bad since write once , can use many times.


Comments

Popular posts from this blog

linux - xterm copying to CLIPBOARD using copy-selection causes automatic updating of CLIPBOARD upon mouse selection -

c++ - qgraphicsview horizontal scrolling always has a vertical delta -