我在for循环中运行selenium测试需要时间。
我需要使用javafx
progressbar
来指明这些测试的进度。
所以我用for task替换了for循环中的代码。
以下是我的代码
runTests()
方法返回String
,显示为Alert
我无法从String
内返回Task<Void>
,因为返回类型为Void
测试代码位于runTest(data)
方法内,返回true
或false
@FXML
private void handleRunTestButton(ActionEvent aEvent) throws IOException {
String testResultMessage = runTests();
if (!testResultMessage.equals("Testing Complete!")) {
Alert alert = DialogUtils.getAlert("Info", "Information", testResultMessage, "info");
alert.showAndWait();
} else {
Alert alert = DialogUtils.getAlert("Error", "Error(s)", testResultMessage, "error");
alert.showAndWait();
}
}
private String runTests() {
/* xlsx read */
FileInputStream fis = null;
File testDataFile = null;
try {
fis = new FileInputStream(selectedTestDataFile);
} catch (FileNotFoundException e) {
return "File Input Stream Error: File Not Found";
}
// Finds the workbook instance for XLSX file
XSSFWorkbook myWorkBook = null;
try {
myWorkBook = new XSSFWorkbook(fis);
} catch (IOException e) {
return "XSSFWorkbook I/O Error";
}
// Return first sheet from the XLSX workbook
XSSFSheet mySheet = myWorkBook.getSheetAt(0);
int totalWids = mySheet.getLastRowNum();
final Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
for (int rowIndex = 1; rowIndex <= totalWids; rowIndex++) {
updateProgress(rowIndex, totalWids);
Row row = mySheet.getRow(rowIndex);
if (row != null) {
String data = "";
Cell cellData = row.getCell(2);
if (cellData != null) {
data = cellWid.getStringCellValue();
boolean testresult = runTest(data);
System.out.println(rowIndex + ". data = " + data + ", testresult = " + testresult);
}
}
}
return null;
}
};
progressBar.progressProperty().bind(task.progressProperty());
progressIndicator.progressProperty().bind(task.progressProperty());
final Thread thread = new Thread(task, "task-thread");
thread.setDaemon(true);
thread.start();
/* xlsx read */
FileOutputStream fos = null;
try {
fos = new FileOutputStream(selectedTestDataFile);
} catch (FileNotFoundException e) {
try {
myWorkBook.close();
} catch (IOException e1) {
return "Error: Please Close Workbook";
}
return "Error: File Not Found";
}
try {
myWorkBook.write(fos);
} catch (IOException e) {
try {
myWorkBook.close();
} catch (IOException e1) {
return "Error: Please Close Workbook";
}
return "Error: Workbook Write";
}
try {
fos.close();
} catch (IOException e) {
try {
myWorkBook.close();
} catch (IOException e1) {
return "Error: Please Close Workbook";
}
return "Error: File Output Stream";
}
try {
myWorkBook.close();
} catch (IOException e) {
return "Error: Please Close Workbook";
}
try {
fis.close();
} catch (IOException e) {
return "Error: Input file format";
}
return "Testing Complete!";
}
但是,现在它在测试仍在运行时返回Testing Complete!
。
我是multithreading
的新手。请建议我如何构建代码。
如何让runTests()
方法从
String
值
final Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
for () {
}
return null;
}
};
在此之前,当我没有使用Task
时,我的代码正确地显示了警报,但是尽管在for循环中设置了进度,但进度条没有更新。
答案 0 :(得分:1)
一般来说,您的代码看起来非常可靠,但有几个问题。
您创建的任务可以解决问题,进度条也可以工作,但它使用一个线程,因此返回测试完成而不确认线程的进度是错误的。因为测试在一个线程中,并且该方法返回一个值而不依赖于它,所以在测试完成之前返回该值。
调用thread.start()
时,线程会从当前线程中单独开始执行,这意味着即使线程没有完成,代码也会继续执行。
您有两种可能的选择:保留线程,或者不保留。如果不保留线程,则意味着测试在方法中执行,该方法导致调用它的javaFX事件等待测试完成。这是一个坏主意,因为现在javaFX线程被卡住了,窗口无法处理任何其他事件(基本上是iresponsive)。
一个好的选择是保留线程,只有在线程结束时才能显示一个对话框,指示测试是否完整。为此,您可以使用Platform.runLater(runnable)
并向其传递一个显示对话框的Runnable
对象:
Platform.runLater(()->{
//show dialog
});
这是必需的,因为你不能在javaFX线程中显示对话框。这允许你在javaFX线程中运行一些东西。
另一个问题是您正在访问线程之外的文件。这意味着在线程运行测试的同时,您尝试访问文件并写入它们。您应该在线程中或在启动之前写入文件,而不是这样做。
为了总结这一切,你应该使用你的线程来执行测试并显示指示测试是否完成的对话框。在线程仍在执行测试时,不应该写入测试文件,而是在线程完成后写入,所以你可以在任务结束时完成。
public void runTests(){
if(testsRunning) return;
testsRunning = true;
final Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
FileInputStream fis = null;
File testDataFile = null;
try {
fis = new FileInputStream(selectedTestDataFile);
} catch (FileNotFoundException e) {
displayResponse("File Input Stream Error: File Not Found");
}
// Finds the workbook instance for XLSX file
XSSFWorkbook myWorkBook = null;
try {
myWorkBook = new XSSFWorkbook(fis);
} catch (IOException e) {
displayResponse("XSSFWorkbook I/O Error");
}
// displayResponse(first sheet from the XLSX workbook
XSSFSheet mySheet = myWorkBook.getSheetAt(0);
int totalWids = mySheet.getLastRowNum();
for (int rowIndex = 1; rowIndex <= totalWids; rowIndex++) {
updateProgress(rowIndex, totalWids);
Row row = mySheet.getRow(rowIndex);
if (row != null) {
String data = "");
Cell cellData = row.getCell(2);
if (cellData != null) {
data = cellWid.getStringCellValue();
boolean testresult = runTest(data);
System.out.println(rowIndex + ". data = " + data + ", testresult = " + testresult);
}
}
}
/* xlsx read */
FileOutputStream fos = null;
try {
fos = new FileOutputStream(selectedTestDataFile);
} catch (FileNotFoundException e) {
try {
myWorkBook.close();
} catch (IOException e1) {
displayResponse("Error: Please Close Workbook");
}
displayResponse("Error: File Not Found");
}
try {
myWorkBook.write(fos);
} catch (IOException e) {
try {
myWorkBook.close();
} catch (IOException e1) {
displayResponse("Error: Please Close Workbook");
}
displayResponse("Error: Workbook Write");
}
try {
fos.close();
} catch (IOException e) {
try {
myWorkBook.close();
} catch (IOException e1) {
displayResponse("Error: Please Close Workbook");
}
displayResponse("Error: File Output Stream");
}
try {
myWorkBook.close();
} catch (IOException e) {
displayResponse("Error: Please Close Workbook");
}
try {
fis.close();
} catch (IOException e) {
displayResponse("Error: Input file format");
}
displayResponse("Testing Complete!");
return null;
}
private void displayResponse(String testResultMessage){
Platform.runLater(()->{
if (testResultMessage.equals("Testing Complete!")) {
Alert alert = DialogUtils.getAlert("Info", "Information", testResultMessage, "info");
alert.showAndWait();
} else {
Alert alert = DialogUtils.getAlert("Error", "Error(s)", testResultMessage, "error");
alert.showAndWait();
}
testsRunning = false;
});
}
};
progressBar.progressProperty().bind(task.progressProperty());
progressIndicator.progressProperty().bind(task.progressProperty());
final Thread thread = new Thread(task, "task-thread");
thread.setDaemon(true);
thread.start();
}
因此,此代码现在可以在线程中执行所有相关测试,并且不会因处理事件而中断窗口。这有一个问题:有人可能会在测试运行时再次按下runTests
按钮。一种选择是使用一个布尔值来指示测试是否已经处于活动状态,并在调用runTests
时检查其值,我将其添加并称为testsRunning
。当测试完成(已完成或未完成)并显示响应对话框时,将调用displayResponse
。
希望我能帮忙,并为长时间的回答感到抱歉。
答案 1 :(得分:0)
首先,你不能在没有阻塞的情况下从线程中返回一个值。相反,尝试在线程完成时调用方法。
假设我们有这个线程运行一些密集型任务(在这种情况下是一个简单的forloop),你希望它在完成时“返回”总和:
private void startMyThread() {
Thread t = new Thread( () -> {
System.out.println("Thread Started.");
// Some intensive task
int sum = 0;
for(int i = 0; i < 1000000; i++) {
sum++;
}
System.out.println("Thread ending.");
threadIsDone(sum);
});
System.out.println("Starting Thread.");
t.start();
}
而不是从startMyThread()
获取返回值,而是等到执行您的操作,直到调用threadIsDone()
:
private void threadIsDone(int sum) {
Platform.runLater( () -> {
/* Update Progress Bar */
System.out.println("Updated Progress Bar");
});
System.out.println("Thread ended.");
}
你会注意到我在方法中使用Platform.runLater()
,因为JavaFx元素的所有更新都需要在主线程上完成,因为我们从另一个线程调用threadIsDone()
,我们需要告诉JavaFx我们希望它在主线程上执行此操作,不当前线程。