Evolving the Symbiont

In my previous post about the start of my Symbiont project, I gave a few examples of what I can do now with libraries like Selenium and Watir. It probably wasn’t entirely clear what direction Symbiont was going to go, given what I described there. Here I’ll to provide a bit more of a roadmap for what Symbiont will need to accomplish.

The test scripts I showed in the last post were ones that you tend to find when people are just starting out with these tools. Here I want to explore a bit about how those scripts tend to evolve, which should then show us the direction that Symbiont will have to evolve as well.

The first thing to notice about those examples is that all of the script logic is essentially contained within the script itself. It’s just one large mash of code. Further, since Selenium and Watir differ in some of their implementation details, those test scripts look quite a bit different in some very specific ways. Yet those underlying details should ideally be hidden from the test scripts. So here’s what I would like my test script to look like:

Test Script Example 1

Before I had a url_is() method and that was called with a view() method. Here I’m doing something that is conceptually different but is actually the same thing: a begin_at() method that is called with a start() method. I’ll come back to why this is relevant a bit later on.

As it turns out, this is quite possible and below I’ll show you what this would require in terms of supporting logic. First here’s the Selenium version that makes the above possible:

And here’s the Watir version:

One thing you’ll probably notice here is that Watir is often a lot easier to deal with than Selenium. Granted, that’s a subjective opinion but to me not only does Watir logic look cleaner, but it seems less convoluted to write as well. Regardless, the important thing here is that the my Test Script Example 1 will work regardless of the browser library I’m using. Put another way, the test script will work the same regardless of which activity definition is referenced.

Wait … activity what? In my previous post, I had talked about a distinction between “page definitions” and “activity definitions.” The above are essentially activity definitions in that it’s a class that gets instanced and provides methods that allow test scripts to reveal more of their intent. This is because the implementation is somewhat “hidden” — or at least behind the scenes — in the definitions.

This, in fact, is why I now provide a begin_at() method. Activity definitions suggest a workflow that begin at a certain point. Page definitions suggest nothing more than a specific page at a specific location that you visit.

Okay, so I hid the implementation behind the intent. And that brings up an interesting point. Is it possible to not only more closely align the intent parts of the logic, but also to more closely align the implementation parts? After all, if you compare the above logic to what I showed in my last post, I really haven’t done anything all that terribly different. The same logic I was using in the previous examples is pretty much the same logic I’m using now. The difference is that I’ve relegated the handling of this to methods in the activity definition, leaving my test script relatively concise.

So is it possible to subsume the differences of Selenium and Watir even further, providing a layer on top of them that allows testers to design logic that can use either library? If so, that might be a short step to providing a layer on top of other libraries beyond just Selenium and Watir. That is what a library like Symbiont should eventually evolve into.

As an example of how this stuff can evolve, let’s consider how the common page object design pattern came about. What I’ve described as “page definitions” and “activity definitions” are really just the page object design pattern being put to use. The reason I don’t use that term is because I think that a rigid adherence to the idea of page objects takes the focus off of business workflows. That’s a topic for a different day, however. For now, consider this test script logic:

Test Script Example 2

That’s pretty simple, right? It’s fairly easy to see what’s happening. Here is what the logic that would support this looks like in Selenium:

Here is that same logic in Watir:

Notice how this second test script is a bit lower level than what I just showed you before, however. My previous scripts were focused on providing higher-level method names that indicated what you wanted to do. So, in the first case, you had a login_as() method that handled the details of what fields to use. Yet consider that method again. First in Watir:

Then in Selenium:

I would like to combine those methods with the easier way of referring to the objects that Test Script Example 2 shows. In short, I want this:

Can I have that? You can and this is where you get into traditional page objects. To click a particular button (that happens to be in an iframe), Selenium says I must do this:

Watir says I must do this:

So what I’m saying here is that I want the page.login statement in Test Script Example 2 to make whatever call is necessary to execute the appropriate logic.

The Symbiont way of doing this is to provide the platform object. The platform will indicate what browser driver is being used. An action — such as clicking a button or typing text — will be passed into the library and will be routed by the platform object to code that is either Selenium-specific or Watir-specific.

So I have the future path that I need to take at this point. My next steps will be to work on handling the most common web page objects: links, text fields, and buttons.


This article was written by Jeff Nyman

Anything I put here is an approximation of the truth. You're getting a particular view of myself ... and it's the view I'm choosing to present to you. If you've never met me before in person, please realize I'm not the same in person as I am in writing. That's because I can only put part of myself down into words. If you have met me before in person then I'd ask you to consider that the view you've formed that way and the view you come to by reading what I say here may, in fact, both be true. I'd advise that you not automatically discard either viewpoint when they conflict or accept either as truth when they agree.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.