Tuesday, August 28, 2012

How do you read a book?

This isn't a rhetorical question. I'm interested in knowing how people read books. I have a feeling that reading is more idiosyncratic that you'd expect.

The more I think about it the more I think that I'm not reading properly. The reason I say this is that for all that my life has changed I still read more or less the way I did when I was a kid or a teenager, picking up a book, reading a few pages, putting it down again, all this several times a day, until about 1 - 4 weeks later I reach the end. Is that the best way to do it? Shouldn't I have a few tricks up my sleeve at this stage, some sophisticated reading techniques?

It's such a fundamental activity, reading. Look, you're doing it now. Such a personal activity, one that surely should be constantly improving as time passes. You will have changed most of your other habits from when you first started reading for pleasure, indeed your life will be barely recognisable from that time. I don't watch TV the way I used to, strumming idly away on the guitar. I don't listen to music the same way I used to, one album from start to finish at a time. But I still go to a shop, buy a book, and sit down and read it more-or-less one page at a time, more-or-less one book at a time.

At least I'm reading

Working in IT you'd think that the vast majority of your colleagues would be bibliophiles, avid readers, bookworms. I used to think that. I don't anymore. I've been shocked over the last few years as I talk to people about books how few do any real reading. Why don't people read? And why do adults read childrens' books and crappy fantasy novels? I've met people older than me who go on like kids about Harry Potter, Lord of the Rings, and Terry Pratchett books. They don't seem embarrassed: it isn't their dirty little secret or anything.

See, I don't count that as reading. There's an argument that says that no matter how trashy or juvenile the material, when people read something "at least they're reading". Ok, once in a while you might want to read something light - I'm reading a cyber-espionage thriller right now that I'm confident isn't going to trouble the Booker prize panel - but not all the time. So, why is it deemed so important to be reading when what you're reading is crap? I don't understand this attitude. No, I call shenanigans on this. Read good stuff, or else go for a bike ride, visit your cousin, or put up some shelves.

Anyway, the upshot of all this is that when I find someone who's interesting in reading half-decent books, I latch on to them to see what I can learn.


Words with friends 1

One thing I like to do is get books off friends. For instance, this guy I used to work with, Dave, mentioned that he had 'Zen and the Art of Motorcycle Maintenance', a book I love and have read a few times. I think it was in the context of 'Hey Eurotrash, you're a reader, you'd like this one'. I couldn't help remarking that it didn't seem to be his sort of thing, and he agreed. He said he hadn't read it, didn't particularly want it, and I must say I thought it'd be nice to have a copy here in Australia so I badgered him to give me his unwanted copy. But when he gave it to me, I was surprised to see a personal dedication on the inside page:

[GoogleBooks]

"Dear David,
Thanks for being the ideal assistant. I hope you get some value out of this book. I look forward to working with you again, and I'm sure you will achieve all your basketball ambitions - with the obvious exception of winning 3pt shootouts against me!"
I began to change my mind about taking it. Dave didn't seem to mind too much and insisted I take it. Still, I felt a bit strange about the book, and pretty quickly passed it on to Karl who I knew was interested in it. Incidentally, the next Lifeline BookFest Karl returned the favour by picking me up a 2nd-hand copy of Shackleton's 'South'. I'm glad I passed 'Zen' on, and the ultimate reward is knowing that Karl enjoyed it - we chatted about it at Friday beers at least once. I borrowed it back briefly to copy that orphaned dedication and give it a home in this post.

Speed reading? Slow down.

"I took a speed reading course and read 'War and Peace' in twenty minutes. It involves Russia."
With all due respect to the sanity of supposed speedreaders, this is a deeply insane business. I'd go further, and say "batshit crazy". So why do I use such strong words? Well, if you're reading a technical, non-fiction, science, or history book you need to pay attention, absorb the details, and let things sink in. If you're reading a fiction book, then you're reading for pleasure so what's the hurry? There, I think I've kinda covered it. The only efficiency gained by "speed-reading" is that you at least get through this waste of time quickly. Science and Woody Allen are skeptical about speed-reading. For me, it falls squarely in the category of 'no free lunch'. Skepdic makes a good point: "Who would want to hire a physician or lawyer who skimmed rather than read his or her texts?"

Doing the slightest little bit of research on speedreading brings up a lot of spammy, testimonial-heavy, unscientific corners of the internet. That doesn't prove anything, it's true. But reading is a bit like talking insofar as it involves words. A person is talking to you, in written form, when you're reading. That's the most commonsense way I can put it.

The book I'm reading at the moment - "Thinking, Fast and Slow" (which is a terrible title for such an important book) is one which I put down all the time to try and make sense of what I've just read. It's a one-book refutation of speedreading. You try reading the chapter "Regression to the Mean" quickly. You have to slowread it.
"Whether undetected or wrongly explained, the phenomenon of regression is strange to the human mind. So strange, indeed, that it was first identified and understood two hundred years after the theory of gravitation and differential calcalus. Furthermore it took one of the best minds of nineteenth-century Britain to make sense of it, and that with great difficulty."
Same with 'The Better Angels of our Nature', 'The Quark and the Jaguar', or indeed 'Zen and the Art...', to pick just a couple of great books you want to read. For me, your approach to reading should be something like progressive resistance with weight training: you should be pumping heavier and heavier ideas as your strength increases, so your toil remains constant over time. Which means you are almost always encountering new ideas, always learning, always surprising yourself. It behoves a chap to stop fiddling and pay attention. You should be reading difficult stuff, whatever level you're at, in relation to the stuff you were reading last year - you know more now, you can take it. Get Wikipedia to 'spot' you.

The underlining question

I'm an underliner. In an attempt to improve my comprehension of sciencey, technical books, I frequently sit down with a pen to hand. You should see my copy of "The Blank Slate". I even went through a note-taking phase recently where I would write out, in the Blogger app on my Xoom tablet, the stuff I would normally underline. The idea was that I would generate the guts of a blog post for each book by default. For example, it helped me write a post about James Gleick. All I'd have to do would be to glue the relevant phrases together around the meaty quotes. The trouble, of course, is that it slows the reading process down too much. Gave it up with the tablet, though I still like to do it with real books, although: "Highlighting and rereading text is among the least effective ways for students to remember the content of what they have read", according to a grumpy scientist in this month's Scientific American.

Words with friends 2

I worked with another Dave, in Executive Building in the CBD. I knew Dave to be a reader because he would come in every day with a scrunched-up copy of The Australian. One day, ol' Dave said he had to buy a new 8-book set of Vintage Classics ('Crime and Punishment', 'The Trial', etc.) because he had lost one of the books in the collection. So, what happens to the old 7, I asked? BANG! Asked for 'em, got 'em. Dave was kind enough to give me his original, now-useless set of classics-minus-one ("To Kill a Mockingbird") which since that fine day sits proudly on my shelf at home. My advice is: take books where you can get them. They will make you a better person. I reread 'All Quiet on the Western Front' and it blew me away again.

The gorilla in the room, reading a Kindle

To wrap up, I haven't even talked here about probably the single biggest recent change in the way we read - ebooks - because I want to write about that separately. I have a very Gen Y friend, Sam, who is aghast that I still read paper books. The tipping of the point with ebooks versus regular books is nigh, certainly in some sections like IT books. Reminds me of 10-12 years ago with MP3s.

But fuck it I still love to sit down with a honking big science hardback, with pictures in the middle, a nice new-book smell - the kind of book that gives you a bookshelf that will impress neighbours and Coles delivery guys. And ah - the buzz of getting the Amazon cardboard box delivery. Not quite the same over Kindle.

So, have you got any interesting insights into the way we read, the way we should or shouldn't be reading? How do you read a book?

Monday, August 20, 2012

Whoops, that IS an error!

Hey come on Blogger, don't suggest to a user that they clear their cache and cookies because your site crashed! That's just rude.


So, because Blogger crashed, you should have to log in anew to Facebook, Amazon, this site, that site, just because Blogger crashed? Naaaah. "Try refreshing the page to see if things are back in order?" Sure, I can do that, that's probably what I would have done anyway. But is it just me or is there a slight whiff of blame emanating from the instruction to "clear" these stale, soiled cookies from your hard drive that you have somehow accumulated that are preventing little old Blogger from doing what it wants to do? Get the fuck outta here!

We're having some problems with users who can't login on the Mater Patient Portal - every site with real people using it will have problems like this - and one suggestion that was made was to suggest to people to clear their browser cache and remove their cookies. Even if this was successful, it's too disruptive to people to do this. It's like hiding their car keys.

I don't know about you, but nowadays I might have a couple of hundred cookies on my hard drive. So I thought I'd check.


That's a lot of cookies.

Look at this small section of the list of cookies I have accumulated in Chrome. This is just part of the google.com cache. Are the other Google teams going to thank the Blogger team for killing the cookies they create? As you can see from the size of the scrollbar in the screenshot, there is a whole jar full of cookies there.

There's nary a site nowadays that doesn't want to personalise your experience when you visit, or in some way track your behaviour. I don't mean track as in snoop or pry, I mean simply to log your visit for their, and possibly your, benefit. Like maybe serving you some half-decent ads. Or saving you from personalising over and over their front page. As you can tell, I have no problem with cookies. I can't stand all this knuckleheaded posturing about cookies abroad today. But that's another blog post.

As well as cookies, you can see there's a lot of "Local storage" items in there. That's HTML5 Web Storage, which can be up to 64Kb per domain, which it seems to me is a shame to just throw away, because ... something happened in Blogger, and ... this might fix it? And Web Storage tends to be treated the same as cookies by browsers, cleared when cookies are cleared. There's even a type of storage in that list called "Database storage", which I'm guessing is actually SQL Database stuff, even though that API is no longer being supported.

Blogger can do a small, discreet, non-intrusive error. I know it can, because here's a nice one I found when I tried to preview my post.


Now, that's what I'm talking about.

Saturday, August 4, 2012

The Page Object pattern for UI tests

Do you do web/UI functional tests in your team? You know, the ones where Selenium, for instance, fires up Firefox, IE, or Chrome, and navigates through the app for all the world like a player piano in an empty room. If so, you want to use the Page Object model.

From the Selenium wiki: "The Page Object pattern represents the screens of your web app as a series of objects." If you're not already conforming to it, you will eventually stumble upon this pattern. So you may as well stride purposefully towards it. Ultimately, the best way to see the benefits of using it is to do UI tests using the Stupid pattern first, which we did in my team. That's the pattern where you have hard-coded strings, button ids, and ad-hoc ways of getting from one screen to another right in your test methods. About as DRY as Sea World. Test code should be treated like regular code, and you wouldn't ordinarily write your app's code in such a procedural, slapdash way.

I can proudly stand up and say that all of the pages in my current project are represented as Page Objects. Every single one. All right then, they aren't - but they're in the process of being converted to being Page Objects as I write this.

So - what's a Page Object? In our case, it's an abstract class containing (among other things) a constructor:
protected PageObject(SeleniumBase browser)
{
    if (browser == null)
        throw new ArgumentNullException("browser");
    Browser = browser;
}
with some methods like this (using the actual model classes from our main app):
public DestinationPage SubmitTheForm(FormModel model)
{
    // boilerplate Selenium code to find form elements, fill them with model data, and submit the form
    return NavigateTo<DestinationPage>(By.Classname(".submitButton"));
}
made possible by this beautiful generic base class method:
protected TDestinationPage NavigateTo(By clickDestination)
where TDestinationPage : PageObject
{
    Navigate(clickDestination);
    return (TDestinationPage) Activator.CreateInstance(typeof (TDestinationPage), Browser);
}
[Photo of a water mill]

This method is the ball bearing, the engine, nay - the water mill of our functional tests suite. It keeps things moving. It does the important work of clicking a link or a button, or some sort of page navigation element (Navigate()), then dutifully completes the corresponding paperwork by newing up (Activator.CreateInstance()) an instance of the type passed in to it, so you can chain methods in a meaningful expression like:
HelpPage page =
Site.BrowseTo(HomePage).GoToLogonPage().LogOn().GoToHelpPage();

Chaining

As you can see, this NavigateTo method allows for method-chaining. This "fluent" approach reminds me of Linq expressions or jQuery methods which are the ways I became familiar with chaining. "Methods on the PageObject should return other Page Objects" says the Selenium docs. I also find it useful to have other properties on the Page Object simply returning strings that I expect to find on the page to prove the state of the page, for instance that there is a particular message showing. But generally speaking, Page Object methods return Page Objects.

I was speaking to Bradley@Readify about all this chaining, and he cautioned against violating the Law of Demeter (or as Martin Fowler calls it, "the Occasionally Useful Suggestion of Demeter") in situations like this. I think though as developers we're conditioned to see all those dots as Demeter territory, whereas in this case there's no actual composition denoted by the dots. Of course, Brad knew this. He was just sayin'.

A fork in the road

Sometimes a Page Object method may result in two different paths being taken, depending on an input parameter, for instance an answer which may be right or wrong. In the Selenium wiki they suggest explicitly creating methods to handle this approach, but I prefer to use generic methods. I'll show you what I mean. Starting with this test:
this.Given(_ => GivenMyPasswordIsReset())
    .When(_ => WhenIChangePassword(_resetPassword), "When I use my old password")
    .Then(_ => ThenIShouldBePromptedToChangeMyPassword())
    .And(_ => AndIShouldSeeAWarningAboutUsingTheSamePassword())
    .BDDfy();
I call this helper method within the test itself (because I have another test where I do more or less the same thing except that I use a new password):
void WhenIChangePasswordTo(string password) where TPage : Page
{
    HomePage.GoToLogonPage().LogonAndChangePassword(_user, password);
}
which in turn calls this, in the LogonPage Page Object:
public TPage LogonAndChangePassword(User user, string newPassword) where TPage : Page
{
    return LogonAndChangePassword(user).ChangePassword(user, newPassword);
}
This can result in the user being successfully directed to the home page, or the Change Password page, depending on whether they use the old or new password. I don't see that there's any difference in terms of the responsibilities of the cleint test class and the Page Object. Sure the test class dictates what page it expects to be returned to, by means of the type (e.g. ChangePasswordPage) that it passes the Page Object, but in the case sugested by the Selenium wiki, it has to explicitly call a method called LogonAndChangePasswordWithOldPassword(), or such like. Generics win, I think.

Chunking

When we invoke a method on the Page Object like Logon(), we're chunking - treating a bunch of things at one level as if they were one thing on a higher level. On one level you might have instructions like "fill in the username field, fill in the password field, and press submit". On a higher level you would have "log on". We can ignore the internal structure of the lower level operation. In the same way, you can have a chunked operation of chunked operations on the Page Object.

Say you have a path that's commonly taken through your app, for example one where a user goes to the home page, logs on, and checks their messages, then you might want to chunk those operations into one aggregate one for convenience. That chunking should obviously happen on the test class itself, because it doesn't make much sense to have a MessagesPage object have to concern itself with the details, however high-level they may be, of logging on.
_messagesPage = HomePage.GoToLogonPage().Logon(user).GoToMessagesPage();
can become
_messagesPage = CheckMessages();
as long as CheckMessages() returns a MessagesPage object. This is all rather obvious I'm sure, but it's interesting to think about the movement up and down the levels you have to do when composing a test with a Page Object.

Also, even though you can new up a new MessagesPage() in each of these cases instead of reusing _messagesPage in consecutive methods, it goes against the spirit of the test which is to simulate moving through the app by getting from one page to another. Unless you're saving state on the page as you go, I guess it doesn't really matter, but reusing the variable is a truer representation of what's happening in the browser: the user gets to a page, then clicks on a link in that page to go to another one - it's the same page they entered and exited.

The wrap-up

The Page Object pattern allows us to do two things mainly:

1) Encapsulate the low-level "internals" of the page - the ids of the buttons, classes of elements, etc. that we want to click on, find, assert are visible, do anything with. Page Objects "should seldom expose the underlying WebDriver instance."
2) And generally, make the client (of the Page Object) test methods more elegant by taking them to a higher level.

Thinking in higher levels is what distinguishes chess masters from chess chumps like me. I'm crap at chess, I should point out. Couldn't beat Ricey, couldn't beat Nagy. But it remains fascinating to me. Grandmasters don't analyze the game by thinking ahead more moves than you or I, but rather by seeing the game at a higher level than you or I would.
Intelligence depends crucially on the ability to create high-level descriptions of complex arrays, such as chess boards, television screens, printed pages, or paintings.
from "Gödel, Escher, Bach: an Eternal Golden Braid" by Douglas Hofstadter