Monthly Archives: July 2014

Something for a Friday Afternoon

Everyone knows the once-counterintuitive examples using exponentiation. Here’s one:

  • Would you rather have a million dollars, or a penny doubled each day for thirty days?

This has been a yawn for quite a while now, so don’t we need a replacement for it?

Mathematics professor Lillian Lieber included a form of the exponentiation example in one of her books. I can’t remember whether it was The Education of T. C. Mits [The Celebrated Man In The Street] or Mits, Wits, and Logic [Wits = Woman In The Street].

As it happens, she also gave another counterintuitive example.

Here’s how it went:

  • Suppose we wrap a belt around the earth, then let out 10 meters of slack, and distribute the belt evenly around the earth so that its “altitude” is everywhere the same. What is the altitude of the belt?

Okay. To begin with, let c and r be the circumference and radius of the earth, and let C and R be the new, enlarged circumference and radius of the encircling belt.

The altitude we’re trying to find will be the difference of the two radii:

R – r

From geometry, we know that

R = C / 2π
r = c / 2π

So

R – r = C / 2π – c / 2π = (C – c) / 2π

From the beginning, we’ve known the difference of the two circumferences

C – c = 10 meters

So the altitude

C – c = 10 meters / 2π ~= 1.6 meters

I thought it surprising that the altitude would be so large. (I expected it to be tiny.) But what I found really surprising is that we did not need to know the circumference or diameter of the earth!

That means that if we’d wrapped a basketball, or even something the size of the sun, the altitude would still be the same!

Counterintuitive?

I thought so!

Pass the Object, Please

This sign-in method looks reasonable:

public HomePage SignInPage.SignIn(String userName, String password)

It isn’t reasonable.

Why? Because it violates encapsulation: the caller knows (or thinks it knows) what’s needed for signing in.

What if a scanned-in card id is added to the sign-in procedure? Well, that would require the method to be changed.

But actually, that’s none of the caller’s business in the first place.

This is reasonable:

public HomePage SignInPage.SignIn(User user)

Moral: don’t pass multiple data items from an object. Pass the object!

Architectural Notes

I’ve been meaning to post some notes about the architecture I’m implementing for CUITs.

Here goes:

  • Page objects: Each page, popover, and page tab is fully encapsulated using the page object pattern, hiding the HTML on the page, and providing services to the test scripts. It defines its own page-specific locators, and derives other locators via page compositors.
  • Base page: Each page, popover, and page tab derives from a base page that houses the current context and provides locator management.
  • Page compositors: Each page includes common elements, such as menu bar and footer, via compositor classes. Each page tab includes common elements, such as page title and tab navigation, via page compositor classes. This composition simplifies the page objects by avoiding redundant code.
  • GUI tool encapsulator: The GUI tool interface is fully encapsulated by a single class that performs all searches for controls, and all access to controls. This greatly reduces redundant code elsewhere. It also facilitates instrumentation and diagnostics, such as logging the context when an error occurs. Example: when a menu item is not found, the menu items that did appear are logged.
  • Locators: Each control on a page is identified by a locator that lists the attributes needed to find the control. A page object accesses a control by calling a method in the GUI encapsulator, passing it a locator. Example: a page object clicks a button by calling method ClickButton(locator).
  • Log: The test log is an XML file consisting of nested sections. These sections correspond to nested code-blocks in the test, and allow a test to “tell its story” in an organized way: steps, substeps, data, verifications. Each section and subsection has a title and, by default, a timestamp and a duration.

    Each logged verification includes the expected value, the actual value, the verdict (passed/failed), and a message. A summary at the top of the log gives the counts of passed and failed verifications, and notes whether an exception was thrown. Any thrown exception is captured and logged, including its type, message, and stack trace.
  • Verifications: Each verification is performed in a page object, but only at the request of a test script. This eliminates the need for the test script to fetch the actual value, which the page object fetches transparently. The page object also logs the verification.
  • Context: A context object is passed to each page constructor, giving access to the test log, the CUIT TestContext object, and other context information.
  • Test runner: A test runner object manages the test, including opening and closing the browser and test log, catching and logging exceptions, and logging version and other environmental information.
  • Documentation: Documentation is via C# XML documentation, which adds the documentation to the IDE’s IntelliSense. The documentation is also compiled into HTML pages using Doxygen.

    Additional documentation covers such topics as test architecture, project best practices, how to “spy” on the application, and various conventions for the test project. This is also captured in the Doxygen.

Good Things Come to Him Who Waits (Instead of Sleeping)

I avoid putting sleeps into my test code. It’s never certain how much time is needed for the hoped-for state to be reached. And the sleep time has to be the longest time believed necessary, even if a much shorter time often would have served.

Waits are better, but I’ve had trouble with some CUIT wait methods, which sometimes seem not to have the desired effect: a search succeeds, but accessing the found control causes an exception.

Recently, I’ve added to the search code these wait method calls:

String state = UITestControl.PropertyNames.State;
ControlStates nvail = ControlStates.Unavailable;
ControlStates nvisi = ControlStates.Invisible;
foundControl.WaitForControlExist();
foundControl.WaitForControlReady();
foundControl.WaitForControlPropertyNotEqual(state, nvail);
foundControl.WaitForControlPropertyNotEqual(state, nvisi);

So far, they seem to be effective; I have not had a false search failure recently.