So Why an Automation Stack and Not a Framework?

How an automation stack can be used for many automation implementations

A while back I listened to a Twitter Space on Page Objects v Screenplay. It was a good discussion, and many good ideas and points were shared. I subsequently tweeted that the session reinforced my belief that an automation stack is appropriate for many automation implementations. 

It’s worth checking out that Spaces session and the tweet thread. In the thread, friend and colleague Titus Fortner tweeted that he didn’t understand my distinction between behaviors and actions. He then laid out some framework needs. After reading that, I realized that my original blog post omitted a concept about an automation stack and that we are using terminology differently. So, to fill in some blanks I decided to write this follow-up to my previous automation stack post.

Why Talk About a Stack Instead of a Framework?

Something I noticed in many of the framework descriptions from the Twitter thread was that things like “assertion library” and “Rspec” were mentioned. My conceptual stack doesn’t include these things– I intentionally leave them out.

Why do I leave them out? Mostly for portability. I’m a consultant, but a large part of my client interactions are implementations (yes, I do still write code and still enjoy it!). As such, I work with many clients in many different contexts. I need a model that facilitates specific implementations but still guides the structure of those implementations. By not prescribing specific technologies, I have the flexibility to make context-aware recommendations to clients. By not binding the conceptual stack to a specific “testing framework,” say, Rspec, NUnit, or XUnit, I have more latitude regarding framework specifics and choosing an appropriate programming language.

What’s more, not binding the stack to a framework at all, at least not at the lower levels of the stack, we can then use appropriate layers of the stack to create automation that is not based on test cases or test scripts. I label this kind of “non-traditional automation” automation assist. I’ve created tools such as an “e-comm order pumper” and “random link clicker” that provide value to testers even though none of the tasks these tools performed could be considered “test cases” (at least not in the traditional sense). Since I had independence from the framework part, i.e., Rspec, MSTest, etc., I could reuse the appropriate layers of the stack without being bound by, and perhaps limited to, the capability of those frameworks.

From a practical standpoint, some part of an implemented (as opposed to conceptual) framework generally gets bound to a testing framework. Typically, we’re not creating reference implementations; we are trying to provide value to real people and binding to an appropriate technology oftentimes provides value. Delaying that binding until implementation time can give greater portability to the stack implementation. Further portability can be achieved by implementing the binding behind a shim, interface, façade, or other appropriate feature of the chosen programming language.

The Clarification: Actions and Behaviors

To increase the understanding of the differentiation I make between actions and behaviors, I want to refer to what I call the “core of automation:” stimulus, response, and check(s). In the conceptual stack the actions layer provides the building blocks for this core:

Locate the thing with which you want to interact, if necessary.
Issue a stimulus to that thing (click, type, POST, etc.).
Obtain a response to that stimulus. This may require waiting.
Perform one or more checks, assertions, etc. of the response.

In the conceptual stack, this level of granularity is the Actions Layer; each of “locate,” “stimulate,” and “check” is an action. As mentioned in the original stack blog post, working at the Action Layer might look like this:

browser.LoginLink.Click()

browser.EmailAddressField.TypeKeys(“a@b.c”)

browser.PasswordField.TypeKeys(“p@ssw0rd”)

browser.LoginButton.Click()

Or this

HttpClient.GetAsync(requestUri).ConfigureAwait(continueOnCapturedContext: false);

Note that these are rather granular activities for test automation and programming in general. Since automation is programming, we should follow appropriate programming paradigms, such as encapsulating code for reusability. That’s where the Behavior Layer comes in.

Behaviors are reusable sequences of actions and other behaviors. Using the previous browser-based example, two possible behavior layer methods could be:

void LogMeIn(string email, string pw)

{

    browser.EmailAddressField.TypeKeysemail);

    browser.PasswordField.TypeKeys(pw);

    browser.LoginLink.Click();

}

 

void GoToLoginPageAndLogMeIn(string email, string pw)

{

    browser.LoginLink.Click();

    LogMeIn(email, pw);

}

Notice in the code above that LogMeIn() is using capabilities from the action layer; behaviors are made up of reusable sets of actions. Also, notice that GoToLoginPageAndLogMeIn() calls into both the actions layer and reuses a previously defined behavior. Essentially, we’re applying basic encapsulation and abstraction principles to test code.

To reiterate, this is a conceptual automation stack. I have a conference talk that explains this stack concept and gives real-world instances where it was used and provided value across multiple different application contexts, where it was used in traditional test automation, and where it was used for automation assist. 

In most of my real-world cases, the implementations of the stack differed, but the layers were either the same or similar. Differences in layers were influenced by the specific organization’s context, such as the application being tested or the “raw tool” being used. Context must influence the definition and implementation of each layer of a stack. Some organizations may only need four stack layers, while others might need seven. If each layer has a valuable purpose and the stack has appropriate stewardship, a specific stack concept can provide value in a specific context. The stack I present has served me well across multiple contexts and can be a great starting point for other teams.