数据提供者与 TestNG XML:Selenium 中的参数化(示例)
Selenium 中的参数化
Selenium 中的参数化是一种对测试脚本进行参数化以在运行时将多个数据传递给应用程序的过程。它是一种执行策略,可使用不同值自动多次运行测试用例。通过参数化测试脚本实现的概念称为数据驱动测试。
TestNG 中的参数化类型-
为了更清楚地说明参数化,我们将通过 Selenium Webdriver 最流行的框架之一 – TestNG 中的参数化选项。
在 TestNG 中,我们可以通过两种方式实现参数化
Testng.xml 中的参数可以是套件级别或测试级别
DataProvider 中的参数可以接受 Method 和 ITestContext 作为参数。
让我们详细研究它们 –
TestNG 中的参数注解
TestNG 中的参数注解是一种使用 .xml 文件将值作为参数传递给测试方法的方法。用户可能需要在运行时将值传递给测试方法。@Parameters 注解方法可以用于任何带有 @Test、@Before、@After 或 @Factory 注解的方法。
Testng.xml 中的参数注解
当您不想处理复杂性且输入组合数量较少时,请选择使用注解进行参数化。
让我们看看它是如何工作的
测试场景
步骤 1) 启动浏览器并访问 Google.com
步骤 2) 输入搜索关键字
步骤 3) 验证输入值是否与我们测试数据提供的值相同
步骤 4) 重复步骤 2 和 3,直到所有值都输入完毕
测试作者 | 搜索关键词 |
---|---|
Guru99 | 印度 |
克里希纳 | 美国 |
布佩什 | 中国 |
下面是一个如何在没有参数的情况下执行此操作的示例
package parameters; import org.testng.annotations.Test; import org.testng.AssertJUnit; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; public class NoParameterWithTestNGXML { String driverPath = "C:\\geckodriver.exe"; WebDriver driver; @Test public void testNoParameter() throws InterruptedException{ String author = "guru99"; String searchKey = "india"; System.setProperty("webdriver.gecko.driver", driverPath); driver= new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); driver.get("https://google.com"); WebElement searchText = driver.findElement(By.name("q")); //Searching text in google text box searchText.sendKeys(searchKey); System.out.println("Welcome ->"+author+" Your search key is->"+searchKey); System.out.println("Thread will sleep now"); Thread.sleep(3000); System.out.println("Value in Google Search Box = "+searchText.getAttribute("value") +" ::: Value given by input = "+searchKey); //verifying the value in google search box AssertJUnit.assertTrue(searchText.getAttribute("value").equalsIgnoreCase(searchKey)); } }
研究一下上面的例子。想象一下,当我们对 3 个输入组合执行此操作时,代码会变得多么复杂
现在,让我们使用 TestNG 对其进行参数化
为此,您需要
- 创建一个将存储参数的 XML 文件
-
在测试中,添加注解 @Parameters
这是完整的代码
测试级别 TestNG.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="TestSuite" thread-count="3" > <parameter name="author" value="Guru99" /> <parameter name="searchKey" value="India" /> <test name="testGuru"> <parameter name="searchKey" value="UK" /> <classes> <class name="parameters.ParameterWithTestNGXML"> </class> </classes> </test> </suite>
ParameterWithTestNGXML.java 文件
package parameters; import org.testng.AssertJUnit; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.testng.annotations.Optional; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class ParameterWithTestNGXML { String driverPath = "C:\\geckodriver.exe"; WebDriver driver; @Test @Parameters({"author","searchKey"}) public void testParameterWithXML( @Optional("Abc") String author,String searchKey) throws InterruptedException{ System.setProperty("webdriver.gecko.driver", driverPath); driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); driver.get("https://google.com"); WebElement searchText = driver.findElement(By.name("q")); //Searching text in google text box searchText.sendKeys(searchKey); System.out.println("Welcome ->"+author+" Your search key is->"+searchKey); System.out.println("Thread will sleep now"); Thread.sleep(3000); System.out.println("Value in Google Search Box = "+searchText.getAttribute("value") +" ::: Value given by input = "+searchKey); //verifying the value in google search box AssertJUnit.assertTrue(searchText.getAttribute("value").equalsIgnoreCase(searchKey)); } }
运行脚本的说明,选择 XML 文件并以 Test NG 套件形式运行
右键点击 .xml 文件 -> 运行方式 -> Testng 套件(注意:套件)
现在,参数可以在两个级别定义
- 套件级别 – TestNG XML 文件中 <suite> 标签内的参数将是套件级别参数。
- 测试级别 — 测试 XML 文件中 <Test> 标签内的参数将是测试级别参数。
以下是具有套件级别参数的相同测试
注意:如果参数名称在套件级别和测试级别相同,则测试级别参数将优先于套件级别参数。因此,在这种情况下,该测试级别内的所有类都将共享被覆盖的参数,而该测试级别之外的其他类将共享套件级别参数。
故障排除
问题 #1 testng.xml 中的参数值无法转换为相应的测试方法的参数,否则会抛出错误。
考虑以下示例
在这里,'author' 属性等于 'Guru99',它是一个字符串,而在相应的测试方法中,它期望一个整数值,因此我们将在此处收到一个异常。
问题 #2 您的 @Parameters 在 testing.xml 中没有相应的值。
您可以通过在测试方法中相应的参数中添加 @optional annotation 来解决此问题。
问题 #3:您想使用 Testng.xml 测试同一参数的多个值
简单的答案是:这无法完成!您可以有多个不同的参数,但每个参数只能有一个值。这有助于防止将值硬编码到脚本中。这使得代码可重用。可以将其视为脚本的配置文件。如果您想对一个参数使用多个值,请使用 DataProviders
TestNG 中的数据提供者
TestNG 中的数据提供者是一种在用户需要传递复杂参数时使用的方法。复杂参数需要从 Java 创建,例如复杂对象、属性文件中的对象或数据库中的对象可以通过数据提供者方法传递。该方法使用 @DataProvider 注解,它返回一个对象数组。
使用数据提供者的参数
@Parameters 注解很容易,但要用多组数据进行测试,我们需要使用 Data Provider。
要使用我们的测试框架填写数千个网页表单,我们需要一种不同的方法,该方法可以在单个执行流中为我们提供非常大的数据集。
这个数据驱动的概念是通过 TestNG 中的 @DataProvider 注解实现的。
它只有一个属性“name”。如果您不指定 name 属性,则 DataProvider 的名称将与相应的方法名相同。
数据提供者向测试方法返回一个二维 JAVA 对象,测试方法将以 M*N 类型的对象数组调用 M 次。例如,如果数据提供者返回一个 2*3 对象的数组,则相应的测试用例将调用 2 次,每次有 3 个参数。
完整示例
package parameters; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; public class ParameterByDataprovider { WebDriver driver; String driverPath = "C:\\geckodriver.exe"; @BeforeTest public void setup(){ //Create firefox driver object System.setProperty("webdriver.gecko.driver", driverPath); driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); driver.get("https://google.com"); } /** Test case to verify google search box * @param author * @param searchKey * @throws InterruptedException */ @Test(dataProvider="SearchProvider") public void testMethod(String author,String searchKey) throws InterruptedException{ { WebElement searchText = driver.findElement(By.name("q")); //search value in google searchbox searchText.sendKeys(searchKey); System.out.println("Welcome ->"+author+" Your search key is->"+searchKey); Thread.sleep(3000); String testValue = searchText.getAttribute("value"); System.out.println(testValue +"::::"+searchKey); searchText.clear(); //Verify if the value in google search box is correct Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } } /** * @return Object[][] where first column contains 'author' * and second column contains 'searchKey' */ @DataProvider(name="SearchProvider") public Object[][] getDataFromDataprovider(){ return new Object[][] { { "Guru99", "India" }, { "Krishna", "UK" }, { "Bhupesh", "USA" } }; } }
从不同的类调用 DataProvider
默认情况下,DataProvider 位于测试方法所在的类或其基类中。要将其放在其他类中,我们需要将数据提供者方法设为静态,并在测试方法中需要在 @Test 注解中添加一个属性 dataProviderClass。
代码示例
测试类 ParameterDataproviderWithClassLevel.java
package parameters; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; public class ParameterDataproviderWithClassLevel { WebDriver driver; String driverPath = "C:\\geckodriver.exe"; @BeforeTest public void setup(){ System.setProperty("webdriver.gecko.driver", driverPath); driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); driver.get("https://google.com"); } @Test(dataProvider="SearchProvider",dataProviderClass=DataproviderClass.class) public void testMethod(String author,String searchKey) throws InterruptedException{ WebElement searchText = driver.findElement(By.name("q")); //Search text in google text box searchText.sendKeys(searchKey); System.out.println("Welcome ->"+author+" Your search key is->"+searchKey); Thread.sleep(3000); //get text from search box String testValue = searchText.getAttribute("value"); System.out.println(testValue +"::::"+searchKey); searchText.clear(); //verify if search box has correct value Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } }
DataproviderClass.java
package parameters; import org.testng.annotations.DataProvider; public class DataproviderClass { @DataProvider(name="SearchProvider") public static Object[][] getDataFromDataprovider(){ return new Object[][] { { "Guru99", "India" }, { "Krishna", "UK" }, { "Bhupesh", "USA" } }; }}
数据提供者中的参数类型
数据提供者方法支持两种类型的参数。
Method – 如果相同的 DataProvider 应该以不同的测试方法表现不同,请使用 Method 参数。
在以下示例中,
- 我们检查方法名是否为 testMethodA。
- 如果是,则返回一组值
- 否则返回另一组值
package parameters; import java.lang.reflect.Method; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; public class ParameterByMethodInDataprovider{ WebDriver driver; String driverPath = "C:\\geckodriver.exe"; @BeforeTest public void setup(){ System.setProperty("webdriver.gecko.driver", driverPath); driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); driver.get("https://google.com"); } @Test(dataProvider="SearchProvider") public void testMethodA(String author,String searchKey) throws InterruptedException{ WebElement searchText = driver.findElement(By.name("q")); //Search text in search box searchText.sendKeys(searchKey); //Print author and search string System.out.println("Welcome ->"+author+" Your search key is->"+searchKey); Thread.sleep(3000); String testValue = searchText.getAttribute("value"); System.out.println(testValue +"::::"+searchKey); searchText.clear(); //Verify if google text box is showing correct value Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } @Test(dataProvider="SearchProvider") public void testMethodB(String searchKey) throws InterruptedException{ { WebElement searchText = driver.findElement(By.name("q")); //Search text in search box searchText.sendKeys(searchKey); //Print only search string System.out.println("Welcome ->Unknown user Your search key is->"+searchKey); Thread.sleep(3000); String testValue = searchText.getAttribute("value"); System.out.println(testValue +"::::"+searchKey); searchText.clear(); //Verify if google text box is showing correct value Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } } /** * Here DataProvider returning value on the basis of test method name * @param m * @return **/ @DataProvider(name="SearchProvider") public Object[][] getDataFromDataprovider(Method m){ if(m.getName().equalsIgnoreCase("testMethodA")){ return new Object[][] { { "Guru99", "India" }, { "Krishna", "UK" }, { "Bhupesh", "USA" } };} else{ return new Object[][] { { "Canada" }, { "Russia" }, { "Japan" } };} } }
这是输出
ITestContext – 它可用于根据组为测试用例创建不同的参数。
在实际生活中,您可以使用 ITestContext 根据测试方法、主机、测试配置来改变参数值。
在以下代码示例中
- 我们有 2 个组 A 和 B
- 每个测试方法都分配给一个组
- 如果组值为 A,则返回特定数据集
- 如果组值为 B,则返回另一个数据集
package parameters; import java.util.concurrent.TimeUnit; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.testng.Assert; import org.testng.ITestContext; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; public class ParameterByITestContextInDataprovider { WebDriver driver; String driverPath = "C:\\geckodriver.exe"; @BeforeTest(groups={"A","B"}) public void setup(){ System.setProperty("webdriver.gecko.driver", driverPath); driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); driver.get("https://google.com"); } @Test(dataProvider="SearchProvider",groups="A") public void testMethodA(String author,String searchKey) throws InterruptedException{ { //search google textbox WebElement searchText = driver.findElement(By.name("q")); //search a value on it searchText.sendKeys(searchKey); System.out.println("Welcome ->"+author+" Your search key is->"+searchKey); Thread.sleep(3000); String testValue = searchText.getAttribute("value"); System.out.println(testValue +"::::"+searchKey); searchText.clear(); //verify correct value in searchbox Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } } @Test(dataProvider="SearchProvider",groups="B") public void testMethodB(String searchKey) throws InterruptedException{ { //find google search box WebElement searchText = driver.findElement(By.name("q")); //search a value on it searchText.sendKeys(searchKey); System.out.println("Welcome ->Unknown user Your search key is->"+searchKey); Thread.sleep(3000); String testValue = searchText.getAttribute("value"); System.out.println(testValue +"::::"+searchKey); searchText.clear(); //verify correct value in searchbox Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } } /** * Here the DAtaProvider will provide Object array on the basis on ITestContext * @param c * @return */ @DataProvider(name="SearchProvider") public Object[][] getDataFromDataprovider(ITestContext c){ Object[][] groupArray = null; for (String group : c.getIncludedGroups()) { if(group.equalsIgnoreCase("A")){ groupArray = new Object[][] { { "Guru99", "India" }, { "Krishna", "UK" }, { "Bhupesh", "USA" } }; break; } else if(group.equalsIgnoreCase("B")) { groupArray = new Object[][] { { "Canada" }, { "Russia" }, { "Japan" } }; } break; } return groupArray; } }
注意:如果您直接运行 testng 类,它将首先调用 dataprovider,dataprovider 无法获取组信息,因为组不可用。但是,如果您通过 testng.xml 调用此类的,它将通过 ITestContext 获得组信息。使用以下 XML 调用测试
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" > <suite name="test-parameter"> <test name="example1"> <groups> <run> <include name="A" /> </run> </groups> <classes> <class name="parameters.ParameterByITestContextInDataprovider" /> </classes> </test> <test name="example2"> <groups> <run> <include name="B" /> </run> </groups> <classes> <class name="parameters.ParameterByITestContextInDataprovider" /> </classes> </test> </suite>
摘要
- 参数化需要创建数据驱动测试。
- TestNG 支持两种参数化方式,使用 @Parameter+TestNG.xml 和使用 @DataProvider
-
在@Parameter+TestNG.xml中,参数可以放置在套件级别和测试级别。如果
在两个地方声明了相同的参数名称;测试级别参数将优先于套件级别参数。
- 使用 @Parameter+TestNG.xml 每次只能设置一个值,但 @DataProvider 返回一个二维对象数组。
- 如果 DataProvider 位于与测试方法所在的类不同的类中,则 DataProvider 应该是 静态方法。
- DataProvider 支持的两个参数是 Method 和 ITestContext。