Using OCUnit to Check for UIAlertView

I am working on an application that displays a UIAlertView after pressing the exit button only if progress in the game is completed. I was wondering how you will use OCUnit to intercept the UIAlertView and interact with it or even detect if it has been presented. The only thing I can think of is monkeypatch [UIAlertViewDelegate willPresentAlertView] , but it makes me want to cry.

Does anyone know a better way to do this?

+6
source share
4 answers

Update: see my blog. How Unit Test your alerts and action tables

The problem with my other answer is that the -showAlertWithMessage: method -showAlertWithMessage: never implemented by unit tests. "Using manual testing to test it once" is not so bad for simple scenarios, but error handling is often associated with unusual situations that are difficult to reproduce .... Also, I got such a nagging feeling that I stopped, and what could be a more thorough way. Exist.

In the tested class, do not instantiate the UIAlertView directly. Define a method instead

 + (Class)alertViewClass { return [UIAlertView class]; } 

which can be replaced using "subclass and override". (Alternatively, use dependency injection and pass this class as an initializer argument.)

Call this to define the class to instantiate to show a warning:

 Class alertViewClass = [[self class] alertViewClass]; id alert = [[alertViewClass alloc] initWithTitle:...etc... 

Now define the layout display class. His task is to remember his initializer arguments and publish a notification, passing himself as an object:

 - (void)show { [[NSNotificationCenter defaultCenter] postNotificationName:MockAlertViewShowNotification object:self userInfo:nil]; } 

In the testing subclass (TestingFoo) +alertViewClass overridden to replace the layout:

 + (Class)alertViewClass { return [MockAlertView class]; } 

Make your test class registered for notification. The called method can now check the arguments passed to the alert initializer, and the number of -show messages -show been sent.

Additional hint: in addition to the layout alert, I defined an alert validation class that:

  • Registers for notification
  • Lets me set the expected values
  • After notification, it checks the status for the expected values.

So, all my test tests now create a verifier, set expectations and make a call.

+4
source

The latest version of OCMock (2.2.1 at the time of this writing) has features that make it easier. Here is a sample test code that chokes the UIAlertView "alloc" class method to return a mock object instead of a real UIAlertView.

 id mockAlertView = [OCMockObject mockForClass:[UIAlertView class]]; [[[mockAlertView stub] andReturn:mockAlertView] alloc]; (void)[[[mockAlertView expect] andReturn:mockAlertView] initWithTitle:OCMOCK_ANY message:OCMOCK_ANY delegate:OCMOCK_ANY cancelButtonTitle:OCMOCK_ANY otherButtonTitles:OCMOCK_ANY, nil]; [[mockAlertView expect] show]; [myViewController doSomething]; [mockAlertView verify]; 
+4
source

Note: see my other answer. I recommend it on this one.

In the current class, define a short method to display a warning, for example:

 - (void)showAlertWithMessage:(NSString message *)message { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:message delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; [alert release]; } 

For your test, do not check this actual method. Instead, use "subclass and override" to define a spy who simply writes down his calls and arguments. Let's say the original class is called "Foo". Here's a subclass for testing purposes:

 @interface TestingFoo : Foo @property(nonatomic, assign) NSUInteger countShowAlert; @property(nonatomic, retain) NSString *lastShowAlertMessage; @end @implementation TestingFoo @synthesize countShowAlert; @synthesize lastShowAlertMessage; - (void)dealloc { [lastShowAlertMessage release]; [super dealloc]; } - (void)showAlertWithMessage:(NSString message *)message { ++countShowAlert; [self setLastShowAlertMessage:message]; } @end 

Now bye

  • your code calls -showAlertWithMessage: instead of directly alerting and
  • your test code creates TestingFoo instead of Foo ,

You can check the number of calls to display a warning and the last message.

Since this does not use the actual code that shows the warning, use manual testing to test it once.

+3
source

You can get unit tests to view alerts pretty easily by exchanging the "show" UIAlertView implementation. For example, this interface gives you several testing options:

 @interface UIAlertView (Testing) + (void)skipNext; + (BOOL)didSkip; @end 

with this implementation

 #import <objc/runtime.h> @implementation UIAlertView (Testing) static BOOL skip = NO; + (id)alloc { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Method showMethod = class_getInstanceMethod(self, @selector(show)); Method show_Method = class_getInstanceMethod(self, @selector(show_)); method_exchangeImplementations(showMethod, show_Method); }); return [super alloc]; } + (void)skipNext { skip = YES; } + (BOOL)didSkip { return !skip; } - (void)show_ { NSLog(@"UIAlertView :: would appear here (%@) [ title = %@; message = %@ ]", skip ? @"predicted" : @"unexpected", [self title], [self message]); if (skip) { skip = NO; return; } } @end 

You can write unit tests, for example. eg:

 [UIAlertView skipNext]; // do something that you expect will give an alert STAssertTrue([UIAlertView didSkip], @"Alert view did not appear as expected"); 

If you want to automate pressing a certain button in alert mode, you will need more magic. The interface receives two new class methods:

 @interface UIAlertView (Testing) + (void)skipNext; + (BOOL)didSkip; + (void)tapNext:(NSString *)buttonTitle; + (BOOL)didTap; @end 

which go as follows

 static NSString *next = nil; + (void)tapNext:(NSString *)buttonTitle { [next release]; next = [buttonTitle retain]; } + (BOOL)didTap { BOOL result = !next; [next release]; next = nil; return result; } 

and the show method becomes

 - (void)show_ { if (next) { NSLog(@"UIAlertView :: simulating alert for tapping %@", next); for (NSInteger i = 0; i < [self numberOfButtons]; i++) if ([next isEqualToString:[self buttonTitleAtIndex:i]]) { [next release]; next = nil; [self alertView:self clickedButtonAtIndex:i]; return; } return; } NSLog(@"UIAlertView :: would appear here (%@) [ title = %@; message = %@ ]", skip ? @"predicted" : @"unexpected", [self title], [self message]); if (skip) { skip = NO; return; } } 

You can test it the same way, but instead of skipNext you would say which button to click. For instance.

 [UIAlertView tapNext:@"Download"]; // do stuff that triggers an alert view with a "Download" button among others STAssertTrue([UIAlertView didTap], @"Download was never tappable or never tapped"); 
0
source

Source: https://habr.com/ru/post/888741/


All Articles