An Inspired Evening

Every company has its own way to engage with his users. Some years ago,  my team members and I spent an evening with the users.

The invitation

One day I received a mail asking for volunteers. It was for a special session. I was curious, so I asked the other testers about it.
“The backlog contains a lot of tickets. So, once in a while user representatives will choose items they want to be solved.”

It was difficult for me to imagine how things would evolve. The other tester continued with: “The programmers will program the solution on that very evening.”

There was no need for extensive documentation and discussion. It was about asking and making it work. Because this sounded agile to me, I volunteered as a tester.

The preparation

In the following weeks the team for the session slowly began to form. There were 3 programmers. An analyst also joined. People with different skill sets were on board.

Welcome in the club.

The selection of the tickets was a balancing act. On one hand a ticket should add real value to the users. On the other hand, it should not take too much time to solve it. The outcome of the session would be a set of solved tickets, which annoyed the users for a long time.

As expected, the users sent a big list to the product owner. In turn he sent the tickets to the programmers. Another selection took place.

During the days before the session, I heard the words “Too big” in different volumes. I interpreted “Too big” in a normal tone as “Solving this ticket will cost a big deal of the session.”. A shouted “Too big” sounded to me like “We need more than 1 session.”.

The meal

The evening started with a meal. Users recognised some developers. There was some small talk.

The session

After the meal we moved to a room with a big table for the laptops. On one wall a big screen was attached.

The problem

The first Jira ticket was shown on a big screen. This was not the application they were looking for. I noticed that the users were disturbed. Imagine your problem described in an unfamiliar form. It was a wall of text. This was not a good start.

A user representative also saw the confused look on the faces of her colleagues and started to talk. Everyone started to look at her instead of that screen with all that text.

She started with “You know that window with …”. People began to nod, while she continued to describe the window and the functions. “Now the problem is, that …”, she continued. Annoyance could be felt, when she described the details.

Then she told: “It would be great, if …” followed by a solution. Her peers showed appreciation for the proposal. She ended with: “Other things to take into account, are …” followed by domain knowledge and their way of working.

She told a first-hand user story. Nothing was lost in translation. There is no shorter path to a user than the shortcut I just described.

The clarification

The single male programmer stepped forward: “I take this one.”

“Can you plug me in?”, he asked. Someone connected his laptop to the big screen. The users saw a familiar window. This was the application they were looking for.

The programmer continued: “So you want the buttons on this window?” Users nodded their heads. The mouse pointer moved to two different locations: “I can place a button here. And here. It will look similar like …”

“this screen”. The programmer was now showing another window on the big screen. “Here are the buttons placed on similar locations.” This sounded good enough for the users.

The laptop of the programmer was disconnected and the next Jira ticket was shown.

The queue

This time the user representative scanned the text in a few seconds and started to talk about the problem, a possible solution, and other relevant information.

Then the first female programmer asked the second female programmer: “Shall I take this one?”
“Yes”
Another round of questions and answers followed. Then she started to program.

Rinse and repeat

Then it was the turn of the user representative and the second female programmer to repeat all the steps for the next Jira ticket.

The wait

It was silent in the room. Three programmers were ticking on their laptops. The other people were quiet.

Once in a while a programmer would ask some questions:
“You wanted to order things.”
A nod followed.
“Is the order ascending?”
“Yes, that would be handy.”

And the silence started again.

The demo

After a while the male programmer said: “I want to show something.” His laptop was connected to the big screen. “On the window I added the buttons.”, while pointing them with the mouse pointer.

“If I do this, …”, he pushed a button, “then this happens.” He told how the buttons interacted with the window.

The 6 users started to ask questions. Some of them were answered by a small demonstration of the application. This was the fix they were looking for.

The business analyst and I were also looking from other perspectives:

  • What is the consequence of the shown information?
  • What happens, if …?

After all the questions were answered, the programmer was available for the next ticket.

The stack

During the evening the stack of tickets slowly decreased. Three different user stories were taken care of at the same time.

The closing

Towards the end of the session the tickets were chosen were with more care: could it be fixed before the end of the session?

I think I can make it.

At the end there was a positive buzz in the air. An evening of working after a complete workday had tired the participants of the session. Afterwards people talked a little before going home.

The follow up

It was a productive evening. The programmers had solved some annoying problems for the users. These were the fixes they were looking for.

The programmers had worked in a development environment, so the fixes were not available on the workplace of the users the next day. The code had to be merged with the code for the next release. Then standard tests and regression test had to be executed by the testers.

The context

It is tempting to compare Jira tickets with low hanging fruit. Is it interesting to invite 6 users for a few hours in an evening to see a development team picking 1 apple?

Strawberries are hanging lower than apples: are 100 strawberries more worthwhile than 100 apples? There are also different strawberries: are green strawberries more worthwhile than red strawberries?

In this company the time to fix the ticket and the value of the ticket were used for the selection of the tickets. These factors might be different for another company.

Later I shared this experience with different companies, but it was not applicable.

Your mileage may vary.

DRY or Don’t Repeat Yourself in test automation – part 3

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:

  1. adding new test automation code.
  2. 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

DRY or Don’t Repeat Yourself – part 2

One evening I had a wrap on my plate. Then I took a modest amount of vegetarian filling. My wife noticed this:
“No wonder you can eat three wraps.”

Refactoring

My wife improved my meal with this single remark. The more filling, the better. Lucky for me, she did it, before I would eat my wrap. These small things add up.

In software development improving the code is called refactoring. The first step is to notice something wrong, which is followed by using the right solution.

Let’s go back to the wraps. My wife saw, that I did not put enough filling on my wrap. Then her solution was more filling.

Refactoring in practice

After hours of programming, I had a test scenario for the Cinema VIP app, where the number of Colas was increased because of the Desert scene in Monsters Unlimited.

Some steps from the scenario were:

  • order 1 Cola in the Drink and Snack page,
  • go to the next page,
  • regret my choice, (I did not code this, but it adds to the flavor.)
  • go back to the Drink and Snack page,
  • change the number of Colas to 2 in the Drink and Snack page, and
  • go to the next page.

Changing lines

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.

Then it was time for me to have a close look to the code. The line of code to find the text field with the number of Colas was used twice. First I set it to 2 and later to 1. Every time the Snack and Drink page was opened, the text field must be found again.

In my code I found the following line twice.

WebElement numberOfColasTextfieldSnackAndDrinkPage = 
  driver.findElement(
    By.name(“4958495”);

This means: find the text field with the name 4958495.

I had to change this, so I added the following line:

public static final String NUMBER_OF_COLAS_TEXT_FIELD =
  “4958495”;

This means: NUMBER_OF_COLAS_TEXT_FIELD is 4958495.

Then I changed both lines to find the text field.

WebElement numberOfColasTextfieldSnackAndDrinkPage = 
  driver.findElement(
    By.name(NUMBER_OF_COLAS_TEXT_FIELD);

This means: find the text field with the name NUMBER_OF_COLAS_TEXT_FIELD.

I had severe doubts about NUMBER_OF_COLAS_TEXT_FIELD. In my test scenario several windows were used. There was only 1 Number of Colas Text Field in the whole application. On the other hand every page had an OK button. Using OK_BUTTON is an accident to happen.

So I changed OK_BUTTON in the Drink and Snack Page to OK_BUTTON_DRINK_AND_SNACK_PAGE. So I used
NUMBER_OF_COLAS_TEXT_FIELD_DRINK_AND_SNACK_PAGE.

In this case I used refactoring to make sure, that my test automation would still work. I like code which is easy to read and change.

Time for some reflection

I noticed the recurring lines of code. This would lead to code which would be difficult to maintain. I remembered DRY or Don’t Repeat Yourself.

My solution was to use a single line of code to determine the name of the Number of Colas Text Field. If the name would change, then I had only to change this line of code.

Page Object Model

A few weeks later I had 3 scenarios with the Drink and Snack Page containing the same lines:

numberOfColasTextfieldSnackAndDrinkPage.sendKeys("1");
numberOfPotatoChipsfieldSnackAndDrinkPage.sendKeys("1");
oKButtonDrinkAndSnackPage.click();

This means:

  • order 1 Cola
  • order 1 potato chips
  • press the OK button.

These same lines of code in 3 different test scenarios did not feel quite right to me. Repeat after me: Don’t Repeat Yourself. There was still too much repetition.

I already used objects for the web elements. Then a tester introduced me to the Page Object Model. I looked at her code and saw less lines of code to do similar things.
“Why did you tell me this before?”
“First you have to get comfortable with the normal way of coding.”
She got a point.

The Page Object Model or POM makes objects for complete pages. So the Drink and Snack page was an object.

My test scenario contained the following actions:

  • select the movie in the Movie Page.
  • select the drink and snack in the Drink and Snack Page.
  • confirm the made options in the Confirm Order Page.

The first 2 steps I programmed as follows:

MoviePage moviePage = homepage.selectMovie();
DrinkAndSnackPage drinkAndSnackPage =
  moviePage.confirmMovie("Monsters Unlimited");
ConfirmOrderPage confirmOrderPage = 
  drinkAndSnackPage.selectColaAndPotatoChips();

This code was more readable than all those detailed lines of code. This also saved me a lot of space.

I put all the code for the web elements of the Drink and Snack Page in the following method:

public ConfirmOrderPage selectColaAndPotatoChips();
{ 
  WebElement numberOfColasTextfield = 
    driver.findElement(
      By.name(NUMBER_OF_COLA_TEXT_FIELD);
  WebElement numberOfPotatoChipsTextfield = 
    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);
}

In the meantime I could not resist another refactoring. I put all the code for the Snack and Drink Page in one Java file. So all mentioned web elements in this file are on this page. So I renamed NUMBER_OF_COLAS_TEXT_FIELD_DRINK_AND_SNACK_PAGE to
NUMBER_OF_COLA_TEXT_FIELD.

Cliffhanger coming up

At the end I used DRY or Don’t Repeat Yourself. Less copied lines meant less problems. It was time to hit the wall again.

To be continued.

Sharing knowledge about testing and other things on my mind