Social Media
GitHub
Navigation
Powered by Squarespace

Entries in Testing (5)

Monday
Sep132010

What a Difference 2 Years Makes - a Study in Contrasts With iPhone Ad-Hoc App Distribution

The first iPhone App that I worked on was submitted in October of 2008. The month before we submitted, there was a flurry of emails between me and my customer, a sample of which are reproduced below:

 

Begin forwarded message:
From: Customer
Date: September 16, 2008 12:58:19 PM CDT
To: Carl Brown 
Subject: Re: iPhone project status

 

My Identifier is XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Emails omitted for brevity.

Begin forwarded message:
From: Customer
Date: September 23, 2008 1:42:02 PM CDT
To: Carl Brown 
Subject: Re: iPhone project status

 

Hi,

Even with the Ad-Hoc Distribution file dragged into iTunes, I get a message that my iPod isn't authorized to have the App installed:

More emails omitted for brevity.

Begin forwarded message:
From: Customer
Date: September 23, 2008 2:12:04 PM CDT
To: Carl Brown
Subject: Re: iPhone project status

 

I don't see Profiles on my General Settings screen. I've restarted the iPod and re-synced, both with and without the app in my iTunes applications list.

I'm trying so hard!

Any advice?


I could go on (and on and on), but you get (I hope) the idea.

 

This evening, I wrapped up the first beta of a new customer app, and had this exchange with my new customer (no emails omitted this time):

Begin forwarded message:
From: Customer
Date: September 13, 2010 9:53:31 PM CDT
To: "'Carl Brown'"
Subject: RE: app design

 

Ok – how about this number…
 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXX

 

Begin forwarded message:
From: Carl Brown
Date: September 13, 2010 11:19:06 PM CDT
To: Customer
Subject: App install

 

OK,

Click on this link on your iPhone and let me know if it works for you:

http://www.pdagent.com/XXXXXXXXX/XXXXXXXXXXXXX/

-Carl

 

Begin forwarded message:
From: Customer
Date: September 13, 2010 11:47:39 PM CDT
To: Carl Brown
Subject: Re: App install

 

I got it. Let me play around. Looks good so far.

 

What was the difference? Two things. The first was a change in Xcode:

Xcode 3.2.3 has a number of features, enhancements, and bug fixes over Xcode 3.2.2:
iPhone OS Development:
· When developing generic applications (applications that don’t require special features, such as push notifications or in-app purchases), you can create, download, and install provisioning profiles and signing certificates in the Xcode Organizer, without having to directly log in to the iPhone Provisioning Portal.


which is wonderful and saves a ton of wasted time, and the second was Beta Builder - which is a tremendous, awesome free OSX app. You give it your archive that Xcode's "Build and Archive" spits out, and it gives you a directory of static files to go put on your web server. You upload them, send your customer a URL to click on, and you're done.

 

Once more, that means:
September of 2008: literally a 9 day, 14 email, 2 phone call ordeal in with a customer who is technical enough to work with HTML and Photoshop for a living.
September of 2010: 3 emails in less than 2 hours for a customer who is a fire fighter by profession.

Thank you Apple. And thank you Hunter Hillegas.

 

Monday
Sep132010

The Rules - At Least As I See Them (Well, the First Two)

Since I've been dealing with computers, I've developed some rules of thumb.  The first rule seems obvious, although I'm constantly surprised by the people that break it.  It is:

Rule 1: Never run a command on a computer that affects the communications path through which you are connected to that machine.

This is slightly more complicated than it sounds - especially when configuring routing protocols in routers.  You change things such that you lose your routes from where you are to that machine, and it's time for Plan B.  However, for the most part, it's straightforward, which is why it became the first rule.

The second is less obvious, and more controversial, although it's potentially more important.  It is:

Rule 2: System add-ons included solely for the purpose of reducing downtime by means of failover or redundancy will cause more downtime due to bugs or misconfigurations than would have been caused by hardware failures if those add-ons had not been included, unless the level of diligence and effort is greatly increased.

Let's take that a piece at a time.  There are a lot of pieces of tech in the world that people use to protect against hardware failures, like SANs and clusters.  That function, protecting against hardware failures, is inherently complicated, and that means that those pieces of tech have to be inherently complicated.  And Murphy's Law (and experience) tell us that the more things there are that could go wrong, the more likely something will.  In fact, I contend that, unless you go to extraordinary efforts to test every last possible thing that can go wrong, problems with those many complicated bits will cause more problems than would have been caused if you'd just left them off.

I've seen a lot of network outages in my time that were caused by routers that got confused and sent out (or listened to) the wrong routes.  Network engineers have many names for this phenomenon - my favorite is "flapping", and it's a very common happening.  I have seen many fewer network outages caused by router hardware that just dies - and most of those have been routers that spent time in places with very dirty electrical power.  Now of course, I have seen networks that lose routers without any hiccups at all, but those are generally the networks that require "pull tests" (where you unplug routers and make sure things fail over as you expect) after every non-trivial configuration change and periodically on a regular basis.

Likewise, I've dealt a lot lately with a network that has regular issues due to "automatic spanning-tree reconfigurations" and a database cluster that blue-screens when the underlying SAN hiccups.

Think about it for a second - there are many different ways that a system can go wrong - many different pieces that can fail in different ways.  What are the odds that the code that is supposed to deal with that specific failure is going to behave exactly as you want it to the very first time that section of code is executed in your environment?

I'm not saying "never use any High-Availability add-on", I'm saying "if you use an High-Availability add-on, either spend far more effort configuring and testing it than you would spend on the non-HA version, or expect it to cause you more problems than you would have had if you'd gone with the non-HA version."

It's okay if you don't believe me.  A lot of vendors have spent a lot of money trying to get you to believe that it isn't true.  But think about it, and start paying more attention to what's causing your enterprise more problems.  After that, I think it will become clear.

Tuesday
Sep072010

A Tale of Two Table Views - my UISearchBar Race Condition that I finally found

OK, so I finally found my race condition, I'd talked about here and here.

So, in my KidChart app, I have a UITableView that has a list of all behaviors that people can pick from:

 

 

and in the search box above, people can start typing to narrow down existing behaviors and then click on one so they don't have to scroll as much. As soon as the UISearchBar gets focus, it does this:

 

Now that tableview that has the 3 items that contain "o" is a different tableview than the one above. It's the tableview that belongs to the UISearchBar, as opposed to the one in my view.

Turns out, though that under some circumstances, core data events get triggered. And those events cause the UITableView to get refreshed. Shouldn't have been the end of the world, except it causes *Both* UITableView to get refreshed, both the filtered one that belongs to the UISearchBar, and the underlying, currently hidden one, that was there before the UISearchBar got focus. And the mistake I made was that I was reusing the same NSFetchedResultsController behind the scenes for both UITableViews (since I naively expected only one to be updated at a time). Most of the time, it seems one would get completely refreshed before the other refresh started, but sometimes (and more often on the iPhone 4), the calls to cellForRowAtIndexPath would get interleaved, causing unpredictable results, and the occasional crash.

Thank you, automated testing.

(And thank you Mike Uricaru who suggested a way for me to be able to use UIASearchBar.setValue() in the UIAutomation Javascript - without the race condition, it does work, and it's much faster to run than taping each key by hand - although I still use the keystroke at a time to test that I've gotten the filtering behavior correctly).

Monday
Sep062010

UI Automation App Input

So, I've been doing more UI Automation test work, and I've discovered a couple of things. I'm kind of trying to write them up as I run into them, although I'm putting together a helper library that I'll announce at some point, hopefully soon.

So, what I'm trying to work on is a race condition in my KidChart app. The issue (I think) has to do with notifications during input into an UISearchBar.

So, I got to the right screen (more on that later), and tried searchBar.setValue("Ate 2 Vegetables!"), but it turns out that if you run ".setValue()", none of the delegate events fire, so I had to use the Keyboard.

I didn't expect using the keyboard to be nearly as hard as it turned out to be. I can't figure out an intuitive way to change keyboard modes (I expected there to be a key called something like "Shift" that you could hit to get capital letters. No such luck), so I ended up having to do it the hard way. This is what I ended up with to type in the string in setValue() above:

 

1 var searchbar = view.searchBars()[0];
2 searchbar.tap();
3
4 if (searchbar.hasKeyboardFocus()) {
5 UIATarget.localTarget().delay(1);
6
7 UIATarget.localTarget().tap({x:20,y:400}); //Hit Shift for Capital Letters
8 UIATarget.localTarget().delay(1);
9
10 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("A").tap();
11 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("t").tap();
12 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("e").tap();
13 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("space").
14 tap();
15 UIATarget.localTarget().tap({x:20,y:460}); //Select Number Keyboard
16 UIATarget.localTarget().delay(1);
17 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("2").tap();
18 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("space").
19 tap();
20 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("v").tap();
21 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("e").tap();
22 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("g").tap();
23 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("e").tap();
24 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("t").tap();
25 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("a").tap();
26 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("b").tap();
27 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("l").tap();
28 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("e").tap();
29 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("s").tap();
30
31 UIATarget.localTarget().tap({x:20,y:460}); //Select Number Keyboard
32 UIATarget.localTarget().delay(1);
33
34 UIATarget.localTarget().tap({x:20,y:400}); //Now Select Symbol Keyboard
35 UIATarget.localTarget().delay(1);
36
37 UIATarget.localTarget().frontMostApp().keyboard().keys().firstWithName("!").tap();
38
39 } else {
40 UIALogger.logFail("Could not activate the Search Bar");
41 }

 

That's all going into a library - so my tests won't actually look like that, but I needed to do it by hand once to figure out what to put in the library. And I figured it was the best thing to throw into the blog here to illustrate the technique - so you don't have to figure it out for yourselves (and I don't have to figure it out again).

Happy Testing.

 

Friday
Sep032010

Apple iOS iPhone UI Automation Testing: What does Accessibility have to do with it?

Trying to do some UI Automation testing going on one of my Apps today. Have a race condition, so I want to have a script to run it over and over again to have a better chance of catching the problem (more on that in a later post).

So, I just wasted 2 hours trying to test this structure:

And the problem was that I had this set in Interface Builder:

(Emphasis mine).

Now, this page says that container views shouldn't be set as Accessible. What it DOESN'T say (unless I missed it) is that IF the container view IS Accessible, then NONE of the subViews will be checked for Accessibility AT ALL. In essence, any Accessible object is a leaf as far as Accessibility is concerned. So even if you have:

set, which is correct, your TextField is UNTESTABLE. The UI Automation scripts can't find it at all. As soon as you do:

in the parent UIView, then it all works.

You've been warned. Don't be like me.

I have this feeling that I'll be running into more fun tips and tricks by the time I get this all working. I'll post them here as they find me. Wish me Luck.