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");
}
}
Related
I'm learning to program in Java for Android Studio. I'm working with a Parse.com query downloading information. I store the information inside an array of a costume object called MyData. When I'm storing the information I can log the content of the array and it has the correct info. But latter when I try to use the same array, if I use the .length function it says it's null. And if I try to retrieve any of the information, it's empty.
This I my object:
public class MyData {
Integer gluc;
Integer insulinaV;
Date fec;
Integer alimento;
String comentarios;
public MyData(Integer gluc, Integer insulinaV, Date fec, Integer alimento, String comentarios) {
this.gluc = gluc;
this.insulinaV = insulinaV;
this.fec = fec;
this.alimento = alimento;
this.comentarios = comentarios;
}
public Integer getGluc() {
return gluc;
}
public Integer getInsulinaV() {
return insulinaV;
}
public Date getFec() {
return fec;
}
public Integer getAlimento() {
return alimento;
}
public String getComentarios() {
return comentarios;
}
}
So, to retrieve the information I use array[I].getWhatever(), this is how I store the information:
public void downloadInformation() {
user = ParseUser.getCurrentUser();
ParseQuery<ParseObject> query = ParseQuery.getQuery("Glucosa");
query.whereEqualTo("usuario", user);
query.orderByDescending("createdAt");
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> objects, ParseException e) {
if (e == null && objects.size() > 0) {
Log.d("score!", "Objects Retrived");
Log.d("size", String.valueOf(objects.size()));
int i = 0;
indexsize = 0;
for (ParseObject object : objects) {
dataArray = new MyData[objects.size()];
dataArray[i] = new MyData(object.getInt("glucosa"), object.getInt("insulina"), object.getDate("fecha"), object.getInt("Alimentos"), object.getString("Comentarios"));
String alimentosexiste = dataArray[i].getAlimento().toString();
Log.i("Is Empty or Not=", alimentosexiste);
indexsize = indexsize+1;
i++;
}
} else {
Log.d("failed", "error");
}
}
});
}
In my logcat I'm getting "Score! Objects retrieved" and "Size: 22", also I get a list with all 22 elements of the "Is Empty or Not" Log. So far so good.
Then, In my attempt to move from this activity to another, I try to save the dataArray with:
public void saveInformation() {
int j = indexsize;
Log.i("size of index?", String.valueOf(indexsize));
for (int i=0; i<=j; i++) {
Log.i("index", String.valueOf(i));
alimentosVal = dataArray[i].getAlimento();
comentariosVal = dataArray[i].getComentarios();
glucVal = dataArray[i].getGluc();
insulinaVal = dataArray[i].getInsulinaV();
fecVal = dataArray[i].getFec();
}
SQLiteDatabase myGlucosebase = this.openOrCreateDatabase("GlucoseEvents", MODE_PRIVATE, null);
myGlucosebase.execSQL("CREATE TABLE IF NOT EXISTS glucoseevents (alimentos INT(2), comentarios VARCHAR, gluc INT(4), insulinv INT(4), fec DATETIME)");
myGlucosebase.execSQL("INSERT INTO glucoseevents (alimentos, comentarios, gluc, insulinv, fec) VALUES (alimentosVal, comentariosVal, glucVal, insulinaVal, fecVal) ");
}
And even do I printed before the content of the array with index [0] (so I'm sure the information got stored in the array), I get the following error:
Attempt to invoke virtual method 'java.lang.Integer com.parse.starter.MyData.getAlimento()' on a null object reference
I've seen that the problem is that I'm pointing to an empty element, but it was working before, how can I do this?
(Data array is declared at the beginning, below the class name as: MyData[] dataArray;)
Thanks!
dataArray = new MyData[objects.size()]; should be outside the for loop
Your class MyData does not have a "dataArray". At least not in the example code you give above.
I am attempting to generate an Avro schema from java to describe a table that I can access via JDBC.
I use the JDBC getMetaData() method to retrieve the relevant column metadata and store in an array list of "columnDetail" objects.
Column Detail defined as
private static class columnDetail {
public String tableName;
public String columnName;
public String dataTypeName;
public int dataTypeId;
public String size;
public String scale;
}
I then iterate through this array list and build up the Avro schema using the org.apache.avro.SchemaBuilder class.
My issue is around decimal logical types.
I iterate throuth the array list twice. The first time to add all fields to the FieldAssembler, the second to modify certain byte fields to add the decimal logical datatype.
The issue I am experiencing is that I get an error if the Decimal scale value changes between iterations.
As it iterates through the columnDetail array, it will work so long as the value "scale" does not change. If it does change, the following occurs:
Exception in thread "main" org.apache.avro.AvroRuntimeException: Can't overwrite property: scale
at org.apache.avro.JsonProperties.addProp(JsonProperties.java:187)
at org.apache.avro.Schema.addProp(Schema.java:134)
at org.apache.avro.JsonProperties.addProp(JsonProperties.java:191)
at org.apache.avro.Schema.addProp(Schema.java:139)
at org.apache.avro.LogicalTypes$Decimal.addToSchema(LogicalTypes.java:193)
at GenAvroSchema.main(GenAvroSchema.java:85)
I can prevent this by hardcoding the decimal size. i.e. I can replace
org.apache.avro.LogicalTypes.decimal(Integer.parseInt(cd.size),Integer.parseInt(cd.scale)).addToSchema(schema.getField(cd.columnName).schema());
with
org.apache.avro.LogicalTypes.decimal(18,2).addToSchema(schema.getField(cd.columnName).schema());
This however ends up with the same size datatype for all decimal fields which is not desirable.
Can someone help with this ?
Java: 1.8.0_202
Avro: avro-1.8.2.jar
My java code:
public static void main(String[] args) throws Exception{
String jdbcURL = "jdbc:sforce://login.salesforce.com";
String jdbcUser = "userid";
String jdbcPassword = "password";
String avroDataType = "";
HashMap<String, String> dtmap = new HashMap<String, String>();
dtmap.put("VARCHAR", "string");
dtmap.put("BOOLEAN", "boolean");
dtmap.put("NUMERIC", "bytes");
dtmap.put("INTEGER", "int");
dtmap.put("TIMESTAMP", "string");
dtmap.put("DATE", "string");
ArrayList<columnDetail> columnDetails = new ArrayList<columnDetail>();
columnDetails = populateMetadata(jdbcURL, jdbcUser, jdbcPassword); // This works so have not included code here
SchemaBuilder.FieldAssembler<Schema> fields = SchemaBuilder.builder().record("account").doc("Account Detials").fields() ;
for(columnDetail cd:columnDetails) {
avroDataType = dtmap.get(JDBCType.valueOf(cd.dataTypeId).getName());
switch(avroDataType)
{
case "string":
fields.name(cd.columnName).type().unionOf().nullType().and().stringType().endUnion().nullDefault();
break;
case "int":
fields.name(cd.columnName).type().unionOf().nullType().and().intType().endUnion().nullDefault();
break;
case "boolean":
fields.name(cd.columnName).type().unionOf().booleanType().and().nullType().endUnion().booleanDefault(false);
break;
case "bytes":
if(Integer.parseInt(cd.scale) == 0) {
fields.name(cd.columnName).type().unionOf().nullType().and().longType().endUnion().nullDefault();
} else {
fields.name(cd.columnName).type().bytesType().noDefault();
}
break;
default:
fields.name(cd.columnName).type().unionOf().nullType().and().stringType().endUnion().nullDefault();
break;
}
}
Schema schema = fields.endRecord();
for(columnDetail cd:columnDetails) {
avroDataType = dtmap.get(JDBCType.valueOf(cd.dataTypeId).getName());
if(avroDataType == "bytes" && Integer.parseInt(cd.scale) != 0) {
//org.apache.avro.LogicalTypes.decimal(Integer.parseInt(cd.size),Integer.parseInt(cd.scale)).addToSchema(schema.getField(cd.columnName).schema());
org.apache.avro.LogicalTypes.decimal(18,2).addToSchema(schema.getField(cd.columnName).schema());
}
}
BufferedWriter writer = new BufferedWriter(new FileWriter("./account.avsc"));
writer.write(schema.toString());
writer.close();
}
Thanks,
Eoin.
I have some other test context with custom logic to import and read
"testConfigFile"
But < I'm planing to iterate over multiple tests so, trying to read test data as
#DataProvider(name = "testdata")
public Object[][] testdata(ITestContext context) {
Map<String, String> parameters = context.getCurrentXmlTest()
.getAllParameters();
System.out.println("-------");
Iterator it = parameters.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pairs = (Map.Entry) it.next();
String x = (String) pairs.getValue();
if (x.startsWith("created_")
&& !pairs.getKey().toString().contains("shareData")) {
String y = (String) universalMap.get(x);
parameters.put((String) pairs.getKey(), y);
}
}
return new Object[][] { { parameters } };
}
In which it is not able to detect context have a getCurrentXmlTest() method even after type casting it as
Map<String, String> parameters = (((ITestContext)context).getCurrentXmlTest())
.getAllParameters();
Still it throws :-
The method getCurrentXmlTest() is undefined for the type ITestContext
Oh, I was using very old testng-5.4-jdk15.jar TestNG library.
Now, I have upgraded it and working fine now.
Thanks.
I am looking for an idea how to accomplish this task. So I'll start with how my program is working.
My program reads a CSV file. They are key value pairs separated by a comma.
L1234456,ygja-3bcb-iiiv-pppp-a8yr-c3d2-ct7v-giap-24yj-3gie
L6789101,zgna-3mcb-iiiv-pppp-a8yr-c3d2-ct7v-gggg-zz33-33ie
etc
Function takes a file and parses it into an arrayList of String[]. The function returns the ArrayList.
public ArrayList<String[]> parseFile(File csvFile) {
Scanner scan = null;
try {
scan = new Scanner(csvFile);
} catch (FileNotFoundException e) {
}
ArrayList<String[]> records = new ArrayList<String[]>();
String[] record = new String[2];
while (scan.hasNext()) {
record = scan.nextLine().trim().split(",");
records.add(record);
}
return records;
}
Here is the code, where I am calling parse file and passing in the CSVFile.
ArrayList<String[]> Records = parseFile(csvFile);
I then created another ArrayList for files that aren't parsed.
ArrayList<String> NotParsed = new ArrayList<String>();
So the program then continues to sanitize the key value pairs separated by a comma. So we first start with the first key in the record. E.g L1234456. If the record could not be sanitized it then it replaces the current key with "CouldNOtBeParsed" text.
for (int i = 0; i < Records.size(); i++) {
if(!validateRecord(Records.get(i)[0].toString())) {
Logging.info("Records could not be parsed " + Records.get(i)[0]);
NotParsed.add(srpRecords.get(i)[0].toString());
Records.get(i)[0] = "CouldNotBeParsed";
} else {
Logging.info(Records.get(i)[0] + " has been sanitized");
}
}
Next we do the 2nd key in the key value pair e.g ygja-3bcb-iiiv-pppp-a8yr-c3d2-ct7v-giap-24yj-3gie
for (int i = 0; i < Records.size(); i++) {
if(!validateRecordKey(Records.get(i)[1].toString())) {
Logging.info("Record Key could not be parsed " + Records.get(i)[0]);
NotParsed.add(Records.get(i)[1].toString());
Records.get(i)[1] = "CouldNotBeParsed";
} else {
Logging.info(Records.get(i)[1] + " has been sanitized");
}
}
The problem is that I need both keyvalue pairs to be sanitized, make a separate list of the keyValue pairs that could not be sanitized and a list of the ones there were sanitized so they can be inserted into a database. The ones that cannot will be printed out to the user.
I thought about looping thought the records and removing the records with the "CouldNotBeParsed" text so that would just leave the ones that could be parsed. I also tried removing the records from the during the for loop Records.remove((i)); However that messes up the For loop because if the first record could not be sanitized, then it's removed, the on the next iteration of the loop it's skipped because record 2 is now record 1. That's why i went with adding the text.
Atually I need two lists, one for the Records that were sanitized and another that wasn't.
So I was thinking there must be a better way to do this. Or a better method of sanitizing both keyValue pairs at the same time or something of that nature. Suggestions?
Start by changing the data structure: rather than using a list of two-element String[] arrays, define a class for your key-value pairs:
class KeyValuePair {
private final String key;
private final String value;
public KeyValuePair(String k, String v) { key = k; value = v; }
public String getKey() { return key; }
public String getValue() { return value; }
}
Note that the class is immutable.
Now make an object with three lists of KeyValuePair objects:
class ParseResult {
private final List<KeyValuePair> sanitized = new ArrayList<KeyValuePair>();
private final List<KeyValuePair> badKey = new ArrayList<KeyValuePair>();
private final List<KeyValuePair> badValue = new ArrayList<KeyValuePair>();
public ParseResult(List<KeyValuePair> s, List<KeyValuePair> bk, List<KeyValuePair> bv) {
sanitized = s;
badKey = bk;
badValue = bv;
}
public List<KeyValuePair> getSanitized() { return sanitized; }
public List<KeyValuePair> getBadKey() { return badKey; }
public List<KeyValuePair> getBadValue() { return badValue; }
}
Finally, populate these three lists in a single loop that reads from the file:
public static ParseResult parseFile(File csvFile) {
Scanner scan = null;
try {
scan = new Scanner(csvFile);
} catch (FileNotFoundException e) {
???
// Do something about this exception.
// Consider not catching it here, letting the caller deal with it.
}
final List<KeyValuePair> sanitized = new ArrayList<KeyValuePair>();
final List<KeyValuePair> badKey = new ArrayList<KeyValuePair>();
final List<KeyValuePair> badValue = new ArrayList<KeyValuePair>();
while (scan.hasNext()) {
String[] tokens = scan.nextLine().trim().split(",");
if (tokens.length != 2) {
???
// Do something about this - either throw an exception,
// or log a message and continue.
}
KeyValuePair kvp = new KeyValuePair(tokens[0], tokens[1]);
// Do the validation on the spot
if (!validateRecordKey(kvp.getKey())) {
badKey.add(kvp);
} else if (!validateRecord(kvp.getValue())) {
badValue.add(kvp);
} else {
sanitized.add(kvp);
}
}
return new ParseResult(sanitized, badKey, badValue);
}
Now you have a single function that produces a single result with all your records cleanly separated into three buckets - i.e. sanitized records, records with bad keys, and record with good keys but bad values.
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.