【问题标题】:Java: call a method with name stored in variableJava:调用名称存储在变量中的方法
【发布时间】:2018-02-08 02:28:55
【问题描述】:

我有这样的要求:

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`.

那么,有没有可能是这样的:

driver.findElement(By.id("id1")).<something to call click()>

我已经看过 Java 中的反射,但根据我的要求,这看起来很复杂。任何指针都会有所帮助!

【问题讨论】:

    标签: java selenium


    【解决方案1】:

    您的变量代表您想要对 Web 元素执行的操作(在本例中,单击它)。

    适合的类型不是String。请改用Consumer&lt;WebElement&gt;(或driver.findElement() 返回的任何类型):

    Consumer<WebElement> command = e -> e.click();
    
    // ...
    
    command.accept(driver.findElement(By.id("id1")));
    

    这是类型安全的、高效的、可重构的,并且比反射更灵活(因为您的消费者可以对元素做任何想做的事情,不限于没有任何参数的单个方法调用。例如,输入一些文本在文本字段中)

    【讨论】:

    • 好点,可以有一个Map&lt;String, Consumer&lt;WebElement&gt; 来通过命令名称获取所需的消费者
    • 嗨 JB:所以,按照你的说法,我必须在分配Consumer&lt;WebElement&gt; 时提及click 吗?如果我想调用像cleargetLocation 等命令,那么我必须再次定义像Consumer&lt;WebElement&gt; command = e -&gt; e.clear(); 这样的命令我是对的,还是我遗漏了什么?
    • 是的。就像您必须将 String command = "click"; 更改为 String command = "clear"; 一样。除非您确保实际使用存在的方法,否则您的 IDE 将提供自动完成功能,您可以传递参数(即Consumer&lt;WebElement&gt; command = e -&gt; e.sendKeys("hello");)。
    • 关于第一部分:然后将您的命令解析为消费者:例如,“click”被解析为e -&gt; e.click(),“text_hello”被解析为e -&gt; e.sendKeys("hello")。关于第二部分,使用包含 ConsumerBy 实例的对象,允许找到要消费的元素。
    • 在某些情况下,这甚至可以通过使用方法引用来缩短一点,例如消费者 命令 = WebElement::click;
    【解决方案2】:

    最简单的方法是使用反射:

    String command = "click";
    WebElement element = driver.findElement(By.id("id1"));
    
    Method method = WebElement.class.getMethod(command);
    method.invoke(element);
    

    如果你还想通过反射调用By.id,那么你可以这样做:

    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);
    

    【讨论】:

    • 我真的很抱歉,因为我在同一个问题中提出了另一个问题。如果我的id 也存储在一个变量中怎么办。例如,一个包含idxpath 的变量,我必须据此采取行动:driver.findElement(By.&lt;coming from variable value&gt;("id1")).&lt;something to call click()&gt; 在这种情况下,如何扩展您的解决方案?
    • @OmSao 我稍微更改了代码并为您的第二个问题添加了答案。请告诉我这段代码是否有效,因为我还没有测试过。
    【解决方案3】:

    在设计方面(可能这肯定可以进一步优化和抽象),您可以定义一个Enum,让我们将其命名为Action

    public enum Action {
     CLICK,
     SENDKEY,
     etc
    }
    

    然后在您的代码中执行:

    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;
    }
    

    【讨论】:

      【解决方案4】:

      似乎您正在尝试构建一个关键字驱动的框架。老实说,听到任何成功的故事,我都会感到非常惊讶。一直想知道这种框架的真正目的是什么?谁会用它?经理、人工 QA 还是利益相关者?对我来说,让非技术人员参与测试自动化活动是没有意义的。任何自动化都需要良好的技术技能(包括编程和 devops)。否则失败的概率很高。

      无论如何,您的问题更多是关于 stringsfunctional interfaces 映射,或者非常聪明的反射使用。但主要问题是您为此选择了错误的 API [输入]。以下行:

      driver.findElement(locator).doSmth();
      

      是正确的失败方式,因为findElement 使用隐式等待。当你遇到NoSuchElementException / StaleElementReferenceException 时,我 100% 确信你会开始全局重构/修改实施的方法。

      常识建议对ExpectedConditions 使用流畅的等待。但这会使您的任务更加复杂,因为除了定位器和操作之外,您还必须考虑条件,这些条件应该由用户提供。

      从技术角度来看,我会首先创建通用包装器来封装低级 WebDriver API 调用。与原始调用相比,映射或反映此类函数会容易得多。例如。预期条件可以隐藏在枚举级别:

      @Getter
      @RequiredArgsConstructor
      public enum WaitCondition {
      
          visible(ExpectedConditions::visibilityOfElementLocated),
          enabled(ExpectedConditions::elementToBeClickable);
      
          private final Function<By, ExpectedCondition<WebElement>> type;
      }
      

      通过调用例如可以很容易地检索所需的常量。 WaitCondition.valueOf("visible"),输入字符串可以从外部传入。

      通用 API 包装器可能如下所示:

      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));
      }
      

      此线程中的其他人已经提供了映射/反射示例。请注意:如果您更喜欢反射,我建议您查看 jOOR 库,它可以简化此过程。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-12-06
        • 2016-08-27
        • 2015-10-28
        • 1970-01-01
        • 2012-05-17
        • 1970-01-01
        • 2016-01-28
        相关资源
        最近更新 更多