Java's built in Function interface allows me to avoid using getters. Instead of hardcoding each property in a separate field, I use a TreeMap to hold all the properties as property name and property value. A second TreeMap links properties to scraping methods. The data type is String but I could also use a TreeMap as the value type within the first Treemap, which would allow me to have repeating field such as ingredients or detail items.
I use the iterable map TreeMap to store my properties as key-value pairs. (The property's name is the key, stored here as a string. Keys must be unique within the map.) I am out of the business of writing a getter for each property and instead I am loading up a map; the record can have any number of fields.
Map<String, String> expectedMap = new TreeMap<>();
Map<String, String> actualMap = new TreeMap<>();
I load the map by specifying the key and the value.
map.put("someKey1", "expectedValue1");
map.put("someKey2", "expectedValue2");
// etc...
You mentioned that you are storing your data in a separate class. Your separate class can be modified to have a TreeMap field rather than individual fields.
The next step is to create another TreeMap list, which is a list of the scraping methods I want to use for my test. Java allows you to store a method reference as a variable.
Map<String, Function<Page, String>> functionList = new TreeMap<>();
Now, for each test, I create a list of all scrape methods that I want to use to get my data. The method signature of the scrape method needs to match that of the Function<Page, String>
interface: it's void and returns a string.
functionList.put("someKey1", (Page page)-> page.getKey1());
functionList.put("someKey2", (Page page)-> page.getKey2());
I loop over my function list to load up my data TreeMap. It extracts the value from Selenium using the page method and then adds it to the expected values map using the same key as was used to identify the function. So my long list of calls to page methods is replaced with this line of code.
for (TreeMap.Entry<String, Function<Page, String>> entry : functionList) {
expectedMap.put(entry.key(), entry.value().apply())
}
After this prep the body of my tests can look like this.
//arrange
// to avoid repetition these load sequences can be looped over
// an array, a CSV,
// stored in my data class, pulled from a database, etc.
expectedMap.put("someKey1", "expectedValue1");
expectedMap.put("someKey2", "expectedValue2");
// this can be loaded elsewhere from a shared class.
functionList.put("someKey1", (Page page)-> page.getKey1());
functionList.put("someKey2", (Page page)-> page.getKey2());
//act
for (TreeMap.Entry<String, Function<Page, String>> entry : functionList) {
actualMap.put(entry.key(), entry.value().apply())
}
//assert
// Note that equals() must be properly overridden here.
if (!expectedMap.equals(actualMap)) throw new AssertionError;
If a String won't do it for me then I can plug in my own class, as long as I reference it elsewhere when declaring the TreeMap:
Map<String, MyClass> expectedMap = new TreeMap<String, MyClass>();
If I use an array as my data type, I am aware that this will break the essential equals() method on the TreeMap, and I'll need to write my own code to verify the content is the same.
Here's the source I tested to proof this explanation. This test fails; if I want the test to pass, I change "expectedValue0"
to "expectedValue2"
.
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Function;
/**
*
* @author apdo
*/
public class AbstractedData {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
TreeMap<String, String> expectedMap = new TreeMap<>();
TreeMap<String, String> actualMap = new TreeMap<>();
TreeMap<String, Function<Page,String>> functionList = new TreeMap<>();
expectedMap.put("someKey1", "expectedValue1");
expectedMap.put("someKey2", "expectedValue2");
functionList.put("someKey1", (Page page)-> page.getKey1());
functionList.put("someKey2", (Page page)-> page.getKey2());
Page page = new Page();
// act - Loop over the function list, looking up
// corresponding values by key name from the actualMap
for (Map.Entry<String, Function<Page, String>> entry : functionList.entrySet()) {
actualMap.put(entry.getKey(), functionList.get(entry.getKey()).apply(page));
}
//assert - TreeMap handles the looping and comparison using equals()
if (!expectedMap.equals(actualMap)) throw new AssertionError("Assertion Failed");
}
public static class Page {
String getKey1() { return "expectedValue1"; }
String getKey2() { return "expectedValue0"; }
}
}