My wife has a good recipe for wraps.
Imagine a situation, that I want to tweak the recipe. Last evening I ate a wonderful wrap and asked about the ingredients. At home I start to change the recipe.
What am I thinking?
“Let me see. 2 spoons of sunflower oil must be replaced by 1 spoon of sunflower oil and 1 spoon of olive oil.
I use a marker to hide the 2. Now I write 1 above the square. Writing “and 1 spoon of “. It was special kind of olive oil. I need to look this up.
First, I have to add new spices. Let me remove the old ones with my marker. The new spices are chili powder and something else.
Time to look things up on the computer.” I leave the kitchen.
Then my wife enters the kitchen to make wraps for dinner:
“What happened with my wrap recipe?”
You run a tight ship.
– Glenn Talbot
Refactoring too early
In this case I used DRY or Don’t Repeat Yourself. There is no need to have several copies of the same recipe, because the new recipe was much better than the old one. Making copies is not my favourite way to spend my time.
In this situation the improvement came too early. There was no proper recipe to make wraps for my wife.
My job is to test stuff. Refactoring is a way to clean up my code. There is a risk, that I clean up my code too early.
Groundhog Day testing
The big advantage of test automation is that the computer does the boring stuff. It can repeat steps without reducing the quality of the tests. A human being is likely to lose concentration over time.
A note for the screen reader users: characters like parentheses, dot, and braces are used in the code. It is advised to change the interpunction and symbol level before reading the code.
For my VIP Cinema app, I had written the following method to select a drink and a snack:
public ConfirmOrderPage selectColaAndPotatoChips(); { WebElement numberOfColasTextfield = driver.findElement( By.name(NUMBER_OF_COLAS_TEXT_FIELD); WebElement numberOfPotatoeChipsTextfield = driver.findElement( By.name(NUMBER_OF_POTATO_CHIPS_TEXT_FIELD); WebElement okButton = driver.findElement( By.name(OK_BUTTON); numberOfColasTextfieldSnackAndDrinkPage.sendKeys("1"); numberOfPotatoChipsfieldSnackAndDrinkPage.sendKeys("1"); oKButtonDrinkAndSnackPage.click(); return ConfirmOrderPage(driver); }
This means:
- order 1 Cola
- order 1 potato chips
- press the OK button.
Now I need a piece of code for ordering 2 Colas like:
public ConfirmOrderPage selectTwoColasAndPotatoChips(); { WebElement numberOfColasTextfield = driver.findElement( By.name(NUMBER_OF_COLAS_TEXT_FIELD); WebElement numberOfPotatoeChipsTextfield = driver.findElement( By.name(NUMBER_OF_POTATO_CHIPS_TEXT_FIELD); WebElement okButton = driver.findElement( By.name(OK_BUTTON); numberOfColasTextfieldSnackAndDrinkPage.sendKeys("2"); numberOfPotatoChipsfieldSnackAndDrinkPage.sendKeys("1"); oKButtonDrinkAndSnackPage.click(); return ConfirmOrderPage(driver); }
This piece of code looks almost the same as the previous one. A lot of repeated lines of code, so it is time for …
Refactor 1
public ConfirmOrderPage selectTwoColasAndPotatoChips(); { driver.findElement( By.name(NUMBER_OF_COLAS_TEXT_FIELD).sendKeys("2"); driver.findElement( By.name(NUMBER_OF_POTATO_CHIPS_TEXT_FIELD).sendKeys("1"); driver.findElement( By.name(OK_BUTTON).click(); return ConfirmOrderPage(driver); }
My first step was to make the code more compact. But I still repeated the lines of code for the text fields and buttons in the method selectColaAndPotatoChips, so I should use methods instead. Small reminder to myself: DRY or Don’t Repeat Yourself.
Trust the system.
– Melissa May
Refactor 2
public ConfirmOrderPage selectTwoColasAndPotatoChips(); { selectColas("2"); selectPotatoChips("1"); pressOKButton(); return ConfirmOrderPage(driver); }
This code looked more readable than the previous code. But I still needed some way to change the number of Colas in a simple way.
Refactor 3
public ConfirmOrderPage selectDrinksAndSnacks(int numberOfColas); { selectColas(numberOfColas); selectPotatoChips(1); pressOKButton(); return ConfirmOrderPage(driver); }
I introduced the parameter numberOfColas, so I could use this method for the situations to select 1 Cola or 2 Colas. One parameter fits all.
Some other small things I refactored are:
- I changed the name of the method to selectDrinksAndSnacks. This is a more general name, so I could extend to other drinks and snacks. One name fits all? I mean all interactions with the Order Drink And Snack Page.
We’ve got to get on the same page.
– Phil Coulson - I did not like that numberOfColas was a string. This could lead to programming errors. An int or integer restricted the use of the possible values. I didn’t want to wait for a tester using the word “hundred” for ordering Colas. Or the polite Spanish tester: “Una, por favor.”. This means: one, please.
- I also changed the method selectPotatoChips to use an integer number of Potato Chips. Consistency fits all.
To explore new combinations
A way to increase sales is to offer discounts to customers. If I have a discount code for a Cola, then I get the Cola for less money. If I take an orange juice, I have no discount. Bad luck.
I talked to the developers and hoarded some discount codes. And off I coded.
public ConfirmOrderPage selectDrinksAndSnacks(int numberOfColas, int numberOfOrangeJuices); { selectColas(numberOfColas); selectOrangeJuices(numberOfOrangeJuices); selectPotatoChips("1"); pressOKButton(); return ConfirmOrderPage(driver); }
This was pretty easy. I just added a parameter for Orange Juices. As time passed by, I added more parameters. Then I got a request to execute the scenarios for a regression test. This was the moment that test automation would shine.
The discount scenarios could be executed except the one which I worked on. But the other scenarios could not be used, because I added parameters like numberOfOrangeJuices. This was not the moment that test automation would shine.
This is a potential volatile situation.
– Melissa May
I used DRY or Don’t Repeat Yourself too early. What I needed, was to find the proper way to use it.
Wrongs have been committed. Now we make them right.
– Jemma Simmons
I could use git, a version control system, to restore my old code. In best case I could use the scenarios and get back to my current state of coding.
There was a big If. If the scenarios were broken in some way, then I had to repair them and merge them with the current scenarios. Making things a little more complicated than I had expected. A typical worst case.
We’ll figure it out. We always do.
– Phil Coulson
In order to keep things simple for myself I repaired the broken scenarios. I was not in the mood to use git to use an older version.
In hindsight
Let me summarise the situation of coding test automation. I was doing two things at the same time:
- adding new test automation code.
- using test automation code for a regression test.
It is like a kitchen where I am trying to improve the wraps and my wife is making new ones. The problem is, that we are using the same recipe at the same time.
It is important to know when to throw out the plan.
– Phil Coulson
In this case DRY or Don’t Repeat Yourself is not the peaceful option to choose. A simple solution is, that both my wife and I use different recipes for our purposes. The copy of the recipe is really needed for a workable situation.
I can still tweak the wrap using herbs and spices. There might be a better wrap. In the meantime my wife is baking the wraps. After all I want to have dinner with wraps.
We are solving problems we created.
– Phil Coulson
Closing note
During the weeks of programming test automation I noticed, that I had made a lot of beginner errors. These things happen. My way is to learn from my mistakes.
What you do next, matters.
– Phil Coulson
DRY or Don’t Repeat Yourself is a principle. I realised, that I should not use it without thinking. As a tester I always looked to exceptions. As a coder I had experienced the same situation.
Should I remove duplicated lines of code? Or even more important, is it smart to copy the code?
Copy that.
– Mack