Our Java test automation code is full of ‘if’ blocks. There is always some condition we need to evaluate. Based on the result of the condition evaluation, we need to execute some code or another. But having too many ‘if’ blocks, or too many ‘else’ branches can lead to clogged tests. So how can we adjust our ‘if’ blocks to fulfill their purpose, without making our tests unreadable? In this post I will go over a set of examples we might encounter in our tests, and I will show an alternate, better way of writing the same code.
1. Is it a weekend day?
Let’s consider that in our test we have a String variable representing a day of the week. We need a helper method that tells us whether the day represented by the String is on a weekend. The method should return ‘true’ if the day is either Saturday or Sunday, and ‘false’ otherwise.
⛔ The ‘NO’ variant
Let’s look at a first variant for solving this task. This is one variant I do not recommend using and I will explain why.
private boolean isWeekend_no(String weekDay) { weekDay = weekDay.toUpperCase(); if (weekDay.equals("SATURDAY")) return true; if (weekDay.equals("SUNDAY")) return true; if (weekDay.equals("MONDAY")) return false; if (weekDay.equals("TUESDAY")) return false; if (weekDay.equals("WEDNESDAY")) return false; if (weekDay.equals("THURSDAY")) return false; if (weekDay.equals("FRIDAY")) return false; return false; }
First, the method takes the parameter representing the name of the day and converts it to upper case. This is because inside the method, the comparison with known day names will be done against upper case values. So, the parameter passed to the method can be all upper case, all lower case, or any kind of mixed case. This is a good thing.
Next, the code is comprised of a series of ‘if’ blocks. Each block compares the value of the day parameter to a day of the week, until all the days are covered (Monday through Sunday). This means 7 comparisons represented by 7 ‘if’ blocks.
Inside each ‘if’ block, a ‘return’ clause is specified. Only the first 2 ‘if’ blocks can return true, namely those where the days represent a weekend day. The remainder of blocks will return ‘false’, since those days represent work week days. Additionally, you will need to return a value outside of the ‘if’ blocks. This will reflect what to return if the parameter passed to the method did not correspond to any of the values evaluated in the ‘if’ blocks.
Let’s say you pass ‘Friday’ as parameter to the method. Once the ‘if’ block that compares the parameter to ‘FRIDAY’ is encountered, the method will exit with the value ‘true’. None of the remaining code will execute.
This variant is not the best one you can write. Coming back to the requirements, we know that the only 2 days that are on a weekend are Saturday and Sunday. Therefore, we should not care or even specify any other values passed as parameters. Those should be treated the same, at once, without those extra 5 ‘if’ constructs. There is simply too much code in this variant.
✅ The ‘YES’ variant
Let’s look at an improved version for determining if a specified day is on the weekend:
private boolean isWeekend_yes(String weekDay) { if (weekDay.toUpperCase().equals("SATURDAY") || weekDay.toUpperCase().equals("SUNDAY")) return true; return false; }
Here, we have only one ‘if’ block. We are only interested in the parameter value representing either Saturday on Sunday. These are the only 2 values that can make the method return ‘true’. Any other value for the parameter, whether a valid week day or even some other random String, will make the method return ‘false’.
Remember that a ‘return’ statement will exit the method entirely. Therefore, once we determined that the parameter value was on a weekend, we return ‘true’, and that is where we exit the method. We will therefore, in this case, skip the ‘return false’ statement. If the String passed to this method is valid (not null) and is anything but those 2 values , we will return false.
2. Getting the text of a WebElement
In this task, we need to grab and return the text of a WebElement. This is the text we get by using the ‘getText()’ method on the WebElement, and of course, it returns a String.
⛔ The ‘NO’ variant
Let’s look at a variant of code I don’t recommend, which involves creating a helper method:
private String getElementText_no(WebElement element) { if (!element.getText().isEmpty()) return element.getText(); return ""; }
This code, in the ‘if’ block, checks whether the text of the WebElement is not empty, and only returns the result of ‘getText()’ if the text is not empty. In this case, any further code in the method does not execute. However, if the String returned by ‘getText()’ is indeed empty, the return statement belonging to the ‘if’ block does not execute. What does execute is the last ‘return’ statement, which, as specified in this example, returns an empty String. Logically speaking this is wrong.
✅ The ‘YES’ variant
The task says to return the text of the WebElement. Whatever the value of the text is, you should return it. The above example does something very weird. First, it checks whether the text is empty. If it is not, it returns the text of the WebElement. However if it is, it will return an empty String, which is basically the same as the value of the text of the WebElement. So, either way, the result returned by the method is the result of the ‘getText()’ method. Therefore, the better variant for the code required by this task, which does not even use any ‘if’ block or a method, is the one below:
element.getText();
Here, I did not create a new method for this code, since, as I mentioned in an earlier post on coding good practices, you should not create a new method for just one line of code.
3. Is the text of the WebElement empty
In this task, you will need to check whether the text of a WebElement is empty and return a boolean value accordingly.
⛔ The ‘NO’ variant
In the first code variant for this task, we will see the first ‘if-else’ block. Up to now we only had ‘if’s.
private boolean isElementTextEmpty_no(WebElement element) { if (element.getText().isEmpty()) { return true; } else return false; }
This code, in the ‘if’ statement, checks whether the text of the WebElement (retrieved by using ‘getText()’) is empty. In case it is, the method we created for this task will return a boolean value of ‘true’. In the ‘else’ branch we will return ‘false’, since, well, the text is empty.
✅ The ‘YES’ variant
Looking at the previous code snippet, there is a very good reason why it’s wrong. The ‘isEmpty()’ method returns a boolean value. What the code above really does can be summed up as this: if true return true, else return false. Instead, we should use the following code, where I did not create a method, since this is only one line of code:
element.getText().isEmpty();
In this case, we are simply returning the value returned by the ‘isEmpty()’ method. When the text passed to it is empty, we will return true, and otherwise we will return false. Problem solved easily.
4. Start browser based on the value of a parameter
For this task, we need to create a method that will initialize a browser instance, through the corresponding WebDriver. The method takes a String as parameter, whose value represents the name of a browser. This parameter will determine what browser needs to be initialized.
⛔ The ‘NO’ variant
In the below example of how not to write the required code, there is a classic usage of type ‘if-else-if-else-if…’. Those are too many branches to be able to read the code properly, and to understand what it does. And when you have such a complicated structure, it’s tricky to understand whether you managed to cover all the possible scenarios needed.
public WebDriver startBrowser_no(String browserName) { if (browserName.equals("Chrome") || browserName.equals("chrome") || browserName.equals("CHROME")) { System.out.println("Chrome will start!"); return new ChromeDriver(); } else if (browserName.equals("Firefox") || browserName.equals("firefox") || browserName.equals("FIREFOX")) { System.out.println("Firefox will start!"); return new FirefoxDriver(); } else if (browserName.equals("Edge") || browserName.equals("edge") || browserName.equals("EDGE")) { System.out.println("Edge will start!"); return new EdgeDriver(); } else throw new RuntimeException("Unsupported browser! Will not start any browser!"); }
The first thing we should address when creating a better version of the code is to compare the value of the ‘browserName’ parameter to just one value corresponding to each browser. That means we should compare the value of ‘browserName’, for example, in lower case, to known browser name Strings which are also in lower case.
The second thing we should do is to change the structure of the code, by not using the ‘if-else-if-else-if-…’ construct. Since in this entire construct we are comparing only one parameter value to a set of other values, we can simply use a ‘switch’ statement.
✅ The ‘YES’ variant
The cleaner version of the above code is the following:
public WebDriver startBrowser_yes(String browserName) { switch (browserName.toLowerCase()) { case "chrome": System.out.println("Chrome will start!"); return new ChromeDriver(); case "firefox": System.out.println("Firefox will start!"); return new FirefoxDriver(); case "edge": System.out.println("Edge will start!"); return new EdgeDriver(); default: throw new RuntimeException("Unsupported browser! Will not start any browser!"); } }
Here, you can clearly see what actions will be taken for each browser value. For example, it is easy and quick to find what happens if the browser parameter value corresponds to Chrome. Since each ‘case’ statement has a corresponding ‘return’ statement, this guarantees that we will not initialize more than one browser: once we encounter the required browser, we exit the method by returning a new instance of that browser. Also, it is easy to find out what happens if the parameter value does not correspond to any known value. We can see this in the ‘default’ section.
5. Before or after lunch
Let’s assume for this task that we need to have a String variable, which stores either the text ‘Before Lunch’ or the text ‘After Lunch’. The value it stores should be computed based on the current time: if the current hour is after 12, it is ‘After Lunch’. This is a weird example, but I just want to demonstrate how to use the simplified ‘if’ construct. For this task, instead of a ‘NO’ and a ‘YES’ variant, I will provide 2 ‘MAYBE’ variants. This is because neither variant is bad, it’s just that one is a bit better than the other one.
For both variants, we will need to declare a String variable that stores the text we need (‘Before Lunch’ and ‘After Lunch’). And, we will also need a way to compute the current time, or, more precisely, what hour it currently is. For this, we will use Java’s LocalDateTime:
int currentHour = LocalDateTime.now().getHour();
The code above gives us the current date and time (by calling the ‘now()’ method), and then the current hour is extracted as an ‘int’ (with the help of the ‘getHour()’ method). The ‘currentHour’ variable will store the value of the current hour, with values ranging from 0 to 23.
The ‘MAYBE 1’ variant
The first variant for setting a String value based on the current hour will be the one below:
String momentOfDay1; if (currentHour > 12) momentOfDay1 = "After Lunch"; else momentOfDay1 = "Before Lunch";
Here, the first thing we do is to declare the variable where we will store the lunch based String we need, namely ‘momentOfDay1’. Then, in the ‘if’ block, based on whether the current hour is after or before 12, we will assign the needed String value to the ‘momentOfDay1’ variable. This is pretty straightforward.
The only slight downside to the variant above is that there’s too much code for such a simple task.
The ‘MAYBE 2’ variant
In this variant, instead of 4 lines of code, we will have only 1:
String momentOfDay2 = (currentHour > 12) ? "After Lunch" : "Before Lunch";
This line of code includes everything: declaring the String variable we need, comparing the value of the current hour with 12, and based on the value, assigning the required String to the ‘momentOfDay2’ variable. Here, we are using the simplified ‘if’ block to accomplish this task. The code found between ‘?’ and ‘:’ corresponds to the ‘if’ branch we had before. What is after the ‘:’ represents the ‘else’ branch. As you can see, this variant is short, concise, and easy to read, once you figure out the syntax.
Additional tips
Apart from the above examples, which show how you can rewrite some of those complicated ‘if’ blocks, here are some further tips on how to manage them:
- No empty ‘else’ branches. If you find yourself writing anything like the following code, please reconsider doing it:
if (condition) { ... doSomethingHere() ... } else {}
In this case, since there is no code in the ‘else’ branch, it makes no sense even to write this branch. Therefore, simplify the code by keeping only the ‘if’ branch:
if (condition) { ... doSomethingHere() ... }
- Use ‘AND’ instead of a new ‘if’ branch. Take a look at the following code:
if (condition1) { if (condition2) { ... doSomethingHere() ... } }
In this example, in the first ‘if’ branch, there is no code apart from the second ‘if’ branch. All the code that needs to execute in the first ‘if’ branch is actually the code from the second ‘if’ branch, of course, if ‘condition2’ is fulfilled. Due to this, we can re-write the code above using only one ‘if’ branch, and the ‘AND’ operator (which in Java is ‘&&’):
if (condition1 && condition2) { ... doSomethingHere() ... }
- Don’t repeat the condition. When you have too many nested ‘if’s you might get lost in the myriad of conditions you used for each of the branches. For example:
if (condition1) { ... someCodeHere() ... if (condition2) { ... someOtherCodeHere() ... if (condition3) { ... moreCodeHere() ... if (condition1) { ... evenMoreCodeHere() ... } if (!condition2) { ... aLotMoreCodeHere() ... } } } }
In the above example, in the first ‘if’ branch, ‘condition1’ is evaluated. However in an inner branch, the same condition is evaluated again. This does not make any sense if none of the code from within the initial ‘if’ changes the result of the evaluation of the condition. The same goes for ‘condition2’. Unless some code from within the second ‘if’ branch changes the result of the evaluation of ‘condition2’, the ‘if’ clause where ‘!condition2’ is evaluated will never execute.
Conclusion
In the examples from this post, we can see that sometimes we simply don’t need to use ‘if’ blocks. However, when do, we need to make sure that we don’t exaggerate with how many levels of nesting we will use inside those blocks. Try to keep the code as simple and as clean as possible. It will help you a lot when you will need to perform debugging or simply, when you will need to understand, months later, what the code is supposed to do.