I'd like to run some tests on a view controller involving CLBeacons. Unfortunately, while CLBeacons are not themselves a private class, all the necessary properties on them are read-only, without any write accessors.
The methods are written in a manner that should help maximize testability, but without the ability to generate the CLBeacons, how do I test my view controller's logic regarding them?
Edit:
To explain the scope of my purpose, I'm experiencing some odd behavior in the app at times. I want to try and confirm the source of the odd behavior is in a specific area of code by testing various scenarios and confirming that various other related components in my code work correctly. (E. g. I can test that my data handling works correctly, therefore I'll know it's the animation / layout code that is misbehaving).
I have done this by using OCMockito with XCTest.
CLBeacon *mockBeacon = mock([CLBeacon class]);
Then I can use this to call the delegate methods on the class that is the CoreLocation delegate. The test might look like this:
- (void)testDidRangeOnABeacon
{
MyLocationDelegate *myDelegate = [[MyLocationDelegate alloc] init];
CLLocationManager *mockManager = mock([CLLocationManager class]);
CLBeacon *mockBeacon = mock([CLBeacon class]);
CLBeaconRegion *mockRegion = mock([CLBeaconRegion class]);
[myDelegate locationManager:mockManager
didRangeBeacons:#[mockBeacon]
inRegion:mockRegion];
// XCTAsserts...
XCTAssert([myDelegate.checkSomethingAboutRanging]);
}
Since you cannot create CLBeacon instances directly, maybe you could refactor your methods to take as parameter a CustomBeacon, defined like this:
#interface CustomBeacon : NSObject
#property (nonatomic, strong) NSNumber *major;
#property (nonatomic, strong) NSNumber *minor;
#property (nonatomic, assign) CLProximity proximity;
// plus all other fields in CLBeacon..
+ (instancetype) customBeaconWithBeacon:(CLBeacon *) beacon;
- (instancetype) initWithBeacon:(CLBeacon *) beacon;
#end
Then you can simply use [[CustomBeacon customBeaconWithBeacon:realBeacon] instead of realBeacon when you are dealing with CLBeacon (after monitoring), and in your test you can instantiate your CustomBeacon instances directly.
Not as clean as using CLBeacon, but this is the best I could think of regarding testability.
Related
Let's say I have a class:
#interface MyClass
#end
#implementation MyClass {
myType *_myIvar;
}
#end
And I'd like to expose it for testing. I see two ways to do this:
expose the ivar as a property:
#interface MyClass
// Pragma mark - Expose for testing
#property myIvar;
#end
#implementation MyClass
#end
Use key value coding:
-(void)myTest {
myType *myIvar = [myClass valueForKey:#"_myIvar"];
}
Which method is preferred?
First - you don't test private methods or state of a class for unit-testing, as per TDD best practices.
Having that said, however, sometimes it's the only way to observer possible side-effects. I personally always wrap any ivar of a class with a property. For data which is not supposed to appear in the public interface I put it in the extension inside of the implementation file:
// Implementation file
#interface TDWClass ()
#property (strong, nullable) NSString *tdw_p_message;
#end
P.S. This also helps to maintain certain semantic for the given property (you immediately can see not just storage modifier, but other attributes the property is supposed to follow: like, being read-only, nullability, etc..)
When it comes to testing such a property, this approach helps to conveniently read any "private" property (or accessing a private method) by re-declaring it in a category:
// XCTest file
#interface TDWClass (XCTest)
#property (strong, nullable) NSString *tdw_p_message;
#end
Simple question, I would just like to hear what other people do, and what best practice might be. To clear some things up.
When declaring an instance variable in one of my view controllers. Where is the proper place to declare it? I want the instance variable to available to all methods/function in the view controller
I've seen it done this way, in the .h file in curly braces after the #interface:
#interface ViewController : UIViewController {
NSString *test3;
}
I've seen it done this way, in the .m file in curly braces after the #interface:
#interface ViewController () {
NSString *test1;
}
And I've seen it done this way, in the .m file in curly braces after the #implementation:
#implementation ViewController {
NSString *test2;
}
What is the best practice? Is there any difference? If so what may they be, and what makes one way better than another?
All help will be appreciated,
Thanks.
Any of those will work, but current best practice is to use a property. If you want it to be accessible only inside your class:
#interface ViewController ()
#property (copy, nonatomic) NSString *test1;
#end
An access it like this:
self.test1 = #"Hello, world";
But, if you want it to be accessible to other classes as well, put it in the public interface:
#interface ViewController : UIViewController
#property (copy, nonatomic) NSString *test1;
#end
And then access it like this:
someInstanceOfViewController.test1 = #"Hello, world";
If you need to access the instance variable directly (and this applies only inside the class in most cases), for example if you are making a custom setter, the compiler will auto-synthesize an ivar that is the name of your property prefixed with an underscore:
- (void)setTest1:(NSString *)test1
{
_test1 = [test1 copy];
}
Note: the copy is because you might set test1 to an instance of NSMutableString, and you probably don’t want its contents getting mutated out from under you. When you override the setter, as above, you have to enforce this copy semantic yourself.
In the stanford course Paul Hegarty prefers to use lazy instantiation. For instance he makes a private declaration of
#property (strong, nonatomic) (NSArray *)cards
and then he uses the getter to perform an initialization
- (NSArray *) cards
{
if(!_cards) _cards = [[NSArray alloc]init]
return _cards;
}
I'm cool with that. What I don't get though is that at another time Paul declares a public suit for a playingCard being:
#property (strong, nonatomic) NSString *suit;
but in the implementation he doesn't perform this lazy instantiation. So I don't understand where the alloc init of the suit string happens? (suit being a pointer to an NSString - object which ought to get a place in the heap)
#import "PlayingCard.h"
#implementation PlayingCard
#synthesize suit = _suit;
- (void)setSuit:(NSString *)suit
{
if ([#[#"♣︎", #"♠︎", #"♥︎", #"♦︎"]containsObject: suit]) {
_suit = suit;
}
}
- (NSString *)suit
{
return _suit? _suit: #"?";
}
#end
The property is public, so he assumes that it will be set somewhere. When you set this property you can alloc, init and then set it to Playing card instance, for example:
PlayingCard *playingCard = [PlayingCard new];
[playingCard setSuit:#"spade"];
Lazy initialisation is used if property is private (so you can not initialise it outside of the class), but you don't want to initialise it in init method of the class.
When you ask for the cards instance there is no additional information required (i.e. there are no necessary parameters). You just instantiate a PlayingCard and return it.
A suit, on the other hand, could be one of four options, so somebody needs to set that somewhere. Note that this issue is really independent of lazy initialization. It has more to do with the fact that suit expects to be initialized with a user-parameterized value.
Lazy initialization is a way to say, "don't bother creating an instance of this object until I ask for it." But in the case of suit, you don't want to create the string until the user supplies it.
Lazy instantiation is not a panacea but can improve the object instantiation and app responsiveness if you don't spend cycles instantiating ivar objects all at once before you need them. This effect is nothing on a small simple class, but if you have a large set or array of objects, setting them all up completely at once will slow down things at one point.
To be fair the same hit could come later.
Looking at the Apple example application for MultipeerGroupChat (specifically MainViewController.m):
https://developer.apple.com/library/ios/samplecode/MultipeerGroupChat/Listings/AdhocGroupChat_MainViewController_m.html#//apple_ref/doc/uid/DTS40013691-AdhocGroupChat_MainViewController_m-DontLinkElementID_8
The example contains the below code assigning properties:
#property (retain, nonatomic) NSMutableArray *transcripts;
#property (retain, nonatomic) NSMutableDictionary *imageNameIndex;
and then initializing them in viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
// Init transcripts array to use as table view data source
_transcripts = [NSMutableArray new];
_imageNameIndex = [NSMutableDictionary new];
---SNIP---
Is there a reason they are assigning directly to the ivars _transcripts and _imageNameIndex? I thought proper convention dictates that you always use the properties to access the variables unless you are in an init method...indeed, a little further in the viewDidLoad method the author does use properties to assign other variables:
self.displayName = [defaults objectForKey:kNSDefaultDisplayName];
self.serviceType = [defaults objectForKey:kNSDefaultServiceType];
I know Apple example code has, in the past, sometimes strayed from good coding practices, so I'm wondering if this is just sloppy coding or if there is a valid reason to access those two ivars directly in a non-initialization method.
There is no definitive way for how properties have to be accessed. Seeing as this is just a demo project, the engineer who wrote this probably knew the scope of the project allowed for some shortcuts.
What's been done here isn't wrong, it just isn't recommended.
Let's consider an application with highly customized or complex views.
We'll have a specific kind of view-controller sending methods to a specific kind of UIView, where the UIView is itself composed of a number of other views.
The view should have a rich, domain-specific interface, allowing the controller to act is a thin "glue" layer between it and a similarly rich model.
So we override our controller's view property as follows:
#interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
NSMutableArray* _sections;
LightingMode _lightingMode;
}
#property (nonatomic, strong) PlaybackView* view; // <------ Specific type of view
#pragma mark - injected
#property (nonatomic, strong) id<OscClient> oscClient;
#property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;
#end
Ovverriding makes sense over defining another accessor, and I can just send messages to the specific type of UIView without having to cast.
Problem: The only problem is that it results in a compiler warning:
property type 'PlaybackView *' is incompatible with type 'UIView *' inherited from 'UIViewController'
. . and I like to build code that doesn't have any warnings. This way a valid warning doesn't get missed by being buried amongst other warnings.
Question:
Is there a way to suppress this particular warning?
Why is this part of the default settings, when most modern OO languages will happily allow overriding a property or method in a sub-class so that it returns a more specific sub-class of the type declared in the super-class?
The problem here is not not the override of the property, its using a forward declaration of the class type.
So this...
#class PlaybackView;
#interface PlaybackViewController : UIViewController
#property (nonatomic, strong) PlaybackView* view;
#end
will give you the mentioned warning because the compiler cannot know the inheritance hierarchy of PlaybackView. UIViewController has a contract to provide a UIView from its view property
Its telling you that it thinks PlaybackView is not a UIView
The simple solution here is to use a #import instead to give the compiler full knowledge of PlaybackView...
#import "PlaybackView.h"
#interface PlaybackViewController : UIViewController
#property (nonatomic, strong) PlaybackView* view;
#end
alternatively (but really bad form as the PCH is an optimising feature and shouldn't manage dependancies ) is to add #import "PlaybackView.h" to your projects PCH
As suggested in another answer using #import instead of #class will clear the warning but it is advised to import as little as possible in the header, so I would recommend leaving the view unchanged and having an additional PlaybackView * playbackView:
It is perfectly fine to have both view and playbackView pointing to the same view.
Classes that need to have knowledge of your specialized view have to import your controllers header, so they could just use playbackView in the first place.
More important, if you want to embed your specialized view as a subview in the future (which happens often like adding a UIScrollView superview), you won't have to refactor other code and classes!
It's plain cleaner.
I do not think override UIViewControllers view property is a good way .
I think it is better to do like this :
#interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
NSMutableArray* _sections;
LightingMode _lightingMode;
}
//#property (nonatomic, strong) PlaybackView* view; //you do not need this property
#pragma mark - injected
#property (nonatomic, strong) id<OscClient> oscClient;
#property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;
#end
and in the .m file .
- (void)loadView
{
PlaybackView *mainView = [[PlaybackView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
// set the mainView
self.view = mainView;
}
and you can use your PlaybackView like this .
((PlaybackView *)(self.view)).oscClient
or
((PlaybackView *)(xxxViewControler.view)).oscClient
Perhaps you could declare another method that provides the cast for you, in a sense.
#implementation PlaybackViewController
- (void)viewDidLoad {
[super viewDidLoad];
// use view_ property instead of view
self.view_.foo = 1;
}
- (void)loadView {
CGRect frame = [UIScreen mainScreen].applicationFrame;
self.view = [[PlaybackView alloc] initWithFrame:frame];
}
- (PlaybackView *)view_ {
return (PlaybackView *)self.view;
}
Not exactly the cleanest approach, but it does avoid the cast on self.view (by not using self.view, though)
[UPDATE]
Finally I've probably found a solution that suit the problem:
This is a quick and dirty just to suppress the warning, try to wrap your code between these lines
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//YOUR CODE
#pragma clang diagnostic pop
or -Wall
To see more about compiler warning suppression Clang Manual
[OLD ANSWER]
I'd like to give my 2 cents.
If I understood well you are trying to create a sort of Abstract factory, that gives you a specialized version of a view based on the view controller funcionality. In my opinion storyboards doesn't work well in that kind of design, but I'd like to give you my vision about it.
First I will create an abstract class for your view controller where in the interface you declare all property you need in all your VC sublcasses, such as :
OSClient
AbstractStageLayoutView
PlaybackView as weak
playbackProperty
The PlaybackView class is a class cluster such as NSNumber, you call a factory method on it, that will return an object that could be different from case to case. If you inspect an NSnumber it returns a different object if you create a float or an integer, but they are all subclasses of NSNumber, and NSNumber declares all the property of its subclasses, but it doesn't implement them.
Now what you can do in the -viewDidLoad method of the abstract class is call a method like that
PlaybackView *plbackView = [PlaybackView playbackViewFroProperty:self.playbackProperty];
[self.view addSubview:playbackView];
self.playbackView = plbackView;
The playbackProperty can be valued inside the User defined runtime attibute in the viewcontroller storyboard editor.