Calling a method with a variable name - possible? - java

I have a function for a Selenium Test that looks like this.
public static WebElement getElmObject (String locinfo, String loctype) {
try{
return driver.findElement(By.loctype(locinfo));
} catch (Throwable t){
return null;
}
The function is supposed to take in the info string and the type (the name of the method to call in the BY class - like xpath, cssselector, tagname etc.) How do I get Java to evaluate the value of "loctype"?
I come from a ColdFusion background and this is easy to do with CF but I am having a hard time trying to do this in Java. I just get a "cannot resolve method" issue and it won't compile. Is it even possible to do?

You can do this using Reflection.
public static WebElement getElmObject(String locinfo, String loctype) {
try {
Method method = By.class.getMethod(loctype, String.class);
By by = (By) method.invoke(By.class, locinfo);
return driver.findElement(by);
} catch (Throwable t) {
return null;
}
}
However I find this strange and I would recommend using different methods (getElmObjectById, getElmObjectByCss, etc.) or to use an enum (ID, CSS, XPATH, etc.) as parameter instead of the method name. Using the method name as parameter, it makes your caller dependent of the Selenium implementation. If they change the name of a method, your code will not work anymore and you will even not notice this at compile time!

we can also do it with enum like this
other than creating seperate methods for each and every locator like getElmObjectById as LaurentG said we can also achieve it as shown below
public enum avilableLocators
{
CLASS_NAME, CSS_SELECTOR, XPATH
}
and have a method with switch case or if-else if which will have a return type of By
public By locinfo(String locinfo)
{
String locatorValue=null;
switch (locType(locinfo))
{
case XPATH:
locatorValue=locinfo.split(",")[1]/*assuming that you are passing locinfo,locvalue*/
return By.xpath(locator);
}
}
public final avilableLocators locType(String loctype) {
if (loctype.contains("xpath"))
{
return avilableLocators.XPATH;
}
}
so the final usage can be like this
String locDetails="xpath,//*[#id='ComScorePingFile']"
locinfo(locDetails);

Related

Invoke Java object constant using a variable

I'm very new to Java so it makes it hard for me to explain what I'm trying to do.
I have an abstract class that invokes several object constants like this:
public abstract class Enchantment implements Keyed {
/**
* Provides protection against environmental damage
*/
public static final Enchantment PROTECTION_ENVIRONMENTAL = new EnchantmentWrapper("protection");
In a different file I can access this perfectly fine with Enchantment value = Enchantment.PROTECTION_ENVIRONMENTAL;
However, I'm trying to use a string variable for this instead. Something like this:
String str = "PROTECTION_ENVIRONMENTAL";
Enchantment value = Enchantment.str;
Obviously that won't work. So I did a bunch of research and learned I need to use reflection for this. Using this source code's docs I figured I was looking for field data. So I tried both:
Field fld = Enchantment.class.getField("PROTECTION_ENVIRONMENTAL");
Field fld = Enchantment.class.getDeclaredField("PROTECTION_ENVIRONMENTAL");
But these returned me a NoSuchFieldException. As I was on it, I've tried both getMethod() and getDeclaredMethod() just as well equally with no luck.
I'm now at the point that these are probably "object constants"? I'm not sure how to call them. But I'm definitely at a loss on how to get this to work now and after everything I've tried myself, I figured it was time to ask for some help here.
That one comment is spot on: you absolutely do not use reflection here.
There are only two valid reasons to use reflection:
you are creating a framework that has to deal with classes it doesn't know about
you have for some other reason to deal with classes you don't know about at compile time
But your code perfectly knows about that Enchantment class, its capabilities, and so on. Therefore reflection is the wrong approach. You figured it yourself: it is damn hard to get right, and damn right to get it wrong in some subtle ways. And when you get it wrong, it always blows up at runtime. Reflection code compiling means nothing. It always waits for you to run it to throw up in your face.
So to answer your question by not answering it: use a Map. Like:
Map<String, Enchantment> enchantmentsByConstantName = new HashMap<>();
enchantmentsByConstantName.put("PROTECTION_ENVIRONMENTAL", PROTECTION_ENVIRONMENTAL);
Alternatively, these constants could go into an enum, as outlined in the other answer, but in a sightly different way:
enum EnchantmentHolder {
PROTECTION_ENVIRONMENTAL(new EnchantmentWrapper("protection")),
ANOTHER_ENCHANTMENT(...)
A_THIRD_ENCHANTMENT(...)
...;
private Enchantment enchantment;
private EnchantmentHolder(Enchantment enchantment) {
this.entchantment = entchantment;
}
public Enchantment getEntchantment() { return entchantment; }
You may want to look into enumerations if you know they're going to be constant values;
public enum Enchantment {
PROTECTION_ENVIRONMENTAL {
public void cast() {
// do enum-specific stuff here
}
},
ANOTHER_ENCHANTMENT {
public void cast() {
// do enum-specific stuff here
}
},
A_THIRD_ENCHANTMENT{
public void cast() {
// do enum-specific stuff here
}
};
public abstract void cast();
}
enums can be treated like classes and have methods and properties. You can also convert to and from strings Enchantment.valueOf("PROTECTION_ENVIRONMENTAL") but that's generally if you are reading from a configuration file - in code you'd reference the value directly.
Once you have the Field, you need to call Field.get(Object) with an instance (in this case the class). Something like,
Class<?> cls = Enchantment.class;
try {
Field f = cls.getField("PROTECTION_ENVIRONMENTAL");
System.out.println(f.get(cls));
} catch (Exception e) {
e.printStackTrace();
}
Since you want the Enchantment, you could then test that the instance you get is assignable to Enchantment. Something like,
Class<? extends Enchantment> cls = Enchantment.class;
try {
Field f = cls.getField("PROTECTION_ENVIRONMENTAL");
Object obj = f.get(cls);
if (cls.isAssignableFrom(obj.getClass())) {
Enchantment e = cls.cast(obj);
System.out.println(e);
}
} catch (Exception e) {
e.printStackTrace();
}
But the enum approach is better.

Custom annotation implementation for WebElements name in test report

I created test framework with Selenium and setup ExtentReports for test report. Used page object model and #FindBy annotation for fields to create own store of WebElements for each page. Now I would like to create custom annotation #Name
#Name(description = "google main page")
#FindBy(linkText = "Gmail")
private WebElement gmail;
And implementation for it, to be able to use description of each WebElement later in my report. I have my own implementation of click() method
public static void click(WebElement element) {
try{
element.click();
TestReport.addLog(LogStatus.INFO, "Element "+NameImpl.getDescription(element)+" clicked");
} catch (NoSuchElementException e) {
TestReport.addLog(LogStatus.ERROR, "Element "+NameImpl.getDescription(element)+" not found");
}
}
I'm able to get description of all elements annotated in class with reflection like here
Is it possible to read the value of a annotation in java?
but cannot get description of specific element used in my click method.
Any ideas how to achieve that?
Just from the parameter passed to the click method, there's no way the get annotations. This reason is the annotation are on the gmail field, not on the WebElement class. So the only way to get the #Name annotation is to first get the Field representing your gmail field, and that will have to be done through the declaring class:
ClassWithGmailField.class.getField("gmail").getAnnotation(Name.class).description()
Just from the parameter of the click method, you could only reach annotations defined on the WebElement class itself e.g.:
#SomeAnnotation
public class WebElement {...}
but this is not useful for anything in your case.
To achieve something similar to what you want, you could potentially:
Reflectively analyze the class, extract all #Name'd fields and collect the meta together with the field values, perhaps into some kind of wrapper e.g. NamedElement that would have the description from #Name and the WebElement itself
Reflectively call the click method providing it with the meta it needs (the description in your case). But for this you'd need to somehow know which method to invoke for each field (e.g. by yet another annotation), making your logic encoded external to your actual code. Might make sense in some cases but probably a bad idea in general.
A quick (uncompiled, untested) code example of the first idea:
public class NamedElement extends WebElement {
public String description;
public WebElement element;
public NamedElement(String description, WebElement element) {
this.description = description;
this.element = element;
}
}
public class NamedElementExtractor {
public static Collection<NamedElement> getNamedElements(Object instanceWithWebElements) {
//instanceWithElements in your case would be an instance of the class that has the "gmail" field, i.e. the one I referred to as ClassWithGmailField above
Collection<NamedElement> namedElements = new List<NamedElement>();
for (Field field : instanceWithWebElements.getClass().getDeclaredFields()) {
field.setAccessible(true);
//maybe first check field.isAnnotationPresent(Name.class)
String desc = field.getAnnotation(Name.class).description();
WebElement element = field.getValue(instanceWithWebElements);
namedElements.add(new NamedElement(desc, element));
}
}
}
...
for (NamedElement namedElement : NamedElementExtractor.getNamedElements(instanceWithWebElements))) {
Click.click(namedElement);
}
...
public static void click(NamedElement namedElement) {
try{
namedElement.element.click();
TestReport.addLog(LogStatus.INFO, "Element "+ namedElement.description +" clicked");
} catch (NoSuchElementException e) {
TestReport.addLog(LogStatus.ERROR, "Element "+ namedElement.description +" not found");
}
}
No idea if this is appropriate/usable in your case, but it's food for thought.

Java's alternative to a C# finding text in a page using particular tag

I am struggling with a piece of code in C# that I am not sure how to change to Java:
This is the calling method:
Assert.Istrue(DashboardPage.IsAt, "Failed to login);"
This is the Dashboard class with the functionality. This is the C# that I want to convert to Java:
public class DashboardPage {
public static bool isAt {
get {
var h2 = Driver.driver().FindElements(By.TagName("h2"));
if (h2.Count>0)
return h2[0].Text =="Dashboard";
return false;
}
}
}
Because I was not able to create the above functionality in Java, I did an alternative as below.But as you can see this checks the whole page source and not for particular tag.
public static boolean isTextPresent(String text) {
try {
boolean b = Driver.driver().getPageSource().contains(text);
return b;
}
catch (Exception e) {
return false;
}
}
I guess your code should be somewhat like this:
public static boolean isTextPresent(WebDriver driver,String text){
try{
boolean b = driver.findElement(By.tagName("h2")).getText().contains(text);
return b;
}
catch(Exception e){
return false;
}
}
Above example will find the first available h2 tag in the DOM.
If you want check your text across all h2 tags, you should use findElements():
List<WebElement> tags = driver.findElements(By.tagName("h2"));
for(WebElement e:tags){
if(e.getText().contains(text))
return true;
}
Note that, here I'm passing WebDriver as an additional argument and assuming that you have declared and defined WebDriver already in the class from which you are calling (something like WebDriver driver=new FirefoxDriver()). This will allow the function to use the same instance.
Also, I have used contains() here , this will check the substring. If you want the exact match use equals() instead.

A point of style with try/catch

I was wondering if this approach was correct :
public ITask getState()
{
statePredicate[Some predicate definition];
ITask nextRunnable = null;
try {
nextRunnable = Iterables.find((Iterable)queue, statePredicate);
}
catch (NoSuchElementException e)
{}
return nextRunnable;
}
The points on which I am wondering are :
should the predicate be cached as a member of the class ?
I do nothing with the catch, I do not even log it because it is
normal for my app to not find anything.
t return null because I do a final return.
Thank you for your input !
-
1) If the predicate is always the same, I would make it a static final class member.
2) There is also a version of Iterables.find that you can specify a default value to (assuming you're using Google Guava). Then you don't need to deal with the NoSuchElementException at all.
3) Is there a reason to cast queue to Iterable? If this is not necessary, then don't cast.
class MyClass {
private static final Predicate STATE_PREDICATE = new Predicate<ITask>() {
#Override
public boolean apply(ITask input) {
// ... your code here
}
};
public ITask getState() {
return Iterables.find(queue, STATE_PREDICATE, null);
}
}
If the exception is really the usual case in your approach than you should put at least a comment into the catch area to make clear for everyone who reads the code that it was intentional and not a mistake. In my opinion returning Null is something different, but it some circumstanced not avoidable.

Letting the code try different things until it succeeds, neatly

This is the second time I found myself writing this kind of code, and decided that there must be a more readable way to accomplish this:
My code tries to figure something out, that's not exactly well defined, or there are many ways to accomplish it. I want my code to try out several ways to figure it out, until it succeeds, or it runs out of strategies. But I haven't found a way to make this neat and readable.
My particular case: I need to find a particular type of method from an interface. It can be annotated for explicitness, but it can also be the only suitable method around (per its arguments).
So, my code currently reads like so:
Method candidateMethod = getMethodByAnnotation(clazz);
if (candidateMethod == null) {
candidateMethod = getMethodByBeingOnlyMethod(clazz);
}
if (candidateMethod == null) {
candidateMethod = getMethodByBeingOnlySuitableMethod(clazz);
}
if (candidateMethod == null) {
throw new NoSuitableMethodFoundException(clazz);
}
There must be a better way…
Edit: The methods return a method if found, null otherwise. I could switch that to try/catch logic, but that hardly makes it more readable.
Edit2: Unfortunately, I can accept only one answer :(
To me it is readable and understandable. I'd simply extract the ugly part of the code to a separate method (following some basic principles from "Robert C.Martin: Clean Code") and add some javadoc (and apologies, if necessary) like that:
//...
try {
Method method = MethodFinder.findMethodIn(clazz);
catch (NoSuitableMethodException oops) {
// handle exception
}
and later on in MethodFinder.java
/**
* Will find the most suitable method in the given class or throw an exception if
* no such method exists (...)
*/
public static Method findMethodIn(Class<?> clazz) throws NoSuitableMethodException {
// all your effort to get a method is hidden here,
// protected with unit tests and no need for anyone to read it
// in order to understand the 'main' part of the algorithm.
}
I think for a small set of methods what you're doing is fine.
For a larger set, I might be inclined to build a Chain of Responsibility, which captures the base concept of trying a sequence of things until one works.
I don't think that this is such a bad way of doing it. It is a bit verbose, but it clearly conveys what you are doing, and is easy to change.
Still, if you want to make it more concise, you can wrap the methods getMethod* into a class which implements an interface ("IMethodFinder") or similar:
public interface IMethodFinder{
public Method findMethod(...);
}
Then you can create instances of you class, put them into a collection and loop over it:
...
Method candidateMethod;
findLoop:
for (IMethodFinder mf: myMethodFinders){
candidateMethod = mf.findMethod(clazz);
if (candidateMethod!=null){
break findLoop;
}
}
if (candidateMethod!=null){
// method found
} else {
// not found :-(
}
While arguably somewhat more complicated, this will be easier to handle if you e.g. need to do more work between calling the findMethods* methods (such as more verification that the method is appropriate), or if the list of ways to find methods is configurable at runtime...
Still, your approach is probably OK as well.
I'm sorry to say, but the method you use seems to be the widely accepted one. I see a lot of code like that in the code base of large libraries like Spring, Maven etc.
However, an alternative would be to introduce a helper interface that can convert from a given input to a given output. Something like this:
public interface Converter<I, O> {
boolean canConvert(I input);
O convert(I input);
}
and a helper method
public static <I, O> O getDataFromConverters(
final I input,
final Converter<I, O>... converters
){
O result = null;
for(final Converter<I, O> converter : converters){
if(converter.canConvert(input)){
result = converter.convert(input);
break;
}
}
return result;
}
So then you could write reusable converters that implement your logic. Each of the converters would have to implement the canConvert(input) method to decide whether it's conversion routines will be used.
Actually: what your request reminds me of is the Try.these(a,b,c) method in Prototype (Javascript).
Usage example for your case:
Let's say you have some beans that have validation methods. There are several strategies to find these validation methods. First we'll check whether this annotation is present on the type:
// retention, target etc. stripped
public #interface ValidationMethod {
String value();
}
Then we'll check whether there's a method called "validate". To make things easier I assume, that all methods define a single parameter of type Object. You may choose a different pattern. Anyway, here's sample code:
// converter using the annotation
public static final class ValidationMethodAnnotationConverter implements
Converter<Class<?>, Method>{
#Override
public boolean canConvert(final Class<?> input){
return input.isAnnotationPresent(ValidationMethod.class);
}
#Override
public Method convert(final Class<?> input){
final String methodName =
input.getAnnotation(ValidationMethod.class).value();
try{
return input.getDeclaredMethod(methodName, Object.class);
} catch(final Exception e){
throw new IllegalStateException(e);
}
}
}
// converter using the method name convention
public static class MethodNameConventionConverter implements
Converter<Class<?>, Method>{
private static final String METHOD_NAME = "validate";
#Override
public boolean canConvert(final Class<?> input){
return findMethod(input) != null;
}
private Method findMethod(final Class<?> input){
try{
return input.getDeclaredMethod(METHOD_NAME, Object.class);
} catch(final SecurityException e){
throw new IllegalStateException(e);
} catch(final NoSuchMethodException e){
return null;
}
}
#Override
public Method convert(final Class<?> input){
return findMethod(input);
}
}
// find the validation method on a class using the two above converters
public static Method findValidationMethod(final Class<?> beanClass){
return getDataFromConverters(beanClass,
new ValidationMethodAnnotationConverter(),
new MethodNameConventionConverter()
);
}
// example bean class with validation method found by annotation
#ValidationMethod("doValidate")
public class BeanA{
public void doValidate(final Object input){
}
}
// example bean class with validation method found by convention
public class BeanB{
public void validate(final Object input){
}
}
You may use Decorator Design Pattern to accomplish different ways of finding out how to find something.
public interface FindMethod
{
public Method get(Class clazz);
}
public class FindMethodByAnnotation implements FindMethod
{
private final FindMethod findMethod;
public FindMethodByAnnotation(FindMethod findMethod)
{
this.findMethod = findMethod;
}
private Method findByAnnotation(Class clazz)
{
return getMethodByAnnotation(clazz);
}
public Method get(Class clazz)
{
Method r = null == findMethod ? null : findMethod.get(clazz);
return r == null ? findByAnnotation(clazz) : r;
}
}
public class FindMethodByOnlyMethod implements FindMethod
{
private final FindMethod findMethod;
public FindMethodByOnlyMethod(FindMethod findMethod)
{
this.findMethod = findMethod;
}
private Method findByOnlyMethod(Class clazz)
{
return getMethodOnlyMethod(clazz);
}
public Method get(Class clazz)
{
Method r = null == findMethod ? null : findMethod.get(clazz);
return r == null ? findByOnlyMethod(clazz) : r;
}
}
Usage is quite simple
FindMethod finder = new FindMethodByOnlyMethod(new FindMethodByAnnotation(null));
finder.get(clazz);
... I could switch that to try/catch logic, but that hardly makes it more readable.
Changing the signature of the get... methods so you can use try / catch would be a really bad idea. Exceptions are expensive and should only be used for "exceptional" conditions. And as you say, the code would be less readable.
What is bothering you is the repeating pattern used for flow control--and it should bother you--but there isn't too much to be done about it in Java.
I get really annoyed at repeated code & patterns like this, so for me it would probably be worth it to extract the repeated copy & paste control code and put it in it's own method:
public Method findMethod(Class clazz)
int i=0;
Method candidateMethod = null;
while(candidateMethod == null) {
switch(i++) {
case 0:
candidateMethod = getMethodByAnnotation(clazz);
break;
case 1:
candidateMethod = getMethodByBeingOnlyMethod(clazz);
break;
case 2:
candidateMethod = getMethodByBeingOnlySuitableMethod(clazz);
break;
default:
throw new NoSuitableMethodFoundException(clazz);
}
return clazz;
}
Which has the disadvantage of being unconventional and possibly more verbose, but the advantage of not having as much repeated code (less typos) and reads easier because of there being a little less clutter in the "Meat".
Besides, once the logic has been extracted into it's own class, verbose doesn't matter at all, it's clarity for reading/editing and for me this gives that (once you understand what the while loop is doing)
I do have this nasty desire to do this:
case 0: candidateMethod = getMethodByAnnotation(clazz); break;
case 1: candidateMethod = getMethodByBeingOnlyMethod(clazz); break;
case 2: candidateMethod = getMethodByBeingOnlySuitableMethod(clazz); break;
default: throw new NoSuitableMethodFoundException(clazz);
To highlight what's actually being done (in order), but in Java this is completely unacceptable--you'd actually find it common or preferred in some other languages.
PS. This would be downright elegant (damn I hate that word) in groovy:
actualMethod = getMethodByAnnotation(clazz) ?:
getMethodByBeingOnlyMethod(clazz) ?:
getMethodByBeingOnlySuitableMethod(clazz) ?:
throw new NoSuitableMethodFoundException(clazz) ;
The elvis operator rules. Note, the last line may not actually work, but it would be a trivial patch if it doesn't.

Categories