Dataleverantör i TestNG: Selenium Parameteriseringsexempel
⚡ Smart sammanfattning
Parametrering in Selenium kör en enda testmetod upprepade gånger mot olika datamängder, vilket omvandlar ett skript till datadriven täckning. TestNG erbjuder två mekanismer för detta: @Parameters-annoteringen som stöds av testng.xml, och @DataProvider-annoteringen som returnerar en tvådimensionell objektmatris.

När vi skapar programvara vill vi alltid att den ska fungera korrekt med olika datamängder. När det gäller testning För den programvaran räcker det inte att kontrollera en enda datamängd. Vi måste verifiera att systemet accepterar alla kombinationer som det förväntas stödja. För det behöver vi parametrisera våra testskript. Det är här parametrisering kommer in i bilden.
Parametrering in Selenium
Parametrering in Selenium är en process för att parametrisera testskripten för att skicka flera data till applikationen vid körning. Det är en exekveringsstrategi som automatiskt kör testfall flera gånger med olika värden. Konceptet som uppnås genom att parametrisera testskripten kallas Datadriven testning.
Typ av parametrering i TestNG
För att göra parametriseringen tydligare kommer vi att gå igenom parametriseringsalternativen i ett av de mest populära ramverken för Selenium Webbdrivrutin — TestNG.
ikon två sätt genom vilken vi kan uppnå parametrisering i TestNG:
- Med hjälp av Parameterannotering och TestNG XML fil.
- Med hjälp av DataProvider anteckning.
Diagrammet ovan sammanfattar uppdelningen: parametrar från testng.xml kan deklareras på svit- eller testnivå, medan en parameter från en DataProvider kan acceptera Method och ITestContext som ett eget argument. Låt oss studera dem i detalj.
Parametrar Anteckning in TestNG
Parametrar Anteckning in TestNG är en metod som används för att skicka värden till testmetoderna som argument med hjälp av en .xml-fil. Användare kan behöva skicka värdena till testmetoderna under körning. Annoteringsmetoden @Parameters kan användas i vilken metod som helst som har annoteringar av typen @Test, @Before, @After eller @Factory.
Parametrar annotering med Testng.xml
Välj parametrisering med hjälp av annoteringar när du inte vill hantera komplexitet och antalet inmatningskombinationer är litet.
Låt oss se hur detta fungerar.
Testscenario
Steg 1) Starta webbläsaren och gå till Google.com
Steg 2) Ange ett sökord
Steg 3) Kontrollera att det angivna värdet är detsamma som värdet som anges i våra testdata
Steg 4) Upprepa steg 2 och 3 tills alla värden har angetts
| Testförfattare | Söknyckel |
|---|---|
| Guru99 | Indien |
| Krishna | Sverige |
| Bhupesh | Kina |
Här är ett exempel på hur man gör det utan parametrar:
package parameters; import org.testng.annotations.Test; import org.testng.AssertJUnit; import java.time.Duration; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; public class NoParameterWithTestNGXML { WebDriver driver; @Test public void testNoParameter() throws InterruptedException { String author = "guru99"; String searchKey = "india"; driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); driver.get("https://google.com"); WebElement searchText = driver.findElement(By.name("q")); // Searching text in the Google text box searchText.sendKeys(searchKey); System.out.println("Welcome ->" + author + " Your search key is->" + searchKey); Thread.sleep(3000); System.out.println("Value in Google Search Box = " + searchText.getDomProperty("value") + " ::: Value given by input = " + searchKey); // Verifying the value in the Google search box AssertJUnit.assertTrue(searchText.getDomProperty("value").equalsIgnoreCase(searchKey)); } }
⚠️ Selenium 4-notering: Tre rader i den ursprungliga versionen av det här exemplet kompileras inte längre eller är föråldrade på nuvarande Selenium utsläpp. System.setProperty("webdriver.gecko.driver", …) är onödigt från Selenium 4.6 och framåt eftersom Selenium Manager löser upp drivrutinsbinärfilen automatiskt. implicitlyWait(10, TimeUnit.SECONDS) överbelastningen togs bort i Selenium 4 och ersatt av implicitlyWait(Duration.ofSeconds(10)). getAttribute() blev avskriven i Selenium 4.27, så att läsa en textruta använder getDomProperty("value")Varje exempel nedan tillämpar samma tre korrigeringar.
Studera exemplet ovan och föreställ dig hur komplex koden blir när detta upprepas för tre inmatningskombinationer.
Låt oss nu parametrisera det med hjälp av TestNGFör att göra det behöver du:
- Skapa en XML-fil som lagrar parametrarna
- I testet, lägg till annoteringen @Parameters
Här är den kompletta koden.
Testnivå TestNG. Xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "https://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>
ParameterMedTestNGXML.java-fil
package parameters; import org.testng.AssertJUnit; import java.time.Duration; 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 { WebDriver driver; @Test @Parameters({"author", "searchKey"}) public void testParameterWithXML(@Optional("Abc") String author, String searchKey) throws InterruptedException { driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); driver.get("https://google.com"); WebElement searchText = driver.findElement(By.name("q")); // Searching text in the Google text box searchText.sendKeys(searchKey); System.out.println("Welcome ->" + author + " Your search key is->" + searchKey); Thread.sleep(3000); System.out.println("Value in Google Search Box = " + searchText.getDomProperty("value") + " ::: Value given by input = " + searchKey); // Verifying the value in the Google search box AssertJUnit.assertTrue(searchText.getDomProperty("value").equalsIgnoreCase(searchKey)); } }
För att köra skriptet, välj XML-filen och kör den som en TestNG på.
Högerklicka på .xml-filen -> Kör som -> TestNG Svit (Obs: Svit)
Parametrar kan definieras på två nivåer:
- Svitnivå — parametrarna inuti
<suite>taggen för TestNG XML-filer är parametrar på svitnivå. - Testnivå — parametrarna inuti
<test>taggen för TestNG XML-filer är parametrar på testnivå.
Här är samma test med parametrar på svitnivå:
OBS: Om parameternamnet är detsamma på svitnivå och testnivå har testnivåparametern företräde framför svitnivå ett. I så fall delar alla klasser inom den testnivån den åsidosatta parametern, och klasser utanför den testnivån fortsätter att använda svitnivåparametern.
Felsökning
Problem nr 1: Ett parametervärde i testng.xml som inte kan typecastas till motsvarande testmetods parameter genererar ett fel.
Tänk på följande exempel:
Här är attributet 'author' lika med 'Guru99', vilket är en sträng, medan motsvarande testmetod förväntar sig ett heltal, så ett undantag genereras.
Problem nr 2: Dina @Parameters har inte ett motsvarande värde i testng.xml.
Du kan lösa detta genom att lägga till @Valfri annotering till motsvarande parameter i testmetoden.
Problem nr 3: Du vill testa flera värden för samma parameter med hjälp av testng.xml.
Det enkla svaret är att detta inte går att göra. Du kan ha flera olika parametrar, men varje parameter kan bara innehålla ett enda värde. Detta förhindrar hårdkodning av värden i skriptet och gör att koden kan återanvändas – se det som en konfigurationsfil för ditt skript. Om du behöver flera värden för en parameter, använd en DataProvider istället.
Dataleverantör i TestNG
Dataleverantör i TestNG är en metod som används när en användare behöver skicka komplexa parametrar. Komplexa parametrar behöver skapas från Java — komplexa objekt, objekt från egenskapsfiler eller objekt från en databas kan skickas med dataprovidermetoden. Metoden annoteras med @DataProvider och returnerar en array av objekt.
Parametrar med hjälp av Dataprovider
@Parameters-annoteringen är enkel, men för att testa med flera datamängder behöver vi använda en dataprovider.
För att fylla i tusentals webbformulär med vårt testramverk behöver vi en annan metod som kan leverera en mycket stor datamängd i ett enda exekveringsflöde.
Detta datadrivna koncept uppnås genom @Dataleverantör anteckning i TestNG.
Den har bara en attribut, 'namn'Om du inte anger name-attributet är DataProviders namn detsamma som motsvarande metodnamn.
En dataleverantör returnerar en tvådimensionell Java objektet till testmetoden, och testmetoden anropas M gånger för en M×N objektmatris. Om till exempel DataProvider returnerar en matris med 2×3 objekt, anropas motsvarande testfall 2 gånger med 3 parametrar varje gång.
Komplett exempel
package parameters; import java.time.Duration; 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; @BeforeTest public void setup() { // Create the Firefox driver object driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); driver.get("https://google.com"); } /** Test case to verify the Google search box */ @Test(dataProvider = "SearchProvider") public void testMethod(String author, String searchKey) throws InterruptedException { WebElement searchText = driver.findElement(By.name("q")); // Search value in the Google search box searchText.sendKeys(searchKey); System.out.println("Welcome ->" + author + " Your search key is->" + searchKey); Thread.sleep(3000); String testValue = searchText.getDomProperty("value"); System.out.println(testValue + "::::" + searchKey); searchText.clear(); // Verify if the value in the Google search box is correct Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } /** * @return Object[][] where the first column contains 'author' * and the second column contains 'searchKey' */ @DataProvider(name = "SearchProvider") public Object[][] getDataFromDataprovider() { return new Object[][] { { "Guru99", "India" }, { "Krishna", "UK" }, { "Bhupesh", "USA" } }; } }
Anropa DataProvider från en annan klass
Som standard finns en DataProvider i samma klass som testmetoden, eller i dess basklass. För att placera den i en annan klass, gör dataprovidermetoden statisk och lägg till attributet dataProviderClass till @Testa anteckning.
Code Exempelvis
TestClass ParameterDataproviderWithClassLevel.java
package parameters; import java.time.Duration; 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; @BeforeTest public void setup() { driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); 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 the Google text box searchText.sendKeys(searchKey); System.out.println("Welcome ->" + author + " Your search key is->" + searchKey); Thread.sleep(3000); // Get text from the search box String testValue = searchText.getDomProperty("value"); System.out.println(testValue + "::::" + searchKey); searchText.clear(); // Verify if the search box has the 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" } }; } }
Typer av parametrar i Dataprovider
Det finns två typer av parametrar som stöds av DataProvider-metoden.
Metod — om den Samma DataProvider bör bete sig olika för olika testmetoder, använd parametern Method.
I följande exempel:
- Vi kontrollerar om metodnamnet är testMethodA
- Om ja, returnera en uppsättning värden
- Annars returnera en annan uppsättning värden
package parameters; import java.lang.reflect.Method; import java.time.Duration; 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; @BeforeTest public void setup() { driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); driver.get("https://google.com"); } @Test(dataProvider = "SearchProvider") public void testMethodA(String author, String searchKey) throws InterruptedException { WebElement searchText = driver.findElement(By.name("q")); searchText.sendKeys(searchKey); // Print author and search string System.out.println("Welcome ->" + author + " Your search key is->" + searchKey); Thread.sleep(3000); String testValue = searchText.getDomProperty("value"); System.out.println(testValue + "::::" + searchKey); searchText.clear(); Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } @Test(dataProvider = "SearchProvider") public void testMethodB(String searchKey) throws InterruptedException { WebElement searchText = driver.findElement(By.name("q")); searchText.sendKeys(searchKey); // Print only the search string System.out.println("Welcome ->Unknown user Your search key is->" + searchKey); Thread.sleep(3000); String testValue = searchText.getDomProperty("value"); System.out.println(testValue + "::::" + searchKey); searchText.clear(); Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } /** The DataProvider returns values based on the test method name */ @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" } }; } } }
Här är utgången:
ITestContext — detta kan användas för att skapa olika parametrar för testfall baserat på grupper.
I verkligheten kan du använda ITestContext för att variera parametervärden baserat på testmetoder, värdar eller testkonfigurationer.
I följande kodexempel:
- Vi har två grupper, A och B
- Varje testmetod tilldelas en grupp
- Om gruppvärdet är A returneras en datauppsättning
- Om gruppvärdet är B returneras en annan datamängd
package parameters; import java.time.Duration; 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; @BeforeTest(groups = {"A", "B"}) public void setup() { driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); driver.get("https://google.com"); } @Test(dataProvider = "SearchProvider", groups = "A") public void testMethodA(String author, String searchKey) throws InterruptedException { WebElement searchText = driver.findElement(By.name("q")); searchText.sendKeys(searchKey); System.out.println("Welcome ->" + author + " Your search key is->" + searchKey); Thread.sleep(3000); String testValue = searchText.getDomProperty("value"); System.out.println(testValue + "::::" + searchKey); searchText.clear(); Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } @Test(dataProvider = "SearchProvider", groups = "B") public void testMethodB(String searchKey) throws InterruptedException { WebElement searchText = driver.findElement(By.name("q")); searchText.sendKeys(searchKey); System.out.println("Welcome ->Unknown user Your search key is->" + searchKey); Thread.sleep(3000); String testValue = searchText.getDomProperty("value"); System.out.println(testValue + "::::" + searchKey); searchText.clear(); Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } /** The DataProvider supplies an Object array based on ITestContext */ @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; } }
Obs: Om du kör TestNG klassen direkt, anropar den först dataleverantören, och leverantören kan inte läsa gruppinformation eftersom grupperna inte är tillgängliga ännu. Om klassen anropas via testng.xml blir gruppinformationen tillgänglig via ITestContext. Använd följande XML för att köra testet:
<!DOCTYPE suite SYSTEM "https://testng.org/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>
Båda mekanismerna är nu täckta, så den praktiska frågan är vilken man ska använda.
@Parametrar vs @DataProvider: Vilken ska man använda
De två metoderna är inte utbytbara. Beslutet beror på hur många värdekombinationer ett test behöver och var dessa värden kommer från.
| Kriterium | @Parametrar + testng.xml | @Dataleverantör |
|---|---|---|
| Värden per parameter | Exakt en | Obegränsade rader |
| Datakälla | Statisk text i XML-filen | Java kod, Excel, CSV, databas, API |
| Stödda typer | Värden typbearbetade från sträng | Vilken som helst Java objektet |
| Testanrop | En gång per konfiguration | En gång per datarad |
| Ändring av data kräver | Redigera XML-filen | Redigering av källkoden, ofta ingen omkompilering |
| Passar bäst till | Miljö- och konfigurationsvärden som webbläsare, URL, meriter | Datadriven testning över många inmatningskombinationer |
I praktiken använder de flesta programsviter båda: @Parameters bär miljöinställningarna som förblir konstanta under en körning, och @DataProvider tillhandahåller varierande affärsdata. När datamängden växer ur en hårdkodad array är nästa steg att läsa den från ett kalkylblad.
Hur man läser testdata från en Excel-fil med hjälp av DataProvider
Hårdkodade arrayer slutar skalas när testare som inte skriver Java behöver underhålla data. Att flytta raderna till ett kalkylblad separerar testdata från testlogik, vilket är den praktiska formen av datadrivna testerApache POI läser arbetsboken och DataProvider konverterar varje rad till ett testanrop.
- Lägg till beroendet. Omfatta
org.apache.poi:poi-ooxmli din Maven eller Gradle bygga. Denooxmlartefakt hanterar .xlsx-formatet; vanligtpoiläser bara äldre .xls-filer. - Skapa arbetsboken. Placera rubrikraden på rad 0 och ett testfall per rad under den, med en kolumn per testmetodargument.
- Läs in arket i en array. Storleksanpassa Object[][] utifrån rad- och cellantal så att nya rader hämtas utan kodändringar.
- Returnera den från leverantören. Testmetodens signatur förblir exakt som tidigare.
import java.io.FileInputStream; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.testng.annotations.DataProvider; public class ExcelDataProvider { @DataProvider(name = "SearchProvider") public Object[][] readSearchData() throws Exception { FileInputStream file = new FileInputStream("src/test/resources/testdata.xlsx"); Workbook workbook = new XSSFWorkbook(file); Sheet sheet = workbook.getSheetAt(0); // Skip the header row, so subtract one from the physical row count int rows = sheet.getLastRowNum(); int cols = sheet.getRow(0).getPhysicalNumberOfCells(); Object[][] data = new Object[rows][cols]; for (int i = 1; i <= rows; i++) { for (int j = 0; j < cols; j++) { data[i - 1][j] = sheet.getRow(i).getCell(j).getStringCellValue(); } } workbook.close(); file.close(); return data; } }
💡 Tips: Ring upp getStringCellValue() endast på celler formaterade som text. En numerisk cell genererar IllegalStateException, vilket är det vanligaste felet när ett kalkylblad innehåller ID:n eller belopp. Använd en DataFormatter för att läsa varje celltyp som en sträng, eller slå på cell.getCellType() och konvertera explicit.
Med data externaliserad blir det en redigering av kalkylblad snarare än en kodändring att lägga till hundra fler testfall, och samma leverantör kan mata flera testklasser genom dataProviderClass attribut som visats tidigare.
















