5

I'm writing unit tests to find if elements exist on a certain nib. For example, I want to loop through a nibs views and check if one exists with a referencing outlet 'commentTextView', to check if that text view exists.

Right now, I only see methods to check if a target exists (like checking if a button will call a certain selector when it is clicked), but not to check what I need.

Logan Serman
  • 29,447
  • 27
  • 102
  • 141
  • “Referencing outlets” are listed by Xcode on the object that they reference. If you have the text view with the intent to determine whether it is referenced by a certain outlet, then you already know the text view exists. – Peter Hosey Aug 31 '12 at 20:06
  • @PeterHosey let's say I have a view with 3 buttons. I want to check if there are buttons for Login, Register, and Help. Checking for the existence of a UIButton isn't helpful. I would argue that checking that there are 3 buttons is also useless. What I would like to do is check if there are existing buttons that are linked to loginButton, registerButton, and helpButton. This would verify that there are 3 buttons, and they do what they are supposed to do (trusting that those references do what they are supposed to in the code). – Logan Serman Sep 01 '12 at 16:17
  • 1
    Presumably your outlets are connected to "files owner", or some root object in the nib? Can you get that object, iterate through the outlets you're trying to test, and verify their existence, uniqueness, and other properties? – Carl Veazey Sep 01 '12 at 20:35
  • Hey, were you ever able to find any solution or framework for this? – Carl Veazey Sep 06 '12 at 22:41
  • 1
    Nope. I created a category on UIView that checks existence of certain elements by their properties (target/action for button, text for labels, etc). So if I want to check the existence of the 'Hello' label in a view, I do something like `[self.view hasLabelWithText: @"Hello"];`. Not the best solution, but that is what I'm using for now. – Logan Serman Sep 07 '12 at 14:15

2 Answers2

0

Instead of checking to see if they have an outlet, assign them all a unique tag. Select the buttons in Interface Builder and go to the Attributes Inspector on the right. Towards the bottom should be a box to set the object's tag property. Then, while you step through your nib's views you can check each one's tag and use that to determine what view it is.

Metabble
  • 11,773
  • 1
  • 16
  • 29
0

I'm making a couple of assumptions here, so if I'm base, let me know.

1) You have a list of objects which will have outlets connected, and a list of those outlets. (e.g., File's owner is a MyViewController class and has outlets view, label, button, and so on; there is a UITableView with outlets delegate, and dataSource, etc.)

2) Your nibs are designed to make it practical to find all of the objects in 1. For example, if some UIControl isn't referenced by a top-level object or a proxy object, it has been given a tag value to make it easy to find with viewWithTag:

Assuming these are true, then you could test that a nib gets loaded by basically doing the following (in pseudo code)

for each referencingObject in nibObjects
{
    for each outletName in referencingObject.outletNames
    {
        assertExistence(/* is an object referenced by this outlet? */)
        assertProperties(/* does the object conform to the properties expected for this referencing object / outlet pairing? */)
    }
}

I started making a stab at an implementation of this. Since iOS nibs are based heavily on key-value coding I think there is a lot of potential to be explored in testing nibs, for what its worth. I didn't get into handling sent actions from objects in the nib as I have to get off SO and study, but I'll share what I did so far.

Here's the test method code I wrote in my SenTestCase subclass:

ViewController *vc = [[ViewController alloc] init];
UINib *nib1 = [UINib nibWithNibName:@"ViewController1" bundle:nil];
NSArray *topLevelObjects = [nib1 instantiateWithOwner:vc options:nil];

ReferencingObject *filesOwnerReferencingObject = [[ReferencingObject alloc] init];
filesOwnerReferencingObject.object = vc;

//Make a referenced object outlet for the view
ReferencedOutlet *viewOutlet = [[ReferencedOutlet alloc] init];
viewOutlet.name = @"view";
viewOutlet.propertyAssertionBlock = ^(id object) {
    UIView *theView = (UIView *)object;
    STAssertEquals(1.0f, theView.alpha, @"shouldn't have any transparency");
};

//Make a referenced object outlet for the label
ReferencedOutlet *labelOutlet = [[ReferencedOutlet alloc] init];
labelOutlet.name = @"label";
labelOutlet.propertyAssertionBlock = ^(id object) {
    UILabel *theLabel = (UILabel *)object;
    NSString *expectedLabelText = @"ViewController1.xib";
    STAssertTrue([expectedLabelText isEqualToString:theLabel.text], nil);

};

filesOwnerReferencingObject.outlets = @[ viewOutlet, labelOutlet ];


NSArray *referencingObjects = @[ filesOwnerReferencingObject ];
for (ReferencingObject *referencingObject in referencingObjects)
{
    for (ReferencedOutlet *outlet in referencingObject.outlets)
    {
        id object = [filesOwnerReferencingObject.object valueForKey:outlet.name];
        STAssertNotNil(object, nil);
        outlet.propertyAssertionBlock(object);
    }
}

And here is my interface / implementation of the ReferencingObject and ReferencedOutlet classes.

@interface ReferencingObject : NSObject

@property (nonatomic, strong) id object;
@property (nonatomic, strong) NSArray *outlets;

@end

@implementation ReferencingObject
@end

typedef void (^ReferencedOutletPropertyAssertionBlock)(id);

@interface ReferencedOutlet : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) ReferencedOutletPropertyAssertionBlock propertyAssertionBlock;

@end

@implementation ReferencedOutlet
@end

Hopefully this answer will be help to you or someone else. Let me know if you have any questions.

Carl Veazey
  • 18,392
  • 8
  • 66
  • 81