数据提供者与 TestNG XML:Selenium 中的参数化(示例)

当我们创建软件时,我们总是希望它能够以不同的数据集进行不同的工作。当涉及到测试同一段软件时,我们不能只用一组数据来测试它。在这里,我们再次需要验证我们的系统是否接受所有预期的组合。为此,我们需要对测试脚本进行参数化。参数化就在这里派上用场。

Selenium 中的参数化

Selenium 中的参数化是一种对测试脚本进行参数化以在运行时将多个数据传递给应用程序的过程。它是一种执行策略,可使用不同值自动多次运行测试用例。通过参数化测试脚本实现的概念称为数据驱动测试

TestNG 中的参数化类型-

为了更清楚地说明参数化,我们将通过 Selenium Webdriver 最流行的框架之一 – TestNG 中的参数化选项。

在 TestNG 中,我们可以通过两种方式实现参数化

  1. 借助 Parameters annotationTestNG XML 文件。

    Type Of Parameterization In TestNG

  2. 借助 DataProvider annotation。

    Type Of Parameterization In TestNG

Type Of Parameterization In TestNG

Testng.xml 中的参数可以是套件级别或测试级别

DataProvider 中的参数可以接受 Method 和 ITestContext 作为参数。

让我们详细研究它们 –

TestNG 中的参数注解

TestNG 中的参数注解是一种使用 .xml 文件将值作为参数传递给测试方法的方法。用户可能需要在运行时将值传递给测试方法。@Parameters 注解方法可以用于任何带有 @Test、@Before、@After 或 @Factory 注解的方法。

Testng.xml 中的参数注解

当您不想处理复杂性且输入组合数量较少时,请选择使用注解进行参数化。

让我们看看它是如何工作的

测试场景

步骤 1) 启动浏览器并访问 Google.com

步骤 2) 输入搜索关键字

Parameters Annotation With Testng.Xml

步骤 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

Parameters Annotation With Testng.Xml

这是完整的代码

测试级别 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 套件(注意:套件)

ParameterWithTestNGXML.java File

现在,参数可以在两个级别定义

  1. 套件级别 – TestNG XML 文件中 <suite> 标签内的参数将是套件级别参数。
  2. 测试级别 — 测试 XML 文件中 <Test> 标签内的参数将是测试级别参数。

以下是具有套件级别参数的相同测试

ParameterWithTestNGXML.java File

注意:如果参数名称在套件级别和测试级别相同,则测试级别参数将优先于套件级别参数。因此,在这种情况下,该测试级别内的所有类都将共享被覆盖的参数,而该测试级别之外的其他类将共享套件级别参数。

ParameterWithTestNGXML.java File

故障排除

问题 #1 testng.xml 中的参数值无法转换为相应的测试方法的参数,否则会抛出错误。

考虑以下示例

TroubleShooting

在这里,'author' 属性等于 'Guru99',它是一个字符串,而在相应的测试方法中,它期望一个整数值,因此我们将在此处收到一个异常。

问题 #2 您的 @Parameters 在 testing.xml 中没有相应的值。

您可以通过在测试方法中相应的参数中添加 @optional annotation 来解决此问题。

TroubleShooting

问题 #3:您想使用 Testng.xml 测试同一参数的多个值

简单的答案是:这无法完成!您可以有多个不同的参数,但每个参数只能有一个值。这有助于防止将值硬编码到脚本中。这使得代码可重用。可以将其视为脚本的配置文件。如果您想对一个参数使用多个值,请使用 DataProviders

TestNG 中的数据提供者

TestNG 中的数据提供者是一种在用户需要传递复杂参数时使用的方法。复杂参数需要从 Java 创建,例如复杂对象、属性文件中的对象或数据库中的对象可以通过数据提供者方法传递。该方法使用 @DataProvider 注解,它返回一个对象数组。

使用数据提供者的参数

@Parameters 注解很容易,但要用多组数据进行测试,我们需要使用 Data Provider。

要使用我们的测试框架填写数千个网页表单,我们需要一种不同的方法,该方法可以在单个执行流中为我们提供非常大的数据集。

这个数据驱动的概念是通过 TestNG 中的 @DataProvider 注解实现的。

Parameters Using Dataprovider

它只有一个属性“name”。如果您不指定 name 属性,则 DataProvider 的名称将与相应的方法名相同。

数据提供者向测试方法返回一个二维 JAVA 对象,测试方法将以 M*N 类型的对象数组调用 M 次。例如,如果数据提供者返回一个 2*3 对象的数组,则相应的测试用例将调用 2 次,每次有 3 个参数。

Parameters Using Dataprovider

完整示例

Parameters Using Dataprovider

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

Invoke DataProvider From Different Class

代码示例

Invoke DataProvider From Different Class

测试类 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 参数。

Types of Parameters In Dataprovider

在以下示例中,

  • 我们检查方法名是否为 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" }
            };}       
    }
}

这是输出

Types of Parameters In Dataprovider

ITestContext – 它可用于根据组为测试用例创建不同的参数。

在实际生活中,您可以使用 ITestContext 根据测试方法、主机、测试配置来改变参数值。

Types of Parameters In Dataprovider

在以下代码示例中

  • 我们有 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 支持的两个参数是 MethodITestContext