Coder Social home page Coder Social logo

testng-dataproviders's Introduction

TestNg-DataProviders BuildStatus

This project exercises testng dataProviders backed by various Office formats

Unlike core TestNg data providers configurable through annotation constant parameters this Data provider class features runtime-flexible data file paths iparameterization enabling one running the jar with environment-specific test data without recompiling the java project. This feature was requested in one of the forums and was easy to implement - details in in Extra Features section below.

Testing

For example test case performs Selenium link count test with the data providers of the following supported data types:

  • Excel 2003
  • Excel 2007
  • Open Office Spreadsheet
  • JSON

The test inputs are defined as spreadsheet with columns

ROWNUM SEARCH COUNT
1 junit 100

or a JSON file with the following structure:

{
    "test": [{
        "keyword": "junit",
        "count": 101.0,
        "comment": "",
        "other unused column": "",
    }, {
        "comment": "",
        "keyword": "testng",
        "count": 31.0
    },
...
]
}

which represent the test id, the seach term and expected minimum count of articles found on the forum through title search.

The following annotations are provided to the test methods:

@Test(enabled = true, singleThreaded = false, threadPoolSize = 1, invocationCount = 1, description = "searches publications for a keyword", dataProvider = "Excel 2003")
@DataFileParameters(name = "data_2003.xls", path = "${USERPROFILE}\\Desktop", sheetName = "Employee Data")
	public void test_with_Excel_2003(double rowNum, String search_keyword,
			double expected_count) throws InterruptedException {
		parseSearchResult(search_keyword, expected_count);
	}

or

@Test(enabled = true, singleThreaded = false, threadPoolSize = 1, invocationCount = 1, description = "searches publications for a keyword", dataProvider = "Excel 2007")
@DataFileParameters(name = "data_2007.xlsx", path = ".")
	public void test_with_Excel_2007(double rowNum, String search_keyword,
			double expected_count) throws InterruptedException {
		parseSearchResult(search_keyword, expected_count);
	}

or

@Test(enabled = true, singleThreaded = false, threadPoolSize = 1, invocationCount = 1, description = "searches publications for a keyword", dataProvider = "OpenOffice Spreadsheet")
@DataFileParameters(name = "data.ods", path = "src/main/resources") // when datafile path is relative assume it is under ${user.dir}
	public void test_with_OpenOffice_Spreadsheet(double rowNum,
			String search_keyword, double expected_count)
			throws InterruptedException {
		parseSearchResult(search_keyword, expected_count);
	}

or

@Test(enabled = true, singleThreaded = false, threadPoolSize = 1, invocationCount = 1, description = "searches publications for a keyword", dataProvider = "JSON")
@JSONDataFileParameters(name = "data.json", dataKey = "test", columns = "keyword,count"
  // one need to list value columns explicitly with JSON due to the way org.json.JSONObject is implemented
	public void test_with_JSON(String search_keyword, double expected_count)
			throws InterruptedException {
		parseSearchResult(search_keyword, expected_count);
	}

The data provider class will load all columns from Excel 2003, Excel 2007 or OpenOffice spreadsheet respectively and columns defined for JSON data provider and run test method with every row of data. It is up to the test developer to make the test method consume the correct number and type or parameters as the columns in the spreadsheet.

To enable debug messages during the data loading, set the debug flag with @DataFileParameters attribute:

	@Test(enabled = true, singleThreaded = true, threadPoolSize = 1, invocationCount = 1, description = "# of articless for specific keyword", dataProvider = "Excel 2007", dataProviderClass = ExcelParametersProvider.class)
	@DataFileParameters(name = "data_2007.xlsx", path = ".", sheetName = "Employee Data", debug = true)
	public void test_with_Excel_2007(double rowNum, String searchKeyword,
			double expectedCount) throws InterruptedException {
		dataTest(searchKeyword, expectedCount);
	}

this will show the following:

Data Provider Caller Suite: Suite 1
Data Provider Caller Test: Parse Search Result
Data Provider Caller Method: test_with_Excel_2007
0 = A ID
1 = B SEARCH
2 = C COUNT
Cell Value: 1.0 class java.lang.Double
Cell Value: junit class java.lang.String
Cell Value: 104.0 class java.lang.Double
...
row 0 : [1.0, junit, 104.0]
...

Extra Features

This data provider overcomes the known difficulty of core TestNG or Junit parameter annotations: developer is not allowed to redefine the dataprovider attributes like for example the data source path:

  public static final String dataPath = "src/main/resources";

@Test(enabled = true, dataProvider = "OpenOffice Spreadsheet", dataProviderClass = ExcelParametersProvider.class)
  @DataFileParameters(name = "data.ods", path = dataPath, debug = false)
  public void test(double rowNum,
    String searchKeyword, double expectedCount) throws InterruptedException {
    // actual code ot the  test
  }

In the above, one is only allowed to initialize the dataPath to a String (or int) primitive type, in particular even declaring the same (pseudo-const) value in a separate class:

public class ParamData {
  public final static String dataPath = "src/main/resources";
}

and assigning the result to the vatiable in the main test class,

public class FileParamsTest {
  private final static String dataPath = ParamData.dataPath;

or assigning the method rerturn value to the parameter:

  public final static String dataPath = param();

  public static final String param() {
    return "src/main/resources";
  }

would fail to compile:

Compilation failure:
[ERROR]TestNgDataProviderTest.java: element value must be a constant expression

so it likely not doable.

However it is quite easy to allow such flexibility in the data provider class ExcelParametersProvider itself by adding an extra class variable e.g. testEnvironment which would load its value from the environment variable named TEST_ENVIRONMENT

private final static String testEnvironment = (System
    .getenv("TEST_ENVIRONMENT") != null) ? System.getenv("TEST_ENVIRONMENT")
        : "";

and override the datafile path value provided in the test method annotation:

@Test(enabled = true, dataProvider = "OpenOffice Spreadsheet", dataProviderClass = ExcelParametersProvider.class)
@DataFileParameters(name = "data.ods", path = "src/main/resources", debug = false)
public void test(double rowNum, String searchKeyword, double expectedCount) throws InterruptedException {
  dataTest(searchKeyword, expectedCount);
}

in the presence of the environment TEST_ENVIRONMENT with the value dev will make it read parameters of the test from src/main/resources/dev/data.ods rather then src/main/resources/data.ods:

if (testEnvironment != null && testEnvironment != "") {
  filePath = amendFilePath(filePath);
}

This functionaliy is implemented directly in the ExcelParametersProvider provider class in a very basic fashion as shown below:

public class ExcelParametersProvider
implements ParametersProvider<ExcelParameters> {

  private final static String testEnvironment = (System.getenv("TEST_ENVIRONMENT") != null) ? System.getenv("TEST_ENVIRONMENT") : "";

  private static String amendFilePath(String filePath) {
    if (debug) {
      System.err.print(
        String.format("Amending the %s with %s", filePath, testEnvironment));       }
    // Inject the directory into the file path
    String updatedFilePath = filePath.replaceAll("^(.*)/([^/]+)$",
    String.format("$1/%s/$2", testEnvironment));
    if (debug) {
      System.err.println(String.format(" => %s", updatedFilePath));
    }
    return updatedFilePath;
}

and take it into account to redefine the inputs during initialization:

    filePath = String.format("%s/%s",
        (parameters.path().isEmpty()
            || parameters.path().equalsIgnoreCase("."))
                ? System.getProperty("user.dir")
                : Utils.resolveEnvVars(parameters.path()),
        parameters.name());
    // if the path is relative assume it is under ${user.dir}
    if (!filePath.matches("^[/\\\\].*")
        && !filePath.matches("^(?i:[A-Z]):.*")) {
      filePath = String.format("%s/%s", System.getProperty("user.dir"),
          filePath);
    }
    if (testEnvironment != null && testEnvironment != "") {
      filePath = amendFilePath(filePath);
    }

therefore the test

mkdir dev
mkdir src\main\resources\dev
copy src\main\resources\data.* src\main\resources\dev\
copy data*.* dev
set  TEST_ENVIRONMENT=dev
mvn test

works as expected (the example shows debug output for Open Office data file data.ods originally red from src/main/resources):

Amending the c:\developer\sergueik\testng-dataproviders/src/main/resources/data.
ods with dev => c:\developer\sergueik\testng-dataproviders/src/main/resources/de
v/data.ods
BeforeMethod Suite: Suite 1
Reading Open Office Spreadsheet: Employee Data
Cell Value: "1.0" class java.lang.Double
Cell Value: "junit" class java.lang.String
Cell Value: "202.0" class java.lang.Double
Cell Value: "2.0" class java.lang.Double

One can easily make this behavior optional, turn the TEST_ENVIRONMENT envirnmant name a separate parameter or switch to store definitions of environment specifics into the property file (this is work in progress). Similar changes will be soon available to

Caller-Specific Behavior

Sometimes a caller-specific behavior is required from custom data provider. See also accessing ITestContext in DataProvider(in Russian) This is easy to achieve, since the signature of the method one has to implement is:

	@DataProvider(parallel = false, name = "OpenOffice Spreadsheet")
	public static Object[][] createData_from_OpenOfficeSpreadsheet(final ITestContext context, final Method method) {
		if (debug) {
			System.err.println(String.format("Providing data to method: '%s' of test '%s'", method.getName(),
					context.getCurrentXmlTest().getName()));
		}

the test log then will include caller information:

Providing data to method: 'testWithOpenOfficeSpreadsheet' of test 'Parse Search Result'
Opening test
Reading Open Office Spreadsheet : test
...

Filtering Data Rows for JUnitParams

In addition to using every row of spreadsheet as test parameter one may create a designated column which value would be indicating to use or skip that row of data, like:

ROWNUM SEARCH COUNT ENABLED
1 junit 100 true
2 testng 30 true
3 spock 20 false
4 mockito 41 true

and annotate the method like

@Test(enabled = false, singleThreaded = true, threadPoolSize = 1, invocationCount = 1, description = "# of articless for specific keyword", dataProvider = "OpenOffice Spreadsheet", dataProviderClass = ExcelParametersProvider.class)
@DataFileParameters(name = "filtered_data.ods", path = dataPath, sheetName = "Filtered Example" , controlColumn = "ENABLED", withValue = "true", debug = true)
public void testWithFilteredParameters(double rowNum,
    String searchKeyword, double expectedCount) throws InterruptedException {
  dataTest(searchKeyword, expectedCount);
}

with this data setting only rows 1,2 and 4 from the data extract above would be used as testWithFilteredParameters test method parameters. The control column itself is not passed to the subject test method. Currently this functionality is implemented for OpenOffice spreadsheet only. Remaining format is a work in progress.

This feature of storing more then one set of tests in one spreadsheet and picking the ones which column is set to a specified value has been inspired by some python post and the forum(in Russian)

Note

When outside the project directory it is common to place the test datafile (like Excel spreadsheet) on Desktop, Downloads and other directories of the current user. However the dataprovider annotation parametes must be constant expressions and test method cannot use class variables or static methods like File.separator in annotation value, code like below will not compile:

Test( dataProvider = "Excel", dataProviderClass = ExcelParametersProvider.class)
@DataFileParameters(name = "data_2003.xls", path = (osName.startsWith("windows")) ? "${USERPROFILE}" : "${HOME}" + File.separator + "Desktop" )

To workaround this inconvenienve, the TestNg Data Providers internally converts between ${USERPRFILE} and ${HOME} and vise versa on Linux and Mac computers therefore the expressions path = "${USERPROFILE}\\Desktop" or path = "${HOMEDIR}/Downloads" work across OS.

Links

Maven Central

The snapshot versions are deployed to https://oss.sonatype.org/content/repositories/snapshots/com/github/sergueik/dataprovider/ The release versions status: Release pending

To use the snapshot version, add the following to pom.xml:

<dependency>
  <groupId>com.github.sergueik.testng</groupId>
  <artifactId>dataprovider</artifactId>
  <version>1.3-SNAPSHOT</version>
</dependency>
<repositories>
  <repository>
    <id>ossrh</id>
    <url>https://oss.sonatype.org/content/repositories/snapshots</url>
  </repository>
</repositories>

Apache POI compatibility

  • The default version of the supported Apache POI is 3.17.
  • Older versions of the package require minor code refactoring. Note that you may also have to clear the other versions of poi and poi-ooxml jars from maven cache '~/.m2/repository'
  • Project can be built with Apache POI 4.0.0 by modifying poi.version to 4.0.0 within &lt;properties&gt; node in the pom.xml - the profile poi40 does not work correctly.
  • Creating branches and tags is a work in progress.

Google Sheet Data Provider

This is an experimental provider based on blog how to use Google Sheets API to read data from Spreadsheet.

The test method that is about to load the parameters from Google sheet is annotated in a similar fashion as with other providers developed in this project:

@Test(dataProviderClass = GoogleSheetParametersProvider.class, dataProvider = "Google Spreadsheet")
@DataFileParameters(name = "Google Sheets Example", path = "17ImW6iKSF7g-iMvPzeK4Zai9PV-lLvMsZkl6FEkytRg", sheetName = "Test Data", secretFilePath = "/home/sergueik/.secret/client_secret.json", debug = true)
public void testWithGoogleSheet(String strRowNum, String searchKeyword, String strExpectedCount)
    throws InterruptedException {
  double rowNum = Double.parseDouble(strRowNum);
  double expectedCount = Double.parseDouble(strExpectedCount);
  dataTest(searchKeyword, expectedCount);
	}

Here the name attibute stores the name of the application, path is for the id part of the data spreadsheet URL: https://docs.google.com/spreadsheets/d/${id}, and optional sheetName stores the name of the sheet.

Google Sheet

The path to secret file that is required to access the API, is to be defined through the secretFilePath attribute. Note: like with other attributes, the the value for annotation attribute DataFileParameters.secretFilePath must be a constant expression. The following would not compile:

  private static final String SECRET_FILEPATH = Paths
    .get(System.getProperty("user.home")).resolve(".secret")
    .resolve("client_secret.json").toAbsolutePath().toString();

  @Test(dataProviderClass = GoogleSheetParametersProvider.class, dataProvider = "Google Spreadsheet")
  @DataFileParameters(name = "Google Sheets Example", secretFilePath = SECRET_FILEPATH, ...)

but the following will:

  private static final String SECRET_FILEPATH = "C:/Users/Serguei/.secret/client_secret.json";

The secret file:

{
  "installed": {
    "client_id": "XXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com",
    "project_id": "gogle-sheet-api-test-xxxxxxx",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_secret": "xxxxxxxxxxxxxxx",
    "redirect_uris": [
      "urn:ietf:wg:oauth:2.0:oob",
      "http://localhost"
    ]
  }
}

can be stored anywhere on disk outside of source control e.g. under ~/.secret/client_secret.json. It will be loaded once and the credential obtained from oauth would be cached and reused until expiration. The credential appears to be valid for approximately one hour. Currently the test opens the browser window prompting the user to confirm the access:

running test with Google Sheet Data Provider

In the future versions, parallel execution of Google Sheet parameterized tests and a more flexible caching of the access credentials is planned.

See Also

Note

  • upgrading to TestNg from version 6.14.3 to verson 7.5.1 breaks certain tests:
mvn -f pom.xml.BROKEN clean compile test-compile test
[ERROR] CommonTest.java:[55,41] cannot find symbol
[ERROR] symbol:   method getParameters()
[ERROR] location: class org.testng.xml.XmlTest
[ERROR] CommonTest.java:[164,49] cannot find symbol
[ERROR] symbol:   method getMethod()
[ERROR] location: variable testNGMethod of type org.testng.ITestNGMethod
[ERROR] CSVProviderTest.java:[62,33] cannot find symbol
[ERROR] symbol:   method getParameters()
[ERROR] location: class org.testng.xml.XmlTest

in the code

ITestResult iTestResult;
ITestNGMethod testNGMethod = iTestResult.getMethod();
Method testMethod = testNGMethod.getMethod();

and

final Map<String, String> parameters = 
(((TestRunner) context).getTest()).getParameters();

Comparison of the version 6.3 interface with version 7.5 interface

Comparison of the version 6.3 interface

with version 7.5 interface

TODO

on Linux develpment machine, seem to not be able to launch google tests. After authenticaling o Windows machine, issue disappears

[ERROR] org.testng.TestNGException:
[ERROR] Cannot find class in classpath: com.github.sergueik.testng.ExcelProviderTest

Author

Serguei Kouzmine

testng-dataproviders's People

Contributors

sergueik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

testng-dataproviders's Issues

Generate a single report for multiple rows read from CSV in TestNG- Data Provider

@sergueik I am using TestNG-DataProvider to generate multiple reports for a test case which is running in loop. The data is fetched from CSV sheet. Now the issue is, I have a column named count which contains value 1,2 3.. and so on. The values are in multiple rows. Suppose the value 1 present in column count is in 5 rows, value 2 is in 7 rows and so on. I want to generate a TestNG report for the column containing 1 as one report, column containing 2 as another report and so on. The number of different values in count column should be equal to the TestNG reports generated. I am able to generate report for each of the line but not for 5 rows as one. I am attaching the code I have written till now. The CSV looks like this: enter image description here

And the code I have written till now is below:

@dataProvider(name = "userDetails")
public static Object[][] readCsv() throws IOException {
CSVReader csvReader = new CSVReader(new FileReader("C:\Users\admin\Desktop\Appium3.csv"),',');
List<String[]> csvData=csvReader.readAll();
Object[][] csvDataObject=new Object[csvData.size()][2];
for (int i=0;i<csvData.size();i++) {
csvDataObject[i]=csvData.get(i);
}
return csvDataObject;
}

@test(dataProvider = "userDetails",priority = 2)
public void userLoginTest(String PLATFORM_NAME,String PLATFORM_VERSION,String DEVICE_NAME,String UDID,String SUPPORT_LOCATION_CONTEXT,String NO_RESET, String FULL_RESET, String appPackage,String appActivity, String url,String Count, String Action, String Element, String Identifier, String XPath , String ClassName , String SendKeysWait , String MoveToX, String MoveToY, String MoveToXPathClassName, String DownToX, String DownToY) throws InterruptedException {

if (Action.contains("Click") && Element.contains("Button")&&Identifier.contains("XPath")) {
    Button b1 = new Button();
    b1.ButtonClickByXpath(GetAppiumDriver.driver, XPath);

}

}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.