I've an ExcelReader class which reads from Excel and populates the appropriate lists.
public static List<Address> addressList;
public static List<User> userList;
Above lists are being populated like this by the ExcelReader:
addressList = new ArrayList<Address>();
Address a = new Address();
a.setAddress1(r.getCell(0).getStringCellValue());
a.setCity(r.getCell(1).getStringCellValue());
I want to use this data in my Selenium test cases. I was planning to use TestNG's #DataProvider tag to feed the test case, but it only accepts Object[][] and Iterator.
Is there a way to convert these lists into an Object[][] format?
I am also open to any suggestions if you prefer to use anything other than #DataProvider.
Thanks in advance
There are lots of ways to do this, but here is an idea. I wrote an example here that does it.
The general gist of the idea is, using the MetaModel api:
public static Object[][] get2ArgArrayFromRows( List<Row> rows ) {
Object[][] myArray = new Object[rows.size()][2];
int i = 0;
SelectItem[] cols = rows.get(0).getSelectItems();
for ( Row r : rows ) {
Object[] data = r.getValues();
for ( int j = 0; j < cols.length; j++ ) {
if ( data[j] == null ) data[j] = ""; // force empty string where there are NULL values
}
myArray[i][0] = cols;
myArray[i][1] = data;
i++;
}
logger.info( "Row count: " + rows.size() );
logger.info( "Column names: " + Arrays.toString( cols ) );
return myArray;
}
public static Object[][] getCsvData( File csvFile )
{
CsvConfiguration conf = new CsvConfiguration( 1 );
DataContext csvContext = DataContextFactory.createCsvDataContext( csvFile, conf );
Schema schema = csvContext.getDefaultSchema();
Table[] tables = schema.getTables();
Table table = tables[0]; // a representation of the csv file name including extension
DataSet dataSet = csvContext.query()
.from( table )
.selectAll()
.where("run").eq("Y")
.execute();
List<Row> rows = dataSet.toRows();
Object[][] myArray = get2ArgArrayFromRows( rows );
return myArray;
}
Now, this code above is just a ROUGH idea. What you really need to do is merge cols and data into a Map<String,String> object and then pass that as the first argument back to your test, containing all parameters from the CSV file, including browser type. Then, as the second argument, set it like so:
myArray[i][1] = new WebDriverBuilderHelper();
Then, in your #Test annotated method, instantiate the driver:
#Test(dataProvider = "dp")
public void testIt( Map<String,String> map, WebDriverBuilderHelper wdhelper ) {
wdhelper.instantiateBrowser( map.get("browser") );
wdhelper.navigateTo(url);
....
Related
I have Data provider defined to get the data from excel sheet and passing it to the test method of testing. I am able to get and read data from excel but when passing it to the testing method under test it throws MethodMatcherException
Code from dataProvider
#DataProvider(name = "testdatasupply")
public Object[][] ReadDetails() throws IOException {
HashMap<String,String> rowdata=new HashMap<String,String>();
File file=new File("src/main/resources/TestData.xlsx");
FileInputStream fis=new FileInputStream(file);
XSSFWorkbook wb= new XSSFWorkbook(fis);
XSSFSheet sheet=wb.getSheet("API");
Row key=sheet.getRow(0);
int rowcount=sheet.getPhysicalNumberOfRows();
int columncount=key.getLastCellNum();
Object[][] data1 =new Object[rowcount][columncount];
for(int i=0;i<sheet.getPhysicalNumberOfRows()-1;i++) {
Row row = sheet.getRow(i+1);
if(row.getCell(2).getStringCellValue().equalsIgnoreCase("Yes")){
for (int j = 0; j < row.getLastCellNum(); j++) {
rowdata.put(key.getCell(j).getStringCellValue(), row.getCell(j).getStringCellValue());
}
data1[i][0]=rowdata;
System.out.println(data1[i][0]);
}
}
return data1;
}
And my testing class under test .
#Test(dataProvider = "testdatasupply",dataProviderClass = TestData.ReadData.class)
public void APITesting(Hashtable<String,String> data){
System.out.println(data);
String API= data.get("API Name");
switch (API.toLowerCase()){
case "petstore/get":
System.out.println(data.get("ID"));
case "petstore/post":
System.out.println(data.get("ID")+"from secnd");
}
}
When I run above code, I am getting org.testng.internal.reflect.MethodMatcherException: which shows return type is not matching as expected. Since I have more columns , getting it as individual string is not helping as I have to create more number of variables.
[public void TestRunner.main.testrunner.APITesting(java.util.Hashtable)] has no parameters defined but was found to be using a data provider (either explicitly specified or inherited from class level annotation).
Data provider mismatch
Method: APITesting([Parameter{index=0, type=java.util.Hashtable, declaredAnnotations=[]}])
Can anyone suggest best way to get this as Hashtable or what I am doing wrong here?
Your dataprovider returns a HashMap for each test, while your test method expects a parameter of type Hashtable. These two are different data types. But both have a common interface Map.
So, in your test method, change as below:
public void APITesting(Map<String,String> data)
In your data provider change as: (not required, but recommended):
Map<String,String> rowdata=new HashMap<String,String>();
Also, your code dataprovider code has some issues - The same map, rowdata is being used inside the loop, So for every test run the data passed to the test method would be the same since you are doing data1[i][0]=rowdata; within the i loop.
I assume you should be doing:
for(int i=0;i<sheet.getPhysicalNumberOfRows()-1;i++) {
Row row = sheet.getRow(i+1);
// initialize the map here.
Map<String,String> rowdata=new HashMap<String,String>();
if(row.getCell(2).getStringCellValue().equalsIgnoreCase("Yes")){
for (int j = 0; j < row.getLastCellNum(); j++) {
rowdata.put(key.getCell(j).getStringCellValue(), row.getCell(j).getStringCellValue());
}
data1[i][0]=rowdata;
System.out.println(data1[i][0]);
}
}
Your code seems incorrect and you can do the following things:
1). First Solution:
a) #Test method cannot have public void APITesting(Hashtable<String,String> data) try to replace it with public void APITesting(String API, String ID)
b) In the data provider try to return the ID and API. You need to parse data before returning it. In the dataprovider method you need to get the API and ID and then pass it to the #test method.
data1[i][0]=APIData;
data1[i][1]=ID;
Code:
#Test(dataProvider = "testdatasupply",dataProviderClass = TestData.ReadData.class)
public void APITesting(String API, String ID) {
switch (API.toLowerCase()) {
case "petstore/get":
System.out.println(ID);
case "petstore/post":
System.out.println(ID + "from secnd");
}
}
2) Second Solution:
As you cannot pass the public void APITesting(Hashtable<String,String> data) try to again replace it with public void APITesting(String data) and then convert the string to hashtable again.
Code:
#Test(dataProvider = "testdatasupply",dataProviderClass = TestData.ReadData.class)
public void APITesting(String data) {
String a = data.toString();
a = a.replaceAll("[{}]","");
System.out.println(a);
// New HashTable obj
Hashtable<String, String> hashTable
= new Hashtable<String, String>();
// split the String by a comma
String parts[] = a.split(",");
// iterate the parts and add them to a HashTable
for (String part : parts) {
String subData[] = part.split("=");
String api = subData[0].trim();
String id = subData[1].trim();
// Add to hashTable
hashTable.put(api, id);
}
System.out.println("String to HashTable: " + hashTable);
System.out.println(hashTable.get("API"));
System.out.println(hashTable.get("id"));
String API= hashTable.get("API Name");
switch (API.toLowerCase()){
case "petstore/get":
System.out.println(hashTable.get("ID"));
case "petstore/post":
System.out.println(hashTable.get("ID")+"from secnd");
}
}
Using Java Reflection:
How do I generically access arrays of other objects to retrieve their values ?
Given this Java structure:
class Master
{
static class innerThing
{
static StringBuilder NumOfThings = new StringBuilder( 2);
static class Thing_def
{
static StringBuilder field1 = new StringBuilder( 3);
static StringBuilder field2 = new StringBuilder( 3);
static StringBuilder field3 = new StringBuilder(13);
}
static Thing_def[] Things = new Thing_def [2];
static { for (int i=0; i<Things.length; i++) Things[i] = new Thing_def(); }
}
}
Using Reflection in this bit of code:
Field[] FieldList = DataClass.getDeclaredFields();
if (0 < FieldList.length )
{
SortFieldList( FieldList );
System.out.println();
for (Field eachField : FieldList)
{
String fldType = new String( eachField.getType().toString() );
if ( fldType.startsWith("class [L") )
System.err.printf("\n### fldType= '%s'\n", fldType); //$$$$$$$$$$$$$$$
if ( fldType.startsWith("class java.lang.StringBuilder") )
{
g_iFieldCnt++;
String str = DataClass.getName().replaceAll("\\$",".");
System.out.printf("%s.%s\n", str, eachField.getName() );
}//endif
}//endfor
}//endif
I get the following output:
(Notice that it shows one copy of the fields in Thing_def.)
Master.innerThing.NumOfThings
### fldType= 'class [LMaster$innerThing$Thing_def;'
Master.innerThing.Thing_def.field1
Master.innerThing.Thing_def.field2
Master.innerThing.Thing_def.field3
In another part of the system I access the fields to generate a CSV file:
Field[] FieldList = DataClass.getDeclaredFields();
if (0 < FieldList.length )
{
for (Field eachField : FieldList)
{
String fldType = new String( eachField.getType().toString() );
if ( fldType.startsWith("class java.lang.StringBuilder") )
{
Field fld = DataClass.getDeclaredField( eachField.getName() );
StringBuilder sb = (StringBuilder)fld.get(null);
CSV_file.printf("%s,", sb ); // emit column to CSV
//fld.set( DataClass, new StringBuilder() );
}//endif
}//endfor
}//endif
So in this case I actually will need to directly access array elements.
That is, I need to get at each Master.innerThing.Thing[n].field
So, the big question is:
How do I generically access arrays like this ?
How do I know that Thing_def does not have data,
it is merely a structural definition for Things[ ] ?
I am trying to add a column to my DataFrame that serves as a unique ROW_ID for the column. So, it would be something like this
1, user1
2, user2
3, user3
...
I could have done this easily using a hashMap with an integer iterating but I can't do this in spark using the map function on DataFrame since I can't have an integer increasing inside the map function. Is there any way that I can do this by appending one column to my existing DataFrame or any other way?
PS: I know there is a very similar post, but that's for Scala and not java.
Thanks in advance
I did it by adding a column containing UUIDs in a new Column in DataFrame.
StructType objStructType = inputDataFrame.schema();
StructField []arrStructField=objStructType.fields();
List<StructField> fields = new ArrayList<StructField>();
List<StructField> newfields = new ArrayList<StructField>();
List <StructField> listFields = Arrays.asList(arrStructField);
StructField a = DataTypes.createStructField(leftCol,DataTypes.StringType, true);
fields.add(a);
newfields.addAll(listFields);
newfields.addAll(fields);
final int size = objStructType.size();
JavaRDD<Row> rowRDD = inputDataFrame.javaRDD().map(new Function<Row, Row>() {
private static final long serialVersionUID = 3280804931696581264L;
public Row call(Row tblRow) throws Exception {
Object[] newRow = new Object[size+1];
int rowSize= tblRow.length();
for (int itr = 0; itr < rowSize; itr++)
{
if(tblRow.apply(itr)!=null)
{
newRow[itr] = tblRow.apply(itr);
}
}
newRow[size] = UUID.randomUUID().toString();
return RowFactory.create(newRow);
}
});
inputDataFrame = objsqlContext.createDataFrame(rowRDD, DataTypes.createStructType(newfields));
Ok, I found the solution to this problem and I'm posting it in case someone would have the same problem:
The way to do this it zipWithIndex from JavaRDD()
df.javaRDD().zipWithIndex().map(new Function<Tuple2<Row, Long>, Row>() {
#Override
public Row call(Tuple2<Row, Long> v1) throws Exception {
return RowFactory.create(v1._1().getString(0), v1._2());
}
})
i have a table in the database of 4rows and 4columns. each column holds a different data.
now i want to retrieve all the data in the database and put them on JLabel on another form. i.e
in my Database i have.
packageName.....monthlyFee..... YearlyFee....... TotalFee
Regular......................150..................300....................450
Gold.........................300...................400..................700
..... ..... .... ....
now i have a form that i have put 4 empty JLabels in four rows but how do i retrieve the values from the database and place each value in the appropriate Label?.
This is what i've done but i still cant get around it. im stuck.
Thank you anyone.
public void getPrices()
{
String srt ="SELECT * FROM program_tbl";
try
{
con.connect();
ps = con.con.prepareStatement(srt);
rs = ps.executeQuery();
ResultSetMetaData data = rs.getMetaData();
int colums = data.getColumnCount();
while(rs.next())
{
Vector rows = new Vector();
for (int i = 1; i < colums; i++)
{
rows.addElement(rs.getObject(i));
}
.....................................................................
If you want to get this data as a string then you could probably try something like:
Vector<String> rows = new Vector<String>();
while(rs.next()) {
String rowEntry = rs.getString("packageName") +
rs.getString("monthlyFee") +
rs.getString("yearlyFee") +
rs.getString("totalFee") +
rows.add(rowEntry);
}
If not String, but an object to use later, then you can create a class:
public class MyObject {
private String packageName;
private int monthlyFee;
private int yearlyFee;
private int totalFee;
public MyObject (String name, int monthlyFee, int yearlyFee, int totalFee) {
this.packageName = name;
this.monthlyFee = monthlyFee;
this.yearlyFee = yearlyFee;
this.totalFee = totalFee;
}
/*Setters
*And
*Getters*/
}
And then use it as:
Vector<MyObject> rows = new Vector<MyObject>();
while (rs.next()) {
MyObject obj = new MyObject(rs.getString("packageName")
, rs.getInt("montlyFee")
, rs.getInt("yearlyFee")
, rs.getInt("totalFee")
);
rows.add(obj)
}
So say we now have a vector with String values - Vector<String> rows;
now i would like to create those JLabels.
JLabel[] myLabels = new JLabel[v.size()];
for(int i=0; i<rows.size(); i++) {
as[i] = new JLabel(rows.get(i));
}
And now we have an array of JLabels ready to be put to applet.
Don't use a JLabel. There is no way you can easily format the data so that you get tabular data.
Instead you should be using a JTable. Read the section from the Swing tutorial on How to Use Tables for more information. You can also search the forum for examples of using a JTable with a ResultSet.
I am using below code which reads a CSV file and passes the object to the method under test called as public void launchWCM1(IBLogonDataCSV data) .
#DataProvider(name = "regCSVData")
public static Object[][] getCSVData() throws IOException {
CSVReader csvReader = new CSVReader(new FileReader(
"C:\\Projects\\Project\\regdata.csv"));
List<String[]> dataList = csvReader.readAll();
String s = "";
Object[][] data = new Object[dataList.size()][1];
List<IBLogonDataCSV> regList = new ArrayList<IBLogonDataCSV>();
for (String[] strArray : dataList) {
IBLogonDataCSV ibLogonData = new IBLogonDataCSV();
ibLogonData.setURL((strArray[0].trim()));
ibLogonData.setApplicationName((strArray[1].trim()));
ibLogonData.setIBLogonURL(strArray[2].trim());
ibLogonData.setWindowName(strArray[3].trim());
ibLogonData.setSnapshotName(strArray[4].trim());
ibLogonData.setRegister(strArray[5].trim());
ibLogonData.setRegisterURL(strArray[6].trim());
ibLogonData.setDemo(strArray[7].trim());
ibLogonData.setDemoURL(strArray[8].trim());
ibLogonData.setSecurity(strArray[9].trim());
ibLogonData.setSecurityURL(strArray[10].trim());
regList.add(ibLogonData);
}
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
data[i][j] = regList.get(i);
System.out.println("In Array" + regList.get(i).URL
+ regList.get(i).ApplicationName);
s = s + regList.get(i).URL;
}
}
csvReader.close();
return data;
}
When the method public void launchWCM1(IBLogonDataCSV data) passes or fails the Testng Report displays the object name which is not helpful in knowing what is the String value or the name of the URL failed in order to further debug .
Example:launchWCM1
Test class: com.seleniumtests.test.IBLogonCSV
Parameters: com.seleniumtests.dataobject.IBLogonDataCSV#de1b8a
What should be done to display the parameters in as a String Value ,something which will can be easily read like :
launchWCM1
Test class: com.seleniumtests.test.IBLogonCSV
Parameters: Name of the URL or something like that instead of the object name
You can override toString() and implement it to return readable meaningful vaule of your class.
#Override
public String toString(){
String valueToReturn = "calculate from the properties";
return valueToReturn;
}
Other way is, there are few third party extensions to TestNG are available, You can utilize available data-provider-extension. For example, QAF has in-built data providers for XML/CSV/Excel/Json/DB.