如何遍历表格以输入数据

huangapple go评论88阅读模式
英文:

How do I iterate through a table to enter data

问题

I have a test framework which uses Specflow, Selenium and C#.

The Specflow test step looks like the below (I'm using datatables):
And I create the below Loan Purposes in the Loan Purposes section

|loan purposes |moreinfo |active|
|Debt Consolidation|True |True |
|Home Improvements |True |True |

So obviously there are 2 rows in this dataTable with the top row just the header.

In the step definition for the above step, I first identify the count in the table and store that as a variable (which correctly gives me a count of 2 in the above instance). I then iterate over the number of rows in the table and click the ‘Add button’ on the UI to add as many rows as I need as per the test step – so in this instance, it correctly clicks the Add button twice which adds 2 blank rows.

var data = dataTable.CreateSet<ProductLoanPurposeData>();

var count = data.Count();
for (int i = 0; i < count; i++)
{
ClickLoanPurposeAddButton();
}

Here's where I’m getting stuck.
So each row added on the UI has its own class – I’ve labelled this element in my PageClass ‘LoanPurposeRow’.
What I want to do is take the data from the first row in the dataTable and input that on the first ‘LoanPurposeRow’ on the UI. If there are more rows in my dataTable, I then want to move onto the second ‘LoanPurposeRow’ and take the data from the 2nd row in the dataTable and so on.

I've been searching online and checked out various answers but not finding what I need in order to carry out the above. Thanks in advance

英文:

I have a test framework which uses Specflow, Selenium and C#.

The Specflow test step looks like the below (I'm using datatables):
And I create the below Loan Purposes in the Loan Purposes section

    |loan purposes     |moreinfo |active|
    |Debt Consolidation|True     |True  |
    |Home Improvements |True     |True  |

So obviously there are 2 rows in this dataTable with the top row just the header.

In the step definition for the above step, I first identify the count in the table and store that as a variable (which correctly gives me a count of 2 in the above instance). I then iterate over the number of rows in the table and click the ‘Add button’ on the UI to add as many rows as I need as per the test step – so in this instance, it correctly clicks the Add button twice which adds 2 blank rows.

    var data = dataTable.CreateSet&lt;ProductLoanPurposeData&gt;();
    
    var count = data.Count();
    for (int i = 0; i &lt; count; i++)
    {
    ClickLoanPurposeAddButton();
    }

Here's where I’m getting stuck.
So each row added on the UI has its own class – I’ve labelled this element in my PageClass ‘LoanPurposeRow’.
What I want to do is take the data from the first row in the dataTable and input that on the first ‘LoanPurposeRow’ on the UI. If there are more rows in my dataTable, I then want to move onto the second ‘LoanPurposeRow’ and take the data from the 2nd row in the dataTable and so on.

I've been searching online and checked out various answers but not finding what I need in order to carry out the above. Thanks in advance

答案1

得分: 1

Your create set is converting your table to an object, so you would reference the object accordingly. You need to iterate though your data object, like so. Note that I removed the spacing in your table header

foreach(var item in data)
{
    driver.FindElement(By.Id("LoanPurposeRow")).SendKeys(item.loanpurposes);
ClickLoanPurposeAddButton();
}

It will grab the text for whatever "loanpurposes" is and input it into your element. You will need to adjust your By selector based on whatever row you're on. You could initialize a counter.

Admittedly I don't use CreateSet in my selenium code, I just iterate though the tables like below, and it also allows you to keep the spacing.

foreach(var item in data.Rows)
{
    driver.FindElement(By.Id("LoanPurposeRow")).SendKeys(item["loan purposes"]);
ClickLoanPurposeAddButton();
}

Either way you go, the first iteration will grab all the data from the first row, the second iteration from the second row, and so forth.

This link has a little more information on CreateSet

英文:

Your create set is converting your table to an object, so you would reference the object accordingly. You need to iterate though your data object, like so. Note that I removed the spacing in your table header

foreach(var item in data)
{
    driver.FindElement(By.Id(&quot;LoanPurposeRow&quot;)).SendKeys(item.loanpurposes);
ClickLoanPurposeAddButton();
}

It will grab the text for whatever "loanpurposes" is and input it into your element. You will need to adjust your By selector based on whatever row you're on. You could initialize a counter.

Admittedly I don't use CreateSet in my selenium code, I just iterate though the tables like below, and it also allows you to keep the spacing.

foreach(var item in data.Rows)
{
    driver.FindElement(By.Id(&quot;LoanPurposeRow&quot;)).SendKeys(item[&quot;loan purposes&quot;]);
ClickLoanPurposeAddButton();
}

Either way you go, the first iteration will grab all the data from the first row, the second iteration from the second row, and so forth.

This link has a little more information on CreateSet

答案2

得分: 1

AutomatedOrder 指引您朝正确的方向前进。您需要获取数据并调用 Selenium API 将数据输入到网页中。我想进一步提供他的答案,戴上我的“架构师”帽子,为您更好地解释 SpecFlow 步骤如何与 Page Object Model 结合使用,从而强化了 SpecFlow 和 Selenium 之间的关注点分离。

  1. 初始化 Selenium Web 驱动程序并将其注册到 SpecFlow 依赖注入框架

  2. 创建一个代表您的网页部分的 Page Object 类:

    public class LoanPurposesPageObject
    {
        private readonly IWebDriver driver;
    
        // TODO: 更改元素定位器以匹配您的 HTML 结构
        private IWebElement AddLoanPurposeButton => driver.FindElement(By.XPath("//button[contains(., 'Add Loan Purpose')]"));
        private IWebElement LoanPurposesTextBox => driver.FindElement(By.Id("LoanPurposes"));
        private IWebElement MoreInfoTextBox => driver.FindElement(By.Id("MoreInfo"));
    
        public LoanPurposesPageObject(IWebDriver driver)
        {
            this.driver = driver;
        }
    
        public void Add(string loanPurposes, string moreInfo, bool isActive)
        {
            LoanPurposesTextBox.SendKeys(loanPurposes);
            MoreInfoTextBox.SendKeys(moreInfo);
    
            if (isActive)
            {
                // TODO: 检查单选按钮或选择下拉选项?
            }
            else
            {
                // TODO: 检查单选按钮或选择下拉选项?
            }
    
            AddLoanPurposeButton.Click();
        }
    }
    
  3. 修改您的步骤定义类,以接受 IWebDriver 对象作为构造函数参数并初始化页面对象。

    [Binding]
    public class YourStepDefinitions
    {
        private readonly LoanPurposesPageObject loanPurposes;
        private readonly IWebDriver driver;
    
        public YourStepDefinitions(IWebDriver driver)
        {
            this.driver = driver;
            loanPurposes = new LoanPurposesPageObject(driver);
        }
    }
    
  4. 修改步骤定义方法,以将数据从您的 ProductLoanPurposeData 对象传递到页面对象:

    [Binding]
    public class YourStepDefinitions
    {
        // ...
    
        [When(@"...")]
        public void YourStepThatUsesTheDataTable(Table table)
        {
            var data = table.CreateSet<ProductLoanPurposeData>();
    
            foreach (var row in data)
            {
                loanPurposes.Add(row.LoanPurposes, row.MoreInfo, row.Active);
            }
        }
    }
    

这样可以在 SpecFlow 和 Selenium 之间实现干净的分离,并促进了大量代码重用,同时保持了适当的解耦。

英文:

AutomatedOrder points you in the right direction. You need to take the data and call the Selenium API to input the data into the web page. I would like to take his answer one step further by putting on my "Architect" hat for a moment to give you a better idea how SpecFlow steps can be used along side the Page Object Model. This enforces Separation of Concerns between SpecFlow and Selenium.

  1. Initialize the Selenium web driver and register it with the SpecFlow dependency injection framework.

  2. Create a Page Object class that represents that part of your web page:

    public class LoanPurposesPageObject
    {
        private readonly IWebDriver driver;
    
        // TODO: Change element locators to match your HTML structure
        private IWebElement AddLoanPurposeButton =&gt; driver.FindElement(By.XPath(&quot;//button[contains(., &#39;Add Loan Purpose&#39;)]&quot;));
        private IWebElement LoanPurposesTextBox =&gt; driver.FindElement(By.Id(&quot;LoanPurposes&quot;));
        private IWebElement MoreInfoTextBox =&gt; driver.FindElement(By.Id(&quot;MoreInfo&quot;));
    
        public LoanPurposesPageObject(IWebDriver driver)
        {
            this.driver = driver;
        }
    
        public void Add(string loanPurposes, string moreInfo, bool isActive)
        {
            LoanPurposesTextBox.SendKeys(loanPurposes);
            MoreInfoTextBox.SendKeys(moreInfo);
    
            if (isActive)
            {
                // TODO: Check radio button or select option in dropdown?
            }
            else
            {
                // TODO: Check radio button or select option in dropdown?
            }
    
            AddLoanPurposeButton.Click();
        }
    }
    
  3. Modify your step definition class to accept an IWebDriver object as a constructor argument and initialize the page object.

    [Binding]
    public class YourStepDefinitions
    {
        private readonly LoanPurposesPageObject loanPurposes;
        private readonly IWebDriver driver;
    
        public YourStepDefinitions(IWebDriver driver)
        {
            this.driver = driver;
            loanPurposes = new LoanPurposesPageObject(driver);
        }
    
  4. Modify the step definition method to pass data from your ProductLoanPurposeData object to the page object:

    [Binding]
    public class YourStepDefinitions
    {
        // ...
    
        [When(@&quot;...&quot;)]
        public void YourStepThatUsesTheDataTable(Table table)
        {
            var data = table.CreateSet&lt;ProductLoanPurposeData&gt;();
    
            foreach (var row in data)
            {
                loanPurposes.Add(row.LoanPurposes, row.MoreInfo, row.Active);
            }
        }
    }
    

This gives you a clean separation between SpecFlow stuff and Selenium stuff and promotes a high amount of code reuse while maintaining proper decoupling.

huangapple
  • 本文由 发表于 2020年1月3日 17:26:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/59575979.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定