Next level - Test Automation DSL

The Semantic Model is a vital part of a well-designed DSL, it provides a clear separation of concerns between the core (Selenium) model and the DSL independently, building new features into the model before figuring out how to expose them through the language (can use Bridge). As bottom line our DSL merely acts as a mechanism for expressing how the model is configured. Meaning that the DSL is just a thin facade over the model (again, the model might be a testing library or framework). So, we aim that our DSL should enhance the capabilities of that model. The right DSL makes it easier to understand what a particular functionality does.
Looking at the big picture, DSL supports a bare minimum of features needed to support its domain (SUT). We can't build an entire software system in it, but rather use a DSL for one particular aspect of a system – yeas this is the testing. So our OOP language of choice fits perfect with the DSL as a particular way of using this general-purpose language. A DSL script is valid code in its general-purpose language, but only uses a subset of the language's features in a particular style to handle one small aspect of the overall system. The result should have the feel of a custom language, rather than its host language (consider Fluent interface). When using the DSL, we should have the feel of putting together whole sentences, rather than a sequence of disconnected commands.



Let’s talk more about the Semantic Model pattern - all the important semantic behavior is captured in a model, and the DSL's role is to populate that model via a parsing step (from the BDD steps to the definition ones). As already said our Semantic model is a completely normal object model, which can be manipulated in the same way as any object model you might have. So our Semantic model is nothing more than a library or framework that the DSL uses.  Already had our hands on the BDD Gherkin’s style of coding we start to see the seamless transition our DSL provides for the programming language. It’s true that sometimes we have methods whose names make no sense in an open context, but read properly in the context of a DSL sentence. With DSL naming, it's the sentence that comes first; the elements are named to fit in with that context. DSL names are written with the context of the specific DSL in mind, while command-query names are written to work without any context (or in any context, which is the same thing).
In my opinion the top most layer (BDD) is really closer to the Procedural programming, than anything else. Your Step defs simply contain a series of computational steps to be carried out by your predefined test work flow. Any given procedure (definition) might be called at any point during a program's execution (the runner), including by other procedures or itself. Furthermore Modularity is generally desirable, especially in large, complex solutions. Inputs are usually specified syntactically in the form of arguments and the outputs delivered as return values.
So keeping things simple - a DSL is really just an API, and you are using facilities of a language you already know. But this DSL should be easier to learn. So keep in mind this when creating a layer of Expression Builders over the model. The Expression Builders are relatively straightforward to write, but most of the effort isn't in getting them to work, but in fiddling with the language so that you have something that works well. This Expression Builder cost won't appear if you are putting the fluent methods directly in the model, but that may lead to other costs if people find these methods confusing compared to a command-query API. We should aim at nothing more than a convention to use certain fluent methods to do things. One more advantage is that it produces a relatively restricted range of what can be done. This restriction can make it easier to understand what to do, and serves as a barrier to bugs. If you have a DSL with strong boundaries that limits the kinds of things you need to test for. With a general-purpose language, anything is possible, so you have to watch the boundaries through convention and review.



     By nature Selenium 2.0  aims to provide a basic set of building blocks from which developers can create their own Domain Specific Language. There are already good Selenium DSL examples.  This fact drives us further and I can point at next step -  Method chaining (Progressive Interfaces). As stated in the book I already mentioned – “a valuable variation to the basic Method Chaining approach is to use multiple interfaces to drive a fixed sequence of method-chaining calls”. This works really great with our BDD support. We utilize the Progressive interfaces to enforce mandatory elements in our test workflow. We can implement this by defining an interface that only takes a single mandatory element.  Another option for mandatory clauses are the Nested Functions. Some attention is required for the sentences finishing. It may become a problem that pops up from time to time. A good way to go is a Nested Closure.  
One more key point is the translation layer that translates the fluent interface into the command-query API. To do so we again can turn to the Expression Builder - an object that provides a fluent interface which it then translates into calls on an underlying command-query API. You can consider using Composite to build sub-expressions within an overall clause.
Finally (probably my favorite feature) DSL advocates Metaprogramming, meaning that our program modify itself at run-time. It’ll minimize the number of lines of BDD code need to express a solution, also gives greater flexibility and efficiency without recompilation. Here the BDD scenario tags will do the job just fine. I personally use them as Fixture strategy, both Setup and Teardown. You can implement such special logic for tagged scenarios in the event bindings, scoped bindings or step bindings by querying the ScenarioContext.Current.ScenarioInfo.Tags property. If done properly your DSL will turn writing the test cases in nothing more than a BDD recipe, leaving 90% automation efforts at the highest level. In time you'll use more and more WHAT steps, over HOW steps.

By now I hope that I've managed to convince you, that our DSL is a representation of the syntax of the OOP general-purpose language we already use. Basically, it's a stylized use of that language for the domain-specific purpose of testing.  


No comments:

Post a Comment