When deciding what data types to use for storing test data, you might want something that:
- Allows the declaration of several properties
- Has no behavior or has minimal behavior
- Allows the easy creation of multiple similar entities
Objects would almost fit these requirements. However, creating several entities would mean creating several Objects that only have a few properties and no or minimal behavior. Minimal behavior translates to a small number of methods. Basically, for each entity you need, you would have to create a new Object. That would just be a waste. Instead, Enum, a specialized type of Object, can be used.
We can use Enums for representing concepts such as: week days, months of the year, browsers, or languages. In this article, I want to exemplify the usage of Enums which have several properties and a constructor to represent countries. You can find the GitHub link to all the code exemplified here at the end of the article. For information on what Enums are, please refer to the official documentation.
Using Java Enums in Testing: Country Example
For this example, let’s consider that in your tests you need to fill in a country specific registration form. Some of the details you need to provide in this form are: the country, a city belonging to that country, and a country specific phone number, all belonging to the client you are filling in the registration form for. The site you are using is available in many countries all over the world.
For the purpose of testing, let’s narrow down the list of countries we will use to: Austria, Estonia and Spain. Each of these countries will have the 3 properties we need to provide to the registration form: the country name, a list of cities, and a phone prefix. Based on this prefix we can generate a test phone number. To represent each country we will use an Enum. The constant values used to represent the countries will be: AT, EE and ES.
We will declare the Enum as follows:
public enum Country {
AT("Austria", Arrays.asList("Vienna", "Salzburg", "Innsbruck"), 43),
EE("Estonia", Arrays.asList("Tallinn", "Haapsalu", "Tartu"), 372),
ES("Spain", Arrays.asList("Malaga","Madrid","Valencia","Corralejo"), 34);
public final String label;
public final List<String> cities;
public int phoneNumberPrefix;
Country(String label, List<String> cities, int phoneNumberPrefix) {
this.label = label;
this.cities = cities;
this.phoneNumberPrefix = phoneNumberPrefix;
}
}
As you can see, first we specify the set of allowed values for the country constants, together with the allowed values for all of their properties. We define the types of these properties by declaring the label, cities and phoneNumberPrefix. These are: a String, a List of Strings and an int.
The constructor is used internally to generate the Enum values. So, for example, ‘AT’ has properties whose order corresponds to the order of the parameters from the constructor: the ‘label‘ property value is ‘Austria’, the List of ‘cities‘ belonging to this country is: “Vienna”, “Salzburg”, “Innsbruck”, and the ‘phoneNumberPrefix‘ is 43.
We can retrieve a property corresponding to an Enum constant as follows: Country.CONSTANT.propertyName. For example: Country.AT.label will give us ‘Austria’. As you can see, the Country properties are static.
On the registration form, selecting the country is done from a dropdown, selecting the city is done from another dropdown, and the phone number is provided by typing in a field.
The Page class created for the country specific information contains the following entries:
@FindBy(css = "#country") private WebElement countryDropdown;
@FindBy(css = "#city") private WebElement cityDropdown;
@FindBy(css = "#phone") public WebElement phoneNumberField;
@FindBy(css = "[type='submit']") public WebElement submitButton;
public Select countrySelect() {
return new Select(countryDropdown);
}
public Select citySelect() {
return new Select(cityDropdown);
}
The countrySelect() method returns a Select referencing the country dropdown. Similarly the citySelect() method returns the Select referencing the city dropdown. The phoneNumberField WebElement will be used for typing the phone number.
In the article, I will omit the parts where the browser is opened, where the webpage opens, and where the browser is closed. You can see all these parts in the GitHub repository I will provide at the end of the article.
Scenario 1: Fill in the registration form for a Spanish customer
Once the browser is open, and the registration page has been accessed from it, the test will need to fill in details for a Spanish customer. We will select the country, then the city of Valencia, and then type a random phone number made up of 10 digits. The country part of the registration form will look like:
So, we will create a new test for this activity:
@Test
void selectCountryCityAndTypePhoneNumber() {
}
First, we will select the country. This means that, from the country dropdown, we will select the value corresponding to the ‘label‘ property of the ‘ES’ Enum entry. This is retrieved easily: Country.ES.label. Selecting the country is done as follows:
page.countrySelect().selectByVisibleText(Country.ES.label);
Next, we need to select the city from the corresponding dropdown. Accessing the List of cities is done: Country.ES.cities. Accessing the element of the List corresponding to Valencia (the third element) is done: Country.ES.cities.get(2). Selecting this value from the city dropdown:
page.citySelect().selectByVisibleText(Country.ES.cities.get(2));
And now, the last step means that the phone number needs to be generated, whose first numbers represent the country prefix. The prefix can be retrieved from the Enum as follows: Country.ES.phoneNumberPrefix. Generating the random number made up of 10 characters: Country.ES.phoneNumberPrefix + randomNumeric(8).
In order to use the ‘randomNumeric’ method, you need to first import the corresponding class from the Apache Commons library as follows:
import static org.apache.commons.lang3.RandomStringUtils.randomNumeric;
If you don’t have this library in your project, you should import it. For a Maven project, you just need to add the following entry in your ‘pom.xml’ file (make sure you are using the latest version):
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
The full test is now:
@Test
void selectCountryCityAndTypePhoneNumber() {
page.countrySelect().selectByVisibleText(Country.ES.label);
page.citySelect().selectByVisibleText(Country.ES.cities.get(2));
page.phoneNumberField.sendKeys(Country.ES.phoneNumberPrefix + randomNumeric(8));
}
Scenario 2: Check available countries
In the second and third test scenarios I will show, we need to check that the country and city dropdowns display only the expected values. The dropdowns from this example work as follows: when they are not open (you did not click on them), the country dropdown shows an empty selection, while the city dropdown is completely empty. The city dropdown at this point is disabled and you cannot select any option from it.
Once you click the country dropdown, you will see the list of available options. We expect this list to have the same values as the ones we have in the Enum specified as the ‘label‘ properties. However, we need to keep in mind that for display purposes, the country dropdown also contains an entry without any text. The requirement said that we don’t want to have any predefined selection in the dropdowns.
Once you select a country, the city dropdown is available for interaction. Depending on the country that you selected, you should only see the cities corresponding to that country in the city dropdown, once you click it. Keep in mind that this dropdown also has the empty text option, for display purposes.
The tests we want to write need to check that all the countries and cities we want and have stored in our Enum are present in their corresponding dropdowns. Keeping also in mind that we have empty entries in each dropdown.
Let’s start with scenario number 2, namely checking whether the country dropdown contains the correct values. We will first build our ‘expected’ content. We know that we have the expected values stored as ‘label‘ parameters in our Enum, but we also need to deal with the empty text option the dropdown displays. Keeping in mind that we will read the values from the webpage with Selenium, and that they are returned as Strings, we can create a List of expected String country values. First, I will create the List and add a first element to it, which is an empty String. This will correspond to the empty String from the country dropdown.
List<String> expectedCountries = new ArrayList<>();
expectedCountries.add("");
Now, we need to add all the ‘label‘ values we have in our Enum. In order to do that, we need to iterate through all the Enum items and add each corresponding ‘label‘ String value to the List of expected Strings. We will iterate through each Enum entry using the ‘Country.values()‘ method.
for (Country country : Country.values()) {
expectedCountries.add(country.label);
}
At this point our List of expected countries contains 4 entries: “”, “Austria”, “Estonia”, “Spain”.
We can now move to reading the country values from the webpage and storing them to a List of ‘actual’ values. Because we are dealing with a ‘Select’, we will need to iterate through all the ‘option’ WebElements belonging to the ‘Select’. We need to apply ‘getText()’ to each ‘option’, and add these resulting Strings to the List of actual Strings.
List<String> actualCountries = new ArrayList<>();
for (WebElement option : page.countrySelect().getOptions()) {
actualCountries.add(option.getText());
}
Before comparing the expected and actual Lists, we need to consider that maybe our Enum labels and the dropdown option values might not be in the same order. So, we should sort them, and then make the comparison.
Collections.sort(expectedCountries);
Collections.sort(actualCountries);
assertEquals(expectedCountries, actualCountries);
The entire test looks like:
@Test
void checkCountries() {
List<String> expectedCountries = new ArrayList<>();
expectedCountries.add("");
for (Country country : Country.values()) {
expectedCountries.add(country.label);
}
List<String> actualCountries = new ArrayList<>();
for (WebElement option : page.countrySelect().getOptions()) {
actualCountries.add(option.getText());
}
Collections.sort(expectedCountries);
Collections.sort(actualCountries);
assertEquals(expectedCountries, actualCountries);
}
Scenario 3: Check available cities
In our previous scenario, we did not need to actually interact with any dropdown. In this scenario, however, we need to check that for each country selected, only the correct cities are displayed in the city dropdown. Because JavaScript is the one that loads the information into the city dropdown after a country is selected from the country dropdown, the test will: select each country, and for each selected country it will check the city dropdown.
Let’s start writing the test by iterating over the available Enum entries:
for (Country country : Country.values()) {
Next, inside the ‘for’ loop, let’s choose from the country dropdown the country corresponding to the current Enum entry’s ‘label‘ property:
page.countrySelect().selectByVisibleText(country.label);
At this point, the city dropdown is populated with, we hope, the values corresponding to the selected country. In order to make sure that the dropdown options are correct, we will first create a List of ‘actual’ city names. We will read these values from the webpage:
List<String> actualCities = new ArrayList<>();
for (WebElement option : page.citySelect().getOptions()) {
actualCities.add(option.getText());
}
We must also create the List of expected city names. In the Enum these are stored as the ‘cities‘ List of String properties. We will create the List of expected values by first adding an empty String to the List. Then we will use the ‘addAll()’ method to add at once all the items from the ‘cities‘ List.
List<String> expectedCities = new ArrayList<>();
expectedCities.add(0, "");
expectedCities.addAll(country.cities);
The only thing remaining now is to compare the Lists of actual and expected values. Of course, after sorting both these Lists.
Collections.sort(expectedCities);
Collections.sort(actualCities);
assertEquals(expectedCities, actualCities);
So, for this scenario, while iterating through each country, we are checking the city List corresponding to that country. The Lists of expected and actual cities are created from scratch for each country. They contain only information corresponding to that country. The entire test now looks like:
@Test
void checkCities() {
for (Country country : Country.values()) {
page.countrySelect().selectByVisibleText(country.label);
List<String> actualCities = new ArrayList<>();
for (WebElement option : page.citySelect().getOptions()) {
actualCities.add(option.getText());
}
List<String> expectedCities = new ArrayList<>();
expectedCities.add(0, "");
expectedCities.addAll(country.cities);
Collections.sort(expectedCities);
Collections.sort(actualCities);
assertEquals(expectedCities, actualCities);
}
}
Conclusion
I hope you found these examples useful and that they will inspire you to use Enums in testing, where such a construct makes sense. You can take a look at the entire code, in my GitHub project repository:
- The HTML can be found here
- You can find Page class here
- The Enum can be found here
- and the test can be found here
Happy Testing! 😉