TL;DR: To avoid duplicating test code, it might be a good idea to parameterize tests on your page objects.
While duplication is mostly a bad thing when it comes to user interfaces as well as at is for code, there are cases where it might be desirable: For example, you might have several user interfaces for the same feature which will be used for A/B testing or expose the same functionality using a different interface for a specific user group, which was my use case here:
We developed a new screen for managing orders in our eCommerce system which is to be used by external users who have less knowledge about the system and fewer permissions. The new new interface reuses it’s functionality from the existing, full-featured one but leaves out some more advanced or internal features. While those interfaces of course reuse the code internally and thus should both work, I wanted to have some WebDriver acceptance tests to ensure the components were properly integrated.
We had a set of WebDriver tests for the existing interface using a page object. A trivial approach would be to copy/paste the code for all the tests I wanted to pass with the new interface to a new test class, but I apply the same rigour to my tests as to production code, so that was out of the question (I see people do it all the time and it makes me sick).
So what I did instead was to first use inheritance to create two page objects sharing a common base class with the shared content and functionality and modelling the differences in the extending classes, providing both a clean API to write automated test cases as well as executable documentation on why there are two so simliar mangement interfaces and how they differ.
Note: We are using Geb to define page objets and Spock to run our tests. Thus I will use their syntax for the example code, but this concept can probably be applied with any test framework and custom page objects as well.
Then I split the existing test into two test classes so that all the test methods I wanted to pass against both interfaces would be in a common class, making it obvious for someone reading that test class that this was shared functionality. Finally I parameterized those shared tests on the page object to have my test runner create the two very similar test cases for each feature instead of explicitly copying it. Suppose you had the following spec:
All I needed to do was to add a where block for the parameterization and change the line that goes to the page like this:
Done – the tests are running against two different implementations. One problem that remains is that in spock is seems not to be possible to parameterize a whole test class. Therefore I extracted the page object classes to a reusable list…
…and added the where block to each test method. So, unfortunately this approach is not 100% code-duplication-free after all, but it is a single line of code that is duplicated and I could imagine that a future spock version allows to parameterize whole classes.
Let me know what you think about parameterizing on page objects in the comments or ping @c089 on twitter.