I am beginner with selenium tests. I wrote this code and it works, but I need to loop this test with another csv line.
I spent almost 10 hours trying to do this.
Scenario of what I am trying to do:
The web browser is opening go to url
login with data from CSV file from first line
Driver is restarting and do the same but with data from second line from csv file.
I also tried to restart test with aftermethod/afterclass but it's not working.
public class CSVdataread {
private WebDriver driver;
String baseUrl = "URL";
String CSV_file = "C:\\Users\\xxxxxxxxxxx\\Desktop\\TestData.csv";
#BeforeClass
public void openBrowser() {
System.setProperty("webdriver.chrome.driver", "C:\\Users\\xxxxxxxxxxxx\\Desktop\\webdriver\\chromedriver.exe");
driver = new ChromeDriver();
driver.navigate().to("URL");
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Test
public void verify_Search() throws InterruptedException, IOException {
CSVReader reader = new CSVReader(new FileReader(CSV_file));
String[] cell;
while((cell = reader.readNext()) != null)
for (int i = 0; i < 1; i++) {
String name = cell[i];
String email = cell[i + 1];
String baseUrl = "http://xxxxx.xxx/xxxx/";
driver.findElement(By.xpath("//input[#id='useridFormField-inputEl']")).sendKeys(name);
driver.findElement(By.xpath("//input[#id='userpasswordFormField-inputEl']")).sendKeys(email);
{
driver.quit();
}
}
}
}
JUnit 4 solution. This one is going to be huge...
First, let's start off with CSVReader and some good practices plus code readability. In your test, you read CSV data and use them in your tests. It's not the test's responsibility to read data. The test should already have all the data provided to it. It's called DataProvider. This term is actually used in TestNG testing framework, just like #user861594 suggested.
So, you should have something to provide data to your tests. But this already Step #2. Since you know you will read data row-by-row from CSV file, you should create a proper class to read the data from CSV.
Here's an example:
public class CSVReader {
private static final String DEFAULT_SEPARATOR = ",";
private BufferedReader reader;
private List<String> lines;
public CSVReader(File file) throws FileNotFoundException {
this.reader = new BufferedReader(new FileReader(file));
lines = this.reader.lines().collect(Collectors.toList());
}
public String[] getRow(int rowNumber) {
return lines.get(rowNumber).split(DEFAULT_SEPARATOR);
}
public int getRowCount() {
return lines.size();
}
}
The CSVReader constructor accepts a File as an argument and creates proper objects to read data in a specific manner (for example: read as String). Then, the data in the CSV file is read, just like in normal TXT file by saving the lines in the memory for later use.
Then we create 2 methods. First is getRowCount which gives us the total number of row/set of data.
Second is getRow which collect the specific row from the list and saves it to String[] array for later use.
String[] array has a presentation like 1 Excel row:
data index 0 | data index 1 | data index 2 | data index 3
We have a class which allows us to read the file in an easy matter. Let's create the DataProvider
To provide data to tests, we need to use #Parameters annotation and return Collection<Object[]> to our test. I will talk later about that.
So, let's implement it in our DataProvider
public class CSVDataProvider {
public Collection<Object[]> getData() throws FileNotFoundException {
CSVReader reader = new CSVReader(new File("C:\\Users\\xxxxxxxxxxx\\Desktop\\TestData.csv"));
int rowCount = reader.getRowCount();
Object[][] data = new Object[rowCount][2];
for (int i = 0; i < rowCount; i++) {
Object[] singleRow = reader.getRow(i);
data[i][0] = singleRow[0];
data[i][1] = singleRow[1];
}
return Arrays.asList(data);
}
}
I assume that you have only logins and passwords in the CSV file. That's why I created a 2-dimensional array new Object[rowCount][2]. We create the array by providing how many elements it has to store and we know how many rows we have from rowCount variable.
2 means we have only 2 data per row. Login and password. If you want to use additional element, for example - the role of the user, you can modify to [3]
In the for loop we are transforming the data from the CSV file to array and return it for later use.
Now, let's talk about our test class.
#RunWith(Parameterized.class)
public class OurTest {
private String login, password;
public OurTest(String login, String password) {
this.login = login;
this.password = password;
}
#Parameterized.Parameters(name = "{index}: Login: ({0}) Password: ({1})")
public static Collection<Object[]> data() throws FileNotFoundException {
return new CSVDataProvider().getData();
}
#Test
public void test() {
System.out.println(String.format("login : %s | Password: %s", login, password));
}
}
In order to pass the parameters from DataProvider to our test, we need to
1. Annotate the class with #RunWith(Parameterized.class)
2. Create a method returning Collection<Object[]> with annotation#Parameters`
3. Create a constructor reflecting what kind of data do we accept.
Regarding point 3, that's why I created a 2 argument constructor with String login and String password. We are passing those 2 parameters. JUnit will create a new instance of OurTest and pass different row for each test.
In the test method I just printed the data we've got from the DataProvider
I do not present a fully working solution because I want you to try to adjust your test to learn this specific approach. It's also called Data-driven Testing.
We have only 1 test method but each line in the CSV file will run as a separate test.
Hope it helps!
Your while loop looks broken. The for loop inside the while loop seems to mess up your login procedure.
while((cell = reader.readNext())!=null) { // iterate through csv file
String name = cell[0]; // cell is current row, you need first column for name
String email = cell[1]; // second column for email (as password?)
// what do you want to do with baseUrl here?
driver.findElement(By.xpath("//input[#id='useridFormField-inputEl']")).sendKeys(name);
driver.findElement(By.xpath("//input[#id='userpasswordFormField-inputEl']")).sendKeys(email);
// you need to check the successful login here
// then logout and open main page
// do not quit before you are finished
}
// quit after the loop is finished
driver.quit();
Without any knowledge of the website it is impossible to tell you how to check successful login and perform logout.
May I suggest you put some effort in learning the ropes with a less complex task? You seem to have a lot of trouble with basic Java elements. Never stop learning.
It looks you want to iterate your test with set of test data. In that case you should use TestNG data provider feature.
public class CSVdataread {
private WebDriver driver;
String baseUrl = "URL";
String CSV_file = "C:\\Users\\xxxxxxxxxxx\\Desktop\\TestData.csv";
#BeforeMethod
public void openBrowser() {
System.setProperty("webdriver.chrome.driver", "C:\\Users\\xxxxxxxxxxxx\\Desktop\\webdriver\\chromedriver.exe");
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
}
#Test(dataProvider="users-data")
public void verify_Search(String name, String email) throws InterruptedException, IOException {
String baseUrl = "http://xxxxx.xxx/xxxx/";
driver.navigate().to(baseUrl);
driver.findElement(By.xpath("//input[#id='useridFormField-inputEl']")).sendKeys(name);
driver.findElement(By.xpath("//input[#id='userpasswordFormField-inputEl']")).sendKeys(email);
}
//This method will provide data to any test method that declares that its Data Provider
#DataProvider(name = "users-data")
public Iterator<Object[]> createDataFromCSV() {
CSVReader reader = new CSVReader(new FileReader(CSV_file));
List<Object[]> data = new ArrayList<Object[]>();
//read csv data to list
return data.iterator();
}
#AfterMethod
public void closeBrowser() {
driver.quit();
}
}
You can also utilize available data-provider-extension. For example, with qaf You don't need to write code for driver management or for data provider. Your test class will look like below:
public class CSVdataread extends WebDriverTestCase{
#QAFDataProvider(dataFile="resources/user-data.csv")
#Test()
public void verify_Search(String name, String email) throws InterruptedException, IOException {
String baseUrl = "http://xxxxx.xxx/xxxx/";
getDriver().navigate().to(baseUrl);
getDriver().findElement(By.xpath("//input[#id='useridFormField-inputEl']")).sendKeys(name);
//another way of finding element...
getDriver().findElement("xpath=//input[#id='userpasswordFormField-inputEl']").sendKeys(email);
}
}
Related
I am trying to learn java and selenium by myself and creating a robot that will scan job/career pages for certain string (job name e.g. QA , developer...)
I'm trying to create JAVA code using selenium, that will read URL links from CSV file and open a new tab.
the main goal is to add several url in the CSV and assert/locate a certain string in the designated url's for example: is there "Careers" link in each URL, the test will pass for this specific url.
created a selenium project
created new chromeDriver
Created CSV built from 3 columns (ID, company's name, URL) - and added it to the project
import org.openqa.selenium.chrome.ChromeDriver;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class URLSearch {
public static void main(String[] args) {
ChromeDriver driver = new ChromeDriver();
driver.manage().window().maximize();
String fileName = "JobURLList.csv";
File file = new File(fileName); //read from file
try {
Scanner inputStream = new Scanner(file);
while (inputStream.hasNext()) {
String data = inputStream.next();
System.out.println(data);
}
inputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
first line in the CSV - titles: id, name, url
Read the url from the second line - e.g. https://careers.google.com/jobs/"
open browsertab and start going over the url list (from the CSV)
locate a hardcoded string (e.g. "developer" , "qa" ..) in each url
if such a string was found, write in console the url that the test turned out to be positive (such a string was found in one of the url's).
if no such string was found, skip to the next url.
To open the new tab do something like this (this assumes "driver" object is your WebDriver):
((JavascriptExecutor)driver).executeScript("window.open('about:blank', '_blank');");
Set<String> tab_handles = driver.getWindowHandles();
int number_of_tabs = tab_handles.size();
int new_tab_index = number_of_tabs-1;
driver.switchTo().window(tab_handles.toArray()[new_tab_index].toString());
You could then create a function that takes a list of key/value pairs, with URL and term to search for and loop through it. Do you want to use a hashmap for this, or maybe an ArrayList of a class (id/name/url)? The code for finding the text would be something like this (assumes you've defined a var of "Pass" to boolean):
driver.get([var for URL]);
//driver will wait for pageready state, so you may
// not need the webdriver wait used below. Depends
// on if the page populates data after pagereadystate
String xpather = "//*[contains(text(), '" + [string var for text to search for] + "')]";
try
{
wait = new WebDriverWait(driver, 10);
List<WebElement> element = wait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.xpath(xpather)));
this.Pass = false;
if (element.size() > 0)
{
this.Pass = true;
}
}
catch (Exception ex)
{
this.Pass = false;
System.out.println ("Exception finding text: " + ex.toString());
}
Then logic for if (this.Pass==true or false)..
I'm using Java with JDBC to run MySql code. I want to execute a DDL script, but JDBC can only execute a single statement at a time, which makes it unsuitable to execute a whole .sql file out of the box.
What I'm trying to do is use Antlr4 to parse the .sql file so I can break up each individual statement and then iteratively execute them with JDBC.
I've gotten this far:
InputStream resourceAsStream = Main.class.getClassLoader()
.getResourceAsStream("an-arbitrary-ddl.sql");
CharStream codePointCharStream = CharStreams.fromStream(resourceAsStream);
MySqlLexer tokenSource = new MySqlLexer(new CaseChangingCharStream(codePointCharStream, true));
TokenStream tokenStream = new CommonTokenStream(tokenSource);
MySqlParser mySqlParser = new MySqlParser(tokenStream);
// Where do I go from here?
I'm sure I'm just not searching for the correct terms because I'm new to Antlr and manually parsing code. I can't find any reference from here as to what I need to do to get individual sql statements out of the MySqlParser. What do I need to do next?
A parser is not the right tool for this kind of problem. A statement splitter is pretty easy to write manually and much faster if you do it yourself. I implemented such a splitter in C++ in MySQL Workbench. Shouldn't be difficult to port this to Java. The code is very fast (1 Mio LOC SQL code in under 1 sec on an average machine). A parser would need much longer.
I'm sure this can be improved, however, as the most simple way I could create this was creating a listener and provide the constructor with a Consumer<String> object. The listener looks at individual statements and recursively constructs them. There is probably a more optimal solution, however, I no longer have time to try to optimize this if there is.
/**
* #author Paul Nelson Baker
* #see GitHub
* #see LinkedIn
* #since 2018-09
*/
public class SqlStatementListener extends MySqlParserBaseListener {
private final Consumer<String> sqlStatementConsumer;
public SqlStatementListener(Consumer<String> sqlStatementConsumer) {
this.sqlStatementConsumer = sqlStatementConsumer;
}
#Override
public void enterSqlStatement(MySqlParser.SqlStatementContext ctx) {
if (ctx.getChildCount() > 0) {
StringBuilder stringBuilder = new StringBuilder();
recreateStatementString(ctx.getChild(0), stringBuilder);
stringBuilder.setCharAt(stringBuilder.length() - 1, ';');
String recreatedSqlStatement = stringBuilder.toString();
sqlStatementConsumer.accept(recreatedSqlStatement);
}
super.enterSqlStatement(ctx);
}
private void recreateStatementString(ParseTree currentNode, StringBuilder stringBuilder) {
if (currentNode instanceof TerminalNode) {
stringBuilder.append(currentNode.getText());
stringBuilder.append(' ');
}
for (int i = 0; i < currentNode.getChildCount(); i++) {
recreateStatementString(currentNode.getChild(i), stringBuilder);
}
}
}
Next you need to traverse the statements, the string consumer from earlier allows you to lazily redirect the output wherever you need. This can be as simple as just printing to stdout, however, it can just as easily be used to append to a list.
public List<String> mySqlStatementsFrom(String sourceCode) {
List<String> statements = new ArrayList<>();
mySqlStatementsToConsumer(sourceCode, statements::add);
return statements;
}
public void mySqlStatementsToConsumer(String sourceCode, Consumer<String> mySqlStatementConsumer) {
CharStream codePointCharStream = CharStreams.fromString(sourceCode);
MySqlLexer tokenSource = new MySqlLexer(new CaseChangingCharStream(codePointCharStream, true));
TokenStream tokenStream = new CommonTokenStream(tokenSource);
MySqlParser mySqlParser = new MySqlParser(tokenStream);
SqlStatementListener statementListener = new SqlStatementListener(mySqlStatementConsumer);
ParseTreeWalker.DEFAULT.walk(statementListener, mySqlParser.sqlStatements());
}
I am working on Selenium WebDriver with Java for automation and TestNG is my framework. I am running Login Test in which i log each step in the Extent report. I have a function for each step and for each step, i am attaching a screenshot.
I am unsure on how to name each screenshot with a unique descriptive name. I tried getting the current method(step) name but it seems like I need to create an anonymous class eveytime and eveywhere to get the current running method name as per the below code.
String name = new Object(){}.getClass().getEnclosingMethod().getName();
Here is my code.
#Test(priority = 0, testName="Verify Login")
public void login() throws Exception {
lp = new LoginPage(driver, test);
tm = new TabMenu(driver, test);
driver.get(Constants.url);
lp.verifyLoginPageLogo();
lp.setUserName("admin");
lp.setPassword("admin");
lp.clickLoginBtn();
tm.isCurrentTab("Dashboard");
}
public void verifyLoginPageLogo() throws IOException {
String name = new Object(){}.getClass().getEnclosingMethod().getName();
Assert.assertTrue(loginLogo.isDisplayed());
test.log(LogStatus.PASS, "Logo is displayed", Screenshots.takeScreenshot(driver, name, test));
}
public static String takeScreenshot(WebDriver driver, String name, ExtentTest test) throws IOException {
String directory = "C:\\Users\\JACK\\Documents\\eclipse-workspace\\OrangeHRM\\Screenshots\\";
String fileName = name + ".png";
File destFile = new File(directory + fileName);
TakesScreenshot ss = (TakesScreenshot) driver;
File sourceFile = ss.getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(sourceFile, destFile);
String imgPath = test.addScreenCapture(directory+fileName);
return imgPath;
}
Is there any other way to do this?
Sure, there are lots of options:
If you don't care what the filename is, you could use File.createTempFile() or just UUID.randomUUID() to get a name. That wouldn't be semantically meaningful, but it would get you unique filenames easily enough.
If you don't need multiple filenames per test, you could use the test name. In JUnit you could use the Test-Name Rule. For TestNG, there seem to be other solutions using #Before methods. Even if you do need multiples, you could combine test-name-rule with a sequence number.
You could just use a sequence number, like an integer in a static variable / singleton. It's not very sophisticated, but it would still work. ;)
Java doesn't make it particularly easy to get the current method name, so you'd either have to do your anonymous object or get a stack trace, either way is a bit painful and not particularly performant.
Using #2 as an example, you could modify your code do something like:
#Test(priority = 0, testName="Verify Login")
public void login(ITestContext context) throws Exception {
lp = new LoginPage(driver, test);
tm = new TabMenu(driver, test);
driver.get(Constants.url);
lp.verifyLoginPageLogo(context.getName(), 0);
lp.setUserName("admin");
lp.setPassword("admin");
lp.clickLoginBtn();
tm.isCurrentTab("Dashboard", context.getName(), 1);
}
public void verifyLoginPageLogo(String testName, int stepName) throws IOException {
String name = new Object(){}.getClass().getEnclosingMethod().getName();
Assert.assertTrue(loginLogo.isDisplayed());
test.log(LogStatus.PASS, "Logo is displayed", Screenshots.takeScreenshot(driver, testName, stepName, test));
}
public static String takeScreenshot(WebDriver driver, String testName, int stepName, ExtentTest test) throws IOException {
String fileName = testName + "_" + stepName ".png";
// the rest of your screenshot code
}
You could even replace the step number with another semantically meaningful word, "loginLogo", "dashboardTab" if that's helpful
I have a test ready to be executed but it takes a long time to finish. In this test I'm feeding in csv data, so basically the whole test will run 56 times. I was wondering if there's anyway I could use multiple browser instance and divide the workload to four instance. It will save me some time. I tried to use TestNG's ThreadPoolSize but it's not doing what I want it to. It's using the same data for four instances of firefox. I want each browser to have it's own unique data. Please check my code and let me know what I'm missing. I really appriciate every one's help.
public class StudentPageTest {
WebDriver driver;
DesiredCapabilities capability;
WebElement element;
WebDriverWait wait;
private String baseURL;
#BeforeTest
public void setUp() throws MalformedURLException{
//capability = DesiredCapabilities.firefox();
//driver = new FirefoxDriver();
//wait = new WebDriverWait(driver, 120);
//driver.manage().deleteAllCookies();
baseURL = "http://somewebsite.com";
}
#SuppressWarnings("resource")
#Test(threadPoolSize = 4)
public void StudentPortalTest() throws InterruptedException, IOException{
driver = new FirefoxDriver();
wait = new WebDriverWait(driver, 120);
driver.manage().deleteAllCookies();
String studentId = "studentID.csv";
BufferedReader br = null;
String line = "";
String cvsSplitBy = ",";
br = new BufferedReader(new FileReader(studentId));
while ((line = br.readLine()) != null) {
String[] student_id = line.split(cvsSplitBy);
//Logging in Student Portal---------------------------------------------------------------------------------------------------------------|
for (int i = 0; i < student_id.length; i++) {
driver.get(baseURL+student_id[i]);
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
driver.findElement(By.cssSelector(".logo>img")).isDisplayed();
driver.findElement(By.cssSelector("#UserName")).sendKeys("SecretUserName");
driver.findElement(By.cssSelector("#Password")).sendKeys("EvenMoreSecretPassword");
driver.findElement(By.cssSelector(".submitBtn")).click();
driver.manage().timeouts().implicitlyWait(120, TimeUnit.SECONDS);
Thread.sleep(4000);
...............and the test goes on below...................
}
#AfterTest
public void tearDown(){
driver.quit();
}
}
You can let the Selenium grid do the task of distribution. Use TestNG to run your cases in parallel using dataprovider. Use a dataprovider to read your csv and pass one data to one #Test.
Set dataprovider thread count in your suite xml and make sure you set #DataProvider(parallel=true)
Going to your code, replace your call of instantiating a firefoxdriver to remotewebdriver to use the grid. Make sure your driver is not at class level but is a threadlocal : threadlocal<driver>.
I was trying many different ways to achieve the same thing you are trying to do. After a lot of trial and error I finally came across a solution that worked for me.
You need Selenium Grid You can then set up a few nodes to run your different browsers from
You need TestNG like you have been using but instead of trying to use threads, you will use an .xml file to pass in parameters to your test which will run the one test over and over in parallel.
If you read this document carefully and follow the steps, you should be able to achieve the same result
#Test(threadPoolSize = 4, invocationCount = 4)
I need to implement email confirmation in my java web application. I am stuck with the email I have to send to the user.
I need to combine a template (of an confirmation email) with the User object and this will be the html content of the confirmation email.
I thought about using xslt as the template engine but I don't have xml form of the User object and don't really know how to create a xml from User instance.
I thought about jsp, but how do I render jsp page with an object and get the html as a result?
Any idea what packages I can use in order to create templae and combine it with an object?
I have used the following before. I seem to recall it wasn't complicated
http://velocity.apache.org/
How complex is the user object? If it's just five string-valued fields (say) you could simply supply these as string parameters to the transformation, avoiding the need to build XML from your Java data.
Alternatively, Java XSLT processors typically provide some way to invoke methods on Java objects from within the XSLT code. So you could supply the Java object as a parameter to the stylesheet and invoke its methods using extension functions. The details are processor-specific.
Instead of learning a new code, debug other's complicate code I decided to write my own small and suitable util:
public class StringTemplate {
private String filePath;
private String charsetName;
private Collection<AbstractMap.SimpleEntry<String, String>> args;
public StringTemplate(String filePath, String charsetName,
Collection<AbstractMap.SimpleEntry<String, String>> args) {
this.filePath = filePath;
this.charsetName=charsetName;
this.args = args;
}
public String generate() throws FileNotFoundException, IOException {
StringBuilder builder = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(
getClass().getResourceAsStream(filePath),charsetName));
try {
String line = null;
while ((line = reader.readLine()) != null) {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
} finally {
reader.close();
}
for (AbstractMap.SimpleEntry<String, String> arg : this.args) {
int index = builder.indexOf(arg.getKey());
while (index != -1) {
builder.replace(index, index + arg.getKey().length(), arg.getValue());
index += arg.getValue().length();
index = builder.indexOf(arg.getKey(), index);
}
}
return builder.toString();
}
}