【问题标题】:How to search for an element by custom HTML attribute using Coded UI Tests如何使用编码的 UI 测试通过自定义 HTML 属性搜索元素
【发布时间】:2018-08-06 20:21:35
【问题描述】:

我在 GridView 中有一堆 input[type=submit] 按钮。这些按钮的ids 和names 虽然可以预测,因为它们使用索引号,但不太适合使用 SpecFlow 和编码的 UI 测试进行自动化。我发现很难搜索这些按钮元素。

传递给浏览器的 HTML 的 sn-p:

<input type="submit" id="abc_xyz_0" data-task-id="123" value="Apply">
<input type="submit" id="qrs_tuv_0" data-task-id="345" value="Renew">

每一行的按钮文本都是通用的(“应用”和“更新”),但data-task-id 属性是唯一的。我想使用此属性和值来标识要单击的按钮。我正在尝试使用SearchPropertiesFilterProperties,但我不断收到异常:

System.NotSupportedException:此控件不支持属性 DataTaskId。

我如何尝试找到控件:

HtmlInputButton button = new HtmlInputButton(document);

button.SearchProperties["data-task-id"] = "123";
// or button.SearchProperties["DataTaskId"] = "123";

一些额外的细节:

  • Windows 7
  • Internet Explorer 11
  • localhost 发送的网站并在 Internet 选项安全设置中标记为“受信任”

更新:感谢 marcel de vries 和 AfroMogli 的回答。 Marcel 使用 JavaScript 来查找元素,而 AfroMogli 使用纯 C# 和 CodedUI API。两个答案都同样有效,我没有注意到两者之间的性能差异。两个同样好的解决方案。

【问题讨论】:

    标签: c# asp.net .net coded-ui-tests


    【解决方案1】:

    您可以通过使用 ControlDefinition 作为属性名进行搜索来完成此操作。以下代码行对我有用:

    HtmlInputButton button = new HtmlInputButton(document);
    button.SearchProperties.Add(new PropertyExpression(HtmlControl.PropertyNames.ControlDefinition, "data-task-id=\"123\"", PropertyExpressionOperator.Contains));
    

    允许您传入任何属性名称和值的示例方法:

    public HtmlInputButton InputButton(string attributeName, string attributeValue, UITestControl container = null)
    {
        string controlDefinition = string.Format("{0}=\"{1}\"", attributeName, attributeValue);
        HtmlInputButton button = new HtmlInputButton(container ?? document);
        button.SearchProperties.Add(new PropertyExpression(HtmlControl.PropertyNames.ControlDefinition, controlDefinition, PropertyExpressionOperator.Contains));
    
        return button;
    }
    

    编辑

    或者如果属性名称没有改变,请执行以下操作?

    public PropertyExpression GetByTaskId(string value) 
    {
      return new PropertyExpression(
        HtmlControl.PropertyNames.ControlDefinition, 
        $"data-task-id=\"{value}\"", 
        PropertyExpressionOperator.Contains
      );
    }
    
    HtmlInputButton button = new HtmlInputButton(document);
    button.SearchProperties.Add(GetByTaskId("123"));
    

    【讨论】:

    • 我得试试这个。理想情况下,我想要一个不涉及在浏览器上运行 JavaScript 的解决方案。
    • 太好了,请告诉我们进展如何 :)
    • 这很棒!我也喜欢它是一个纯粹的编码 UI 测试解决方案。我被撕裂了。我希望我可以将您和马塞尔·德弗里斯的答案标记为解决方案。
    • 太棒了!是的,当有纯 Coded UI 测试解决方案作为替代方案时,您不想执行 JS 代码。但是 JS 解决方案也很有效,所以我知道你很伤心:)
    • 请注意,使用此解决方案您无法在 chrome 和 Firefox 上运行它,因为跨浏览器插件不支持使用 ControlDefinition。这是我多次要求团队修复的问题,但尚未更改。我也希望这个解决方案更好,但更喜欢 javascrip 一个,这样我就可以跨浏览器运行它。
    【解决方案2】:

    执行此操作的最简单方法是使用简单的 javascript 执行来根据您提供的任何属性名称和值获取控件。像这样:

    const string javascript = "return document.querySelector('{0}');";
    var bw = BrowserWindow.Launch("your page");
    string selector = "[data-task-id]='123']";
    var control = bw.ExecuteScript(string.Format(javascript,selector));`
    

    变量控件现在包含您要查找的控件并且类型正确。所以如果它是例如和 HtmlHyperLink 你可以这样正确地使用它。

    我有一个关于如何在 Angular 站点中使用它的更长的故事:http://fluentbytes.com/testing-angular-sites-with-codedui/,因为 Angular 甚至使用自定义属性,如 ng-*

    【讨论】:

    • ExecuteScript 方法总是为我返回 null。我阅读了博客文章,使用ControlDefinition 作为过滤器也找不到该元素。需要做更多的挖掘
    • 我正在暂停调试器并使用window.ExecuteScript("document.querySelector('body');"),甚至返回null(“window”字段是一个 BrowserWindow 对象)。
    • 我试过window.ExecuteScript("alert('foo');"),它显示浏览器警报很好。 JavaScript 正在执行,它只是没有捕获脚本的返回值。
    • 我想通了。无论出于什么愚蠢的原因,我不得不在 eval'd JavaScript 中 return document.querySelector(...)。我已经编辑了您的答案以反映这一点(如果可以的话)。 +1 并标记为答案。谢谢!
    • 我明白,但请注意,这有一个很大的缺点。您无法运行 AfroMogli 的跨浏览器提供的解决方案。因此,当您针对 chrome 或 Firefox 运行此测试时,测试将失败,因为跨浏览器播放仍然不支持控件定义
    【解决方案3】:

    我编写了这些实用方法来测试此处其他答案中解释的 JavaScript 和直接解决方案。

    我发现虽然 ControlDefinition 属性最初不能使用 Visual Studio Marketplace (v1.7) 上的开箱即用 Selenium Cross-Browser Components 跨浏览器(使用 Chrome)工作,但一切都开始工作了正如upgradingchromedriver.exe 之后的预期(到v2.41)。

    请注意,升级后的chromedriver.exe 必须先“解锁”才能正常工作。我使用 PowerShell 的 Unblock-File 命令来执行此操作,注意这必须使用 Run as Administrator 启动才能成功。

    实用方法:

    public PropertyExpression GetByCustomAttribute(string attributeName, string value)
    {
        return new PropertyExpression(HtmlControl.PropertyNames.ControlDefinition,
                                      $"{attributeName}=\"{value}\"",
                                      PropertyExpressionOperator.Contains);
    }
    
    public static T GetControlByCustomAttribute<T>(this BrowserWindow browserWindow,
                                                   string attributeName,
                                                   string value,
                                                   HtmlControl context = null)
         where T : HtmlControl
    {
        string queryContext = (context != null ? "arguments[0]" : "document");
        string script = $"return {queryContext}.querySelector(\"[{attributeName}=\'{value}\']\");";
        return browserWindow.ExecuteScript(script, context) as T;
    }
    

    用法:

    BrowserWindow.CurrentBrowser = "chrome";
    var browser = BrowserWindow.Launch(new Uri("index.html"));
    
    // first find parent of item with custom data attribute
    var div = new HtmlControl(browser);
    div.SearchProperties.Add(HtmlControl.PropertyNames.Id, "myDiv");
    
    // Either using ControlDefinition solution:
    HtmlEdit edit1 = new HtmlEdit(div);
    edit1.SearchProperties.Add(GetByItemId("4711"));
    
    // Or using JavaScript solution:
    HtmlEdit edit2 = browser.GetControlByItemId<HtmlEdit>("4711", div);
    
    // use control...
    edit1.Text = "text";
    edit2.Text = "text";
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-01
      • 1970-01-01
      • 2022-08-02
      • 2021-03-22
      • 2019-08-12
      • 1970-01-01
      • 1970-01-01
      • 2018-03-30
      相关资源
      最近更新 更多