Setting up the problem
Here is a simple scenario:
1. Go to Google
2. Provide a search string
3. Search the web
4. Make sure that some results are returned
The previous scenario was implemented using Selenium IDE. Below you can see the resulting generated Java code (JUnit4):
Mr. Code meet Mr Freddy
Now, just imagine that for one reason or another, you have 100 different tests that start the same way (go to Google site, search something, assert results are displayed) and then you make an assertion over some specific thing (eg first result, text snippet of first result, etc). No big deal. But what would happen if that for one terrible (and long) second the locator of the search button is changed from “btnG” to “fasterBtnG”? Every and each one of your 100 tests will fail when asserting that the results message is displayed. If you figure out quickly the root cause of the problem, fix is easy but tedious. You have to spend a lot of time updating manually the click button locator. A better design could be coding your own test the following way:
So code duplication is avoided in a per class case. Tests are more readable. Fixes are easier because of that. But if not all your hundred tests are placed in the same class you will have to update “btnG” as many times as classes you have defined. Worst case scenario: 100 times. Ughh.
We would like a solution where code is not duplicated at all.
Proposed final solution
This solution completely decouples the writing of the test themselves from the underlaying access technology. Also, notice the name of the accessor methods. They are tightly coupled with the view. The main idea here is to provide friendly method names that when used to implement an scenario, reading the code aloud will be similar to read aloud a use case:
- mainPage.setSearch (“Some search string”);
- mainPage. clickOnSearchButton();
- No access methods are duplicated. So, updating a method will immediately update all the scenarios that use such a method.
- Access technology (eg Selenium) is completely encapsulated, so if you like/need to change it in the future, you only have to focus on the accessor methods. For example, going from Selenium to WebDriver would be particularly easy.
- Coded scenarios high re-usability: Accessor logic separated from test logic.
- Pretty good readability of test code because of accessor methods names being tightly coupled with the actual view. Scenarios could be implemented almost as defined in a use case. A casual reader of the code will read lines like clickOnSave(), setCustomerName(), selectCountryFromCombo(Country.ARGENTINA), etc. This approach has the additional advantage that QA people with basic knowledge of the language can code the scenarios on their own, provided that the back end part (accessor methods) were already implemented.
- Once accessor methods are defined, an scenario can be coded really fast. Example: An issue is discovered and fixed. You add the scenario to your tests, so the issue is verified for the following release and from that point on, the test case works as regression testing also.
- Implementing accessor methods can take a long time. Particularly true if you do not have access to the code base (to add ids as needed) and/or you are working with technologies different from plain JSP (JSF, GWT, etc.) where you have to figure out how to inject the ids that you want instead of allowing the framework to define then at runtime.
2Note: Although the following problem is not related to this approach (in fact, it is far better to deal with it than test recorders), applications based on Ajax can add a “nice” new dimension to the problem. You have to take into account wait times in most of your accessor methods. And no fixed wait times, but something fancier, like wait until such and such elements are visible, text is present, etc.2
Results are really fast to achieve with test recorders. Pretty cool also when used as proof of concept. But when you can measure your test base in tens of tests or more, maintenance becomes rapidly in some chapter of Nighmare in Elm Street. In order to avoid meeting with Mr Freddy, you should decouple the actual accessor logic from your tests, with the additional advantage that with this approach your coded scenarios will be almost self documented in a highly readable way. Using Page Models pattern when designing your Integration Tests will take precious time on the short term, but in the mid/long term you will save a lot of developing time because of them.2