Flower Series Introduction
In Poland, stories about some silly mistake that you or someone else has made are called flowers. Probably after THE LITTLE FLOWERS OF SAINT FRANCIS OF ASSISI.
And that why this series of automation mistakes is called Flowers. I will write here about broken principals and anti-patterns which I have encountered in my automation career. I will also show how to fix them. I will concentrate on small mistakes. There are easy to ignore, and by themselves are not harmful. But they have a tendency to multiply and then they are a time-consuming nuisance.
All code examples of bad Practices are based on an actual 'production’ code. It was running as part of regressions suites, CI pipelines, etc. I have stripped it of all unnecessary elements: other antipatterns, redundant logic. I have also anonymized it, so at least Names and locators will be different
Let’s start
WET Locators:
[csharp]
private IWebDriver webDriver;
public IWebElement SomeTable
{
get { return webDriver.FindElement(By.Id("someTableId")); }
}
public bool IsSomeTableDisplayed
{
get
{
return webDriver.FindElement(By.Id("someTableId")).Displayed;
}
}
[/csharp]
I hope you see unnecessary repetition in IsSomeTableDisplayed. It reuses webDriver.FindElement(By.Id(„someTableId”))
There is principle called „Don’t Repeat Yourself” (DRY). It also has its negative counterpart called WET – „We Enjoy Typing.” [expand=„Click for more info”] There are many interpretations of this abbreviations, but this is my favorite.[/expand]
I Had seen this stuff too often. One Method/Property finds an element. And then another is doing the same. Why is it bad?
- More places to edit locator in case of a change to the tested page.
- It obscures the meaning of the function. – You will spend some time reading it before you realize that they are the same element.
This example was trivial, cause I have used similar names. But now Imagine that those elements are few methods away from each other – for example, there are 10-15 similar properties between them. And atop of that, you can expect different names on them.
If you are looking for a hardcore example, add to above different locators leading to the same element. (For example one uses id: sometable and CSS on the other element: body div#result table). I have seen that too. Most complex cases are so obscure, that only after lots of refactoring and debugging you realize they are the same.
[csharp]
private IWebDriver webDriver;
public IWebElement ProductsTable
{
get { return webDriver.FindElement(By.Id("someTableId")); }
}
public IWebElement FilterInput
{
get { return webDriver.FindElement(By.Id("InputFiltersId")); }
}
public IWebElement SearchButton
{
get { return webDriver.FindElement(By.Id("buttonId")); }
}
public IWebElement ResetFilters
{
get { return webDriver.FindElement(By.Id("resetFiltersId")); }
}
public bool IsItemsTableDisplayed
{
get {return webDriver.FindElement(By.CssSelector("#someTableId")).Displayed;}
}
public IWebElement Title
{
get { return webDriver.FindElement(By.Id("Title")); }
}
public IWebElement OptionsDropDown
{
get { return webDriver.FindElement(By.Id("InputFiltersId")); }
}
public IWebElement ProductsCount
{
get { return webDriver.FindElement(By.Id("ItemCount")); }
}
[/csharp]
This one is closer to a real thing. Probably you did catch that ProductTable and IsItemsTableDisplayed are both same things. Did you find that OptionsDropDown and FilterInput are the same?
Now, why this mess happened? Well, many people working on the same project and lack of proper reviews. And most important Different Language. Product – is a term used by Business, but Devs usually call them Items. Same situation with Filter and Options.
Time To Fix Our Orginal Example
[csharp]
private IWebDriver webDriver;
public IWebElement SomeTable
{
get { return webDriver.FindElement(By.Id("someTableId")); }
}
public bool IsSomeTableDisplayed
{
get { return SomeTable.Displayed; }
}
[/csharp]
Now it is better; this fix was an original goal of this Flower. As I said, they are about fixes for small stuff that is annoying and can cause waste of time. But There is another issue here much bigger one.
Why the hell we have encapsulation For SomeTable property if SomeTable is public?
That is an excellent question; I had many discussion about method wrapping of single line WebElements Call.
But there has to be consistency. If we wrap one thing, we should wrap other too and mark SomeTable Private.
Or we can delete IsSomeTableDisplayed and get it straight from Element. I prefer the first solution
[csharp]
private IWebDriver webDriver;
private IWebElement SomeTable
{
get { return webDriver.FindElement(By.Id("someTableId")); }
}
public bool IsSomeTableDisplayed
{
get { return SomeTable.Displayed; }
}
public int SomeTableRowCount()
{
//Someoperations
}
[/csharp]
Much better. There is another discussion here about methods and properties, but we leave it for another day.
This is it, small fix for a small problem.
Death by thousand cuts.
By this series you will see lots of this kind annoyances each of them is easy to fix, only requires discipline and some thinking. You may think it is an even waste of time. But same as one dirty dish in a sink is not an issue but sink overflowing is a filthy kitchen. Next
Next time we take a look at more WET – this time Useless logic!