Improving the code base of your test suites - Clean Test Code and Patterns of Automation

26.11.2014
Martin Fleischer

Some of my colleagues and I attended the Agile Testing Days in Potsdam and I wanted to share some of its content as well as my personal outcomes from this great event. This blog post focuses on best practice for applying clean code guidelines and common test patterns on automated test suites.

kreuzwerer at AgileTD

Clean Test Code

Test automation was part of my work since I started at kreuzwerker so I was pretty excited to learn some new tricks for improving my test setups and code. The first talk that broached this issue was Clean Test Code by David Völkel. The talk was focused on how to write good test code and even though most of the points he mentioned can be found in Uncle Bob's Clean Code , David applied these principles to test code and provided some neat practical examples.

Basic principals like reliability, readability, single responsibility and no redundancy are not only important for production code but for test code as well. Amen.

I can agree with that especially since some of our test suites can grow to be massive. David was demonstrating how he refactored some test code for abstraction on his slides. He suggested factoring out test code to logical units. In the end only well named methods are left within the test body, a practice I love.

Furthermore one of the subjects was the organization of the test cases. David advised grouping tests with the same Fixture/Object-under-Test, so hierarchical tests can be implemented sharing the same setup. This test structure is, in my experience, not only good for the code organization but also a huge time saver. Especially in cases where the setup phase of a test takes a good amount of time. On the other hand you need to be careful when sharing test data between different tests so that your data is not manipulated unintentionally by other tests.

For ensuring single responsibility in your code David presented the following concepts:

for each test.

I think these rules are not always practical but I like them as rules of thumb too. For me as a Rubyist using rspec quite extensively, betterspecs, which preaches these rough rules among others, was always a good source for learning how to write tidy specs. The examples provided there can be transfered to other BDD frameworks as well, if you want to write tests that read like specifications. If you don't know why you should do so I recommend you read Uncle Bob's Specs vs. Tests.

Another suggestion was using Object Mothers for test data. Object Mother is basically a ThoughtWorks coined name for the Factoy Pattern. This can be "brought to the boil" by using the builder pattern for test data with configurable attributes.

Talking about patterns brings me to the next session I want to share. Jeff "Cheezy" Morgan demonstrated some hands on in Patterns of automation to the audience.

Patterns of Automation

Even if it was pretty interesting I'll skip the first part of Cheezy's talk and jump directly into patterns. First of all he started with the well-known Page Objects. The common understanding of the Page Object Pattern is encapsulating elements and actions of a HTML page or a user interface in general in objects representing one page or screen of an application. Jeff stated that reducing Page Object to the UI is a misunderstood limitation of this pattern. I fully agree on that, in my opinion this pattern can be used on all kinds of Objects under Test. For example think about a webservice you are testing. Representing its endpoints and common actions on these endpoints as Page Objects helps hiding unnecessary implementation details and keeps your tests readable. For those of you writing User Acceptance Tests with watir or the selenium-webdriver, using ruby Cheezy's page-objects gem could be worthwhile.

Another subject Jeff demonstrated in his vital workshop was Test data management. During the run of an automated test suite, depending on its size, a lot of data could be required. As already proposed before Object Mother could be used for providing data in your tests. Jeff presented his gem data_magic when speed coding his acceptance tests for demonstration. This gem reads test data from yaml files and works nicely together with the page_objects gem. It also implements a simple mechanism for randomizing data. He also pointed out that you should stub/mock data and services. Tests should run against the fully integrated system only from time to time. Here at kreuzwerker we used the VCR gem in some projects for stubbing other http services. VCR records http interactions and stubs a service by playing back the responses. This gem not only saves a lot of time because implementing it is quite easy, but also saves you a lot of time running your tests. It also helps to make your tests more deterministic, because other services should always be treated as something unreliable.

Last but not least I would like to mention his so called route navigation pattern. In my UATs, I was encapsulating navigation, mostly by putting them in some action methods of my Page Objects. When I had test steps that required quite a lot of pages to pass, in order to assert the correctness of an acceptance criteria, the code kind of looked like this (stealing Jeff's page_object gem syntax for simplicity here):

  on(StartPage).register
  on(NextPage).hit_a_button
  on(ThirdPage).do_another_action
  on(LastPage).do_final_action

Jeff presented his page_navigation gem for cleaning this up. You can define a 'routes' attribute when including this gem, if you use the page_object gem you can even use it out of the box. Have a look at the PageFactory for details.

Now you can define routes and use the navigate_to method to simplify your test body:

  PageObject::PageFactory.routes = {
    :default => [[StartPage, :register], [NextPage, :hit_a_button], [ThirdPage, :do_another_action], [LastPage, :do_final_action]]
  }

Nice. That's pretty straightforward. If you want to see this example in action check out his Screencast on navigational features of page-object.

Without wanting to sound arrogant none of these techniques on patterns were completely new to me, as we use them in most of our projects, but the detail makes the differences. Seeing it all brought together and watching tests implemented as swiftly as lightning, live, it incites you to keep up with improving the quality of your test code.

Picture from the conference

If you missed the AgileTD or attended another session, checkout the slides for Clean Test Code or Patterns of automation. Moreover Cheezy published most of his patterns he demonstrated in his workshop in two screencasts.

Martin Fleischer
comments powered by Disqus