J'ai une exigence telle que:
String command = "click"; // this can have value such as clear, getLocation, getSize, getTagName etc.
WebDriver driver = new ChromeDriver(options); //creating a webdriver object
driver.findElement(By.id("id1")).click(); //Here I want "click" method should be called dynamically as per what I have stored in variable `command`.
Alors, y a-t-il quelque chose de possible comme:
driver.findElement(By.id("id1")).<something to call click()>
J'ai déjà examiné Reflection en Java, mais cela me semblait complexe selon mes besoins. Tous les pointeurs seront utiles!
La manière la plus simple de faire est d’utiliser la réflexion:
String command = "click";
WebElement element = driver.findElement(By.id("id1"));
Method method = WebElement.class.getMethod(command);
method.invoke(element);
Si vous souhaitez également appeler By.id
avec réflexion, procédez comme suit:
String command = "click";
String id = "id";
Method byMethod = By.class.getMethod(id, String.class);
WebElement element = driver.findElement((By) byMethod.invoke(null, "id1"));
Method method = WebElement.class.getMethod(command);
method.invoke(element);
Votre variable représente quelque chose que vous voulez faire avec un élément Web (dans ce cas, cliquez dessus).
Le type approprié pour cela n'est pas String
. Utilisez un Consumer<WebElement>
à la place (ou quel que soit le type de ce que driver.findElement()
renvoie est):
Consumer<WebElement> command = e -> e.click();
// ...
command.accept(driver.findElement(By.id("id1")));
Ceci est sûr en termes de type, efficace, refactorable et beaucoup plus souple que la réflexion (puisque votre consommateur peut faire ce qu’il veut avec l’élément, sans se limiter à un seul appel de méthode sans aucun argument. champ)
En termes de conception (et cela peut sans doute être optimisé et résumé à l’avantage), vous pouvez probablement définir un Enum
, appelons-le Action
ainsi:
public enum Action {
CLICK,
SENDKEY,
etc
}
Dans votre code alors faites:
Action action = <input>;
// find the element
WebElement element = driver.findElement(By.id("id1"));
switch(action) {
case CLICK:
element.click();
break;
case SENDKEY:
element.sendKey();
break;
...
default:
System.out.println("Undefined action");
break;
}
On dirait que vous essayez de créer un cadre basé sur des mots clés. Pour être honnête, je serais très surpris d'entendre toute histoire réussie à ce sujet. Vous vous demandiez toujours quel était le véritable objectif de ce type de cadre? Qui va l'utiliser? Gestionnaires, QA manuels ou parties prenantes? Pour moi, cela n’a aucun sens d’impliquer des non-techniciens dans les activités d’automatisation des tests. Toute automatisation nécessite de bonnes compétences techniques (y compris en programmation et en développement). Sinon, la probabilité d'échec est assez élevée.
Quoi qu'il en soit, votre question concerne davantage le mappage strings
vs functional interfaces
ou l'utilisation très réfléchie de la réflexion. Mais le problème principal est que vous avez choisi de mauvaises API [entrées] pour cela. La ligne suivante:
driver.findElement(locator).doSmth();
est un bon moyen d’échouer, car findElement
utilise des attentes implicites. Et je suis sûr à 100% que vous lancerez l'approche globale de refactorisation/révision mise en œuvre, lorsque vous serez confronté à NoSuchElementException
/StaleElementReferenceException
.
Le bon sens suggère d’utiliser des attentes fluides avec ExpectedConditions
. Mais cela rendra votre tâche encore plus compliquée, car en plus des localisateurs et des actions, vous devez penser aux conditions, qui devraient être fournies par les utilisateurs.
D'un point de vue technique, je créerais d'abord des wrappers communs pour encapsuler les appels d'API WebDriver de bas niveau. Il serait beaucoup plus facile de mapper ou de refléter ces fonctions par rapport aux appels bruts. Par exemple. les conditions attendues peuvent être masquées au niveau enum:
@Getter
@RequiredArgsConstructor
public enum WaitCondition {
visible(ExpectedConditions::visibilityOfElementLocated),
enabled(ExpectedConditions::elementToBeClickable);
private final Function<By, ExpectedCondition<WebElement>> type;
}
Il sera facile de récupérer la constante requise en appelant, par exemple, WaitCondition.valueOf("visible")
, où la chaîne d'entrée peut être passée de l'extérieur.
Le wrapper de l'API commune peut alors ressembler à ceci:
protected void click(By locator) {
click(locator, enabled);
}
protected void click(By locator, WaitCondition condition) {
waitFor(locator, condition).click();
}
private WebElement waitFor(By locator, WaitCondition condition) {
return wait.until(condition.getType().apply(locator));
}
Des exemples de mappage/réflexion ont déjà été fournis par d'autres dans ce fil. Juste une remarque: si vous préférez la réflexion, je vous recommanderais de regarder jOOR library, ce qui simplifie ce processus.