Доставчик на данни в TestNG: Selenium Пример за параметризация
⚡ Умно обобщение
Параметризация в Selenium изпълнява един и същ тестов метод многократно върху различни набори от данни, превръщайки един скрипт в покритие, управлявано от данни. TestNG предлага два механизма за това: анотацията @Parameters, подкрепена от testng.xml, и анотацията @DataProvider, връщаща двуизмерен обектен масив.

Когато създаваме софтуер, винаги искаме той да работи правилно с различни набори от данни. Що се отнася до тестване този софтуер, проверката на един набор от данни не е достатъчна. Трябва да проверим дали системата приема всяка комбинация, която се очаква да поддържа. За тази цел трябва да параметризираме нашите тестови скриптове. Тук се намесва параметризацията.
Параметризация в Selenium
Параметризация в Selenium е процес за параметризиране на тестовите скриптове, за да се предадат множество данни към приложението по време на изпълнение. Това е стратегия за изпълнение, която автоматично изпълнява тестови случаи няколко пъти, използвайки различни стойности. Концепцията, постигната чрез параметризиране на тестовите скриптове, се нарича Тестване, управлявано от данни.
Тип параметризация в TestNG
За да направим параметризацията по-ясна, ще разгледаме опциите за параметризация в една от най-популярните рамки за Selenium Уеб драйвер — TestNG.
Има два начина чрез които можем да постигнем параметризация в TestNG:
- С помощта на Анотация на параметрите и TestNG XML файл.
- С помощта на Доставчик на данни анотация.
Диаграмата по-горе обобщава разделението: параметри от testng.xml могат да бъдат декларирани на ниво пакет или тест, докато параметър от DataProvider може да приема Method намлява ITestContext като свой собствен аргумент. Нека ги разгледаме подробно.
Параметри Анотация в TestNG
Параметри Анотация в TestNG е метод, използван за предаване на стойности към тестовите методи като аргументи, използвайки .xml файл. Потребителите може да са задължени да предават стойностите на тестовите методи по време на изпълнение. Методът за анотация @Parameters може да се използва във всеки метод с анотация @Test, @Before, @After или @Factory.
Анотация на параметрите с Testng.xml
Изберете параметризация с помощта на анотации, когато не искате да се справяте със сложност и броят на входните комбинации е малък.
Нека видим как работи това.
Сценарий на теста
Стъпка 1) Стартирайте браузъра и отидете на Google.com
Стъпка 2) Въведете ключова дума за търсене
Стъпка 3) Проверете дали въведената стойност е същата като стойността, предоставена от нашите тестови данни
Стъпка 4) Повторете стъпки 2 и 3, докато въведете всички стойности
| Автор на теста | SearchKey |
|---|---|
| Guru99 | Индия |
| Krishna | САЩ |
| Бхупеш | Китай |
Ето пример как да го направите без параметри:
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 г.: Три реда в оригиналната версия на този пример вече не се компилират или са остарели в текущата версия. Selenium пресата. System.setProperty("webdriver.gecko.driver", …) е ненужно от Selenium 4.6 нататък, защото Selenium Мениджърът автоматично разрешава двоичния файл на драйвера. implicitlyWait(10, TimeUnit.SECONDS) претоварването беше премахнато през Selenium 4 и заменен с implicitlyWait(Duration.ofSeconds(10)). getAttribute() беше остаряло през Selenium 4.27, така че четенето на текстово поле използва getDomProperty("value")Всеки пример по-долу прилага едни и същи три корекции.
Разгледайте горния пример и си представете колко сложен става кодът, когато това се повтори за три входни комбинации.
Сега нека го параметризираме, използвайки TestNGЗа да направите това, трябва да:
- Създайте XML файл, който ще съхранява параметрите
- В теста добавете анотацията @Parameters
Ето пълния код.
Тестово ниво 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>
ParameterWithTestNGXML.java файл
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)); } }
За да стартирате скрипта, изберете XML файла и го стартирайте като TestNG нататък.
Щракнете с десния бутон върху .xml файла -> Изпълни като -> TestNG Апартамент (Забележка: Апартамент)
Параметрите могат да бъдат дефинирани на две нива:
- Ниво на апартамента — параметрите вътре в
<suite>етикет на TestNG XML файловете са параметри на ниво пакет. - Ниво на тест — параметрите вътре в
<test>етикет на TestNG XML файловете са параметри на тестово ниво.
Ето същия тест с параметри на ниво пакет:
ЗАБЕЛЕЖКА: Ако името на параметъра е едно и също на ниво пакет и тестово ниво, параметърът на тестово ниво има предимство пред параметъра на ниво пакет едно. В този случай всички класове в това тестово ниво споделят презаписания параметър, а класовете извън това тестово ниво продължават да използват параметъра на ниво пакет.
Отстраняване на проблеми
Проблем # 1: Стойност на параметър в testng.xml, която не може да бъде преобразувана в съответния параметър на тестовия метод, хвърля грешка.
Помислете за следния пример:
Тук атрибутът „автор“ е равен на „Guru99', което е низ, докато съответният тестов метод очаква цяло число, така че се генерира изключение.
Проблем # 2: Вашите @Parameters нямат съответстваща стойност в testng.xml.
Можете да решите това, като добавите @Незадължителна анотация към съответния параметър в метода на изпитване.
Проблем # 3: Искате да тествате множество стойности на един и същ параметър, използвайки testng.xml.
Простият отговор е, че това не може да се направи. Можете да имате множество различни параметъра, но всеки параметър може да съдържа само една стойност. Това предотвратява твърдото кодиране на стойности в скрипта и запазва кода за многократна употреба – мислете за това като за конфигурационен файл за вашия скрипт. Ако имате нужда от множество стойности за един параметър, използвайте DataProvider вместо това.
Доставчик на данни в TestNG
Доставчик на данни в TestNG е метод, използван, когато потребителят трябва да предаде сложни параметри. Сложните параметри трябва да бъдат създадени от Java — сложни обекти, обекти от файлове със свойства или обекти от база данни могат да бъдат предадени чрез метода за доставчик на данни. Методът е анотиран с @DataProvider и връща масив от обекти.
Параметри, използващи Dataprovider
Анотацията @Parameters е лесна, но за да тестваме с множество набори от данни, трябва да използваме доставчик на данни.
За да попълним хиляди уеб формуляри, използвайки нашата рамка за тестване, се нуждаем от различна методология, която може да предостави много голям набор от данни в един поток на изпълнение.
Тази концепция, основана на данни, се постига чрез @Доставчик на данни анотация в TestNG.
Има само един атрибут, „име“Ако не укажете атрибута name, името на DataProvider е същото като съответното име на метода.
Доставчик на данни връща двуизмерен Java обект към тестовия метод и тестовият метод се извиква M пъти за масив от M×N обекта. Например, ако DataProvider върне масив от 2×3 обекта, съответният тестов случай се извиква 2 пъти с 3 параметъра всеки път.
Пълен пример
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" } }; } }
Извикване на DataProvider от различен клас
По подразбиране, DataProvider се намира в същия клас като тестовия метод или в базовия му клас. За да го поставите в друг клас, направете метода на доставчика на данни статичен и добавете атрибута dataProviderClass към @Тест анотация.
Code Пример
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" } }; } }
Типове параметри в Dataprovider
Методът DataProvider поддържа два типа параметри.
Начин на доставка — ако един и същ Доставчикът на данни трябва да се държи различно за различните методи за тестване, използвайте параметъра Method.
В следния пример:
- Проверяваме дали името на метода е testMethodA
- Ако отговорът е „да“, върнете един набор от стойности
- В противен случай върнете друг набор от стойности
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" } }; } } }
Ето резултата:
ITestContext — това може да се използва за създаване на различни параметри за тестови случаи въз основа на групи.
В реалния живот можете да използвате ITestContext, за да променяте стойностите на параметрите въз основа на методи за тестване, хостове или тестови конфигурации.
В следния примерен код:
- Имаме 2 групи, А и Б
- Всеки тестов метод е присвоен на група
- Ако стойността на групата е A, се връща един набор от данни
- Ако стойността на групата е B, се връща друг набор от данни
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; } }
Забележка: Ако стартирате TestNG клас директно, той първо извиква доставчика на данни, а доставчикът не може да прочете информация за групата, защото групите все още не са налични. Извикването на класа чрез testng.xml прави информацията за групата достъпна чрез ITestContext. Използвайте следния XML код, за да изпълните теста:
<!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>
И двата механизма вече са обхванати, така че практическият въпрос е към кой да се обърне внимание.
@Parameters срещу @DataProvider: Кой да използвате
Двата подхода не са взаимозаменяеми. Решението се свежда до това колко комбинации от стойности са необходими на един тест и откъде произхождат тези стойности.
| критерий | @Параметри + testng.xml | @Доставчик на данни |
|---|---|---|
| Стойности на параметър | Точно един | Неограничени редове |
| Източник на данни | Статичен текст в XML файла | Java код, Excel, CSV, база данни, API |
| Поддържани типове | Типизиране на стойности от String | който и да е Java обект |
| Тестови извиквания | Веднъж на конфигурация | Веднъж на ред с данни |
| Промяната на данните изисква | Редактиране на XML файла | Редактиране на изходния код, често без прекомпилиране |
| Най-подходящ за | Стойности на средата и конфигурацията, като например браузър, URL, пълномощия | Тестване, управлявано от данни, в множество входни комбинации |
На практика повечето пакети използват и двете: @Parameters съдържа настройките на средата, които остават постоянни по време на изпълнение, а @DataProvider предоставя променливите бизнес данни. Когато наборът от данни надхвърля твърдо кодиран масив, следващата стъпка е четенето му от електронна таблица.
Как да четете тестови данни от Excel файл с помощта на DataProvider
Твърдо кодираните масиви спират мащабирането, след като тестерите, които не пишат Java необходимостта от поддържане на данните. Преместването на редовете в електронна таблица разделя тестовите данни от тестовата логика, което е практическата форма на тестване, управлявано от данниApache POI чете работната книга и DataProvider преобразува всеки ред в едно тестово извикване.
- Добавете зависимостта. Включва
org.apache.poi:poi-ooxmlвъв вашия Maven или Gradle изграждане. Theooxmlартефактът обработва .xlsx формат; plainpoiчете само стари .xls файлове. - Създайте работната книга. Поставете заглавния ред в ред 0 и по един тестов случай на ред под него, с по една колона за всеки аргумент на тестовия метод.
- Прочетете листа в масив. Оразмерете Object[][] според броя на редовете и клетките, така че новите редове да се зареждат без промени в кода.
- Върнете го от доставчика. Сигнатурата на метода за тестване остава точно както преди.
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; } }
💡 Съвет: ПОЗВЪНЯВАНЕ getStringCellValue() само върху клетки, форматирани като текст. Числова клетка хвърля IllegalStateException, което е най-често срещаният проблем, когато електронна таблица съдържа идентификатори или суми. Използвайте DataFormatter да чете всеки тип клетка като низ или да го включва cell.getCellType() и конвертирайте изрично.
С външните данни, добавянето на сто допълнителни тестови случая се превръща в редактиране на електронна таблица, а не в промяна на код, и един и същ доставчик може да захранва няколко тестови класа чрез... dataProviderClass атрибут, показан по-рано.
















