0 java junit swing jmenuitem actionevent
我是JUnit测试的新手.我正在尝试测试导出报告的方法.基本上,此方法会弹出一个保存菜单,以选择保存文件的位置,并从另一个类获取报告.我不确定我需要在这里测试什么或者如何测试它.我已经添加了我的JMenuItem和我的actionEvent.任何想法或帮助都会被大大接受.
这是我的JMenuItem:
JMenuItem jMenuFileexportProjectReport = new JMenuItem(exportProjectReportAction);
Run Code Online (Sandbox Code Playgroud)
这是我对JMenuItem的Action事件:
public Action exportProjectReportAction =
new AbstractAction(Local.getString("Export Project Report")) {
public void actionPerformed(ActionEvent e) {
reportExportAction(e);
}
};
Run Code Online (Sandbox Code Playgroud)
以下是导出报告的方法:
public void reportExportAction(ActionEvent e) {
JFileChooser chooser = new JFileChooser();
chooser.setFileHidingEnabled(false);
chooser.setDialogTitle(Local.getString("Export Project Report"));
chooser.setAcceptAllFileFilterUsed(false);
chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
chooser.addChoosableFileFilter(
new AllFilesFilter(AllFilesFilter.XHTML));
chooser.addChoosableFileFilter(new AllFilesFilter(AllFilesFilter.HTML));
String lastSel = (String) Context.get("LAST_SELECTED_EXPORT_FILE");
if (lastSel != null) {
chooser.setCurrentDirectory(new File(lastSel));
}
ProjectExportDialog dlg =
new ProjectExportDialog(
App.getFrame(),
Local.getString("Export Project Report"),
chooser);
String enc = (String) Context.get("EXPORT_FILE_ENCODING");
if (enc != null) {
dlg.encCB.setSelectedItem(enc);
}
Dimension dlgSize = new Dimension(550, 500);
dlg.setSize(dlgSize);
Dimension frmSize = App.getFrame().getSize();
Point loc = App.getFrame().getLocation();
dlg.setLocation(
(frmSize.width - dlgSize.width) / 2 + loc.x,
(frmSize.height - dlgSize.height) / 2 + loc.y);
dlg.setVisible(true);
if (dlg.CANCELLED) {
return;
}
Context.put(
"LAST_SELECTED_EXPORT_FILE",
chooser.getSelectedFile().getPath());
int ei = dlg.encCB.getSelectedIndex();
enc = null;
if (ei == 1) {
enc = "UTF-8";
}
boolean nument = (ei == 2);
File f = chooser.getSelectedFile();
boolean xhtml =
chooser.getFileFilter().getDescription().indexOf("XHTML") > -1;
CurrentProject.save();
ReportExporter.export(CurrentProject.get(), chooser.getSelectedFile(), enc, xhtml,
nument);
}
Run Code Online (Sandbox Code Playgroud)
创建HTML报告的类:
public class ReportExporter {
static boolean _chunked = false;
static boolean _num = false;
static boolean _xhtml = false;
static boolean _copyImages = false;
static File output = null;
static String _charset = null;
static boolean _titlesAsHeaders = false;
static boolean _navigation = false;
static String charsetString = "\n";
public static void export(Project prj, File f, String charset, boolean xhtml, boolean chunked) {
_chunked = chunked;
_charset = charset;
_xhtml = xhtml;
if (f.isDirectory()) {
output = new File(f.getPath() + "/Project Report.html");
}
else {
output = f;
}
NoteList nl = CurrentStorage.get().openNoteList(prj);
Vector notes = (Vector) nl.getAllNotes();
//Creates Labels for the HTML output for each section.
String notesLabelHTML = "Notes";
String tasksLabelHTML = "Tasks";
String eventsLabHTML = "Events";
//NotesVectorSorter.sort(notes);
Collections.sort(notes);
Writer fw;
if (output.getName().indexOf(".htm") == -1) {
String dir = output.getPath();
String ext = ".html";
String nfile = dir + ext;
output = new File(nfile);
}
try {
if (charset != null) {
fw = new OutputStreamWriter(new FileOutputStream(output),
charset);
charsetString = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset="
+ charset + "\" />";
}
else
fw = new FileWriter(output);
}
catch (Exception ex) {
new ExceptionDialog(ex, "Failed to write to " + output, "");
return;
}
//Writes the title and the notes section of the HTMl Report
write(fw, "<html>\n<head>\n" + charsetString + "<title>"
+ prj.getTitle()
+ "</title>\n</head>\n<body>\n<h1 class=\"projecttitle\">"
+ prj.getTitle() + "</h1>\n" +"\n<br>\n"
+ "</title>\n</head>\n<body>\n<h2 class=\"projecttitle\">"
+ notesLabelHTML + "</h2>\n" );
generateChunks(fw, notes);
//Writes the Task section of the HTML Report
write(fw, "\n<hr></hr><a" +"</title>\n</head>\n<body>\n<h2 class=\"projecttitle\">" + "\n<br>\n"
+ tasksLabelHTML + "</h2>\n" );
//writes the Events section of the HTML Report
write(fw, "\n<hr></hr><a" +"</title>\n</head>\n<body>\n<h2 class=\"projecttitle\">" + "\n<br>\n"
+ eventsLabHTML + "</h2>\n" );
//Writes the ending of the report with the data and time
write(fw, "\n<hr></hr><a "
+ "\n<br></br>\n" + new Date().toString()
+ "\n</body>\n</html>");
try {
fw.flush();
fw.close();
}
catch (Exception ex) {
new ExceptionDialog(ex, "Failed to write to " + output, "");
}
}
public static String getNoteHTML(Note note) {
String text = "";
StringWriter sw = new StringWriter();
AltHTMLWriter writer = new AltHTMLWriter(sw,
(HTMLDocument) CurrentStorage.get().openNote(note), _charset,
_num);
try {
writer.write();
sw.flush();
sw.close();
}
catch (Exception ex) {
new ExceptionDialog(ex);
}
text = sw.toString();
if (_xhtml) {
text = HTMLFileExport.convertToXHTML(text);
}
text = Pattern
.compile("<body(.*?)>", java.util.regex.Pattern.DOTALL
+ java.util.regex.Pattern.CASE_INSENSITIVE).split(text)[1];
text = Pattern
.compile("</body>", java.util.regex.Pattern.DOTALL
+ java.util.regex.Pattern.CASE_INSENSITIVE).split(text)[0];
text = "<div class=\"note\">" + text + "</div>";
if (_titlesAsHeaders) {
text = "\n\n<div class=\"date\">"
+ note.getDate().getFullDateString()
+ ":</div>\n<h1 class=\"title\">" + note.getTitle()
+ "</h1>\n" + text;
}
return text;
}
private static String generateNav(Note prev, Note next) {
String s = "<hr></hr><div class=\"navigation\"><table border=\"0\" width=\"100%\" cellpadding=\"2\"><tr><td width=\"33%\">";
if (prev != null) {
s += "<div class=\"navitem\"><a href=\"" + prev.getId() + ".html\">"
+ Local.getString("Previous") + "</a><br></br>"
+ prev.getDate().getMediumDateString() + " "
+ prev.getTitle() + "</div>";
}
else {
s += " ";
s += "</td><td width=\"34%\" align=\"center\"><a href=\""
+ output.getName()
+ "\">Up</a></td><td width=\"33%\" align=\"right\">";
}
if (next != null) {
s += "<div class=\"navitem\"><a href=\"" + next.getId() + ".html\">"
+ Local.getString("Next") + "</a><br></br>"
+ next.getDate().getMediumDateString() + " "
+ next.getTitle() + "</div>";
}
else {
s += " ";
}
s += "</td></tr></table></div>\n";
return s;
}
private static void generateChunks(Writer w, Vector notes) {
Object[] n = notes.toArray();
for (int i = 0; i < n.length; i++) {
Note note = (Note) n[i];
CalendarDate d = note.getDate();
if (_chunked) {
File f = new File(output.getParentFile().getPath() + "/"
+ note.getId()
+ ".html");
Writer fw = null;
try {
if (_charset != null) {
fw = new OutputStreamWriter(new FileOutputStream(f),
_charset);
}
else {
fw = new FileWriter(f);
}
String s = "<html>\n<head>\n"+charsetString+"<title>" + note.getTitle()
+ "</title>\n</head>\n<body>\n" + getNoteHTML(note);
if (_navigation) {
Note nprev = null;
if (i > 0) {
nprev = (Note) n[i - 1];
}
Note nnext = null;
if (i < n.length - 1) {
nnext = (Note) n[i + 1];
}
s += generateNav(nprev, nnext);
}
s += "\n</body>\n</html>";
fw.write(s);
fw.flush();
fw.close();
}
catch (Exception ex) {
new ExceptionDialog(ex, "Failed to write to " + output, "");
}
}
else {
write(w, "<a name=\"" + "\">" + note.getDate() +"</a>\n" + getNoteHTML(note) + "</a>\n");
}
}
}
private static void write(Writer w, String s) {
try {
w.write(s);
}
catch (Exception ex) {
new ExceptionDialog(ex, "Failed to write to " + output, "");
}
}
Run Code Online (Sandbox Code Playgroud)
正如已经提到的那样,基本上你应该从GUI代码移出应用程序逻辑.
单元测试通常不像写入工作代码那样工作,然后添加测试.首先你应该考虑一下
直接测试GUI代码通常不起作用(除了一些罕见且设计良好的UI框架),因为您的测试必须处理许多技术问题(如框架初始化,UI对象的实例化,正确触发事件等) .所以你应该创建一个更抽象的应用层.从更大的角度来看,它将导致众所周知的模型 - 视图 - 控制器模式(自Smalltalk以来用户界面的事实上的标准设计模式),其中模型层独立于UI框架,因此它是可测试的.
因此,作为某种案例研究,让我们来看看上面的概念:
首先让我们检查什么是可测试的,什么不是,以及什么阻止你编写测试:
chooser.setDialogTitle(Local.getString("Export Project Report"));
这条单线处理渲染和i18n,这不是一个好主意.此外,静态方法的使用是用于编写测试的顶级阻止反模式.
String lastSel =(String)Context.get("LAST_SELECTED_EXPORT_FILE");
静态方法再次调用此处,也很难确定Context类的责任.
Local.getString("导出项目报告")
也是一个静态调用,也是一种重复的代码
...等等(到处都是静态调用).现在让我们看看我们可以为此创建哪种更抽象的模型.首先让我们从需求的一些文字描述开始:
不稳定的部分:
现在我们将设计一个可测试的模型类.在这样做的同时,我们将牢记两个原则:
所以现在让我们为此创建一些抽象模型(代码后的摘要):
public class ProjectExportModel {
// see the reasoning below
public static ProjectExportModel create() {
return new ProjectExportModel(Local::getString,
(String) Context.get("LAST_SELECTED_EXPORT_FILE"),
Context::put);
}
private final Function<String, String> i18n;
private final File lastSelectedExportFile;
private final Consumer<File> lastSelectedFileSaver;
private String encoding;
private boolean nument;
private boolean xhtml;
public ProjectExportModel(final Function<String, String> i18n, final File lastSelectedExportFile,
final Consumer<File> lastSelectedFileSaver) {
this.i18n = i18n;
this.lastSelectedExportFile = lastSelectedExportFile;
this.lastSelectedFileSaver = lastSelectedFileSaver;
}
/**
* Called after a file has been selected from the JFileChooser
*
* Things to test:
* - lastSelectedFileSaver.accept(file.getPath()) should be called - you may use a
* mocking library to test
* - the xhtml flag should be changed - testing is easy
*
*/
public void fileSelected(final File file) {
// TODO
}
/**
* At this point we break a bit the concept of the UI-independent model layer, since Point and Dimension
* are UI-framework-related classes. But these 2 classes are easy to instantiate and easy to assert on the
* returned value, so good-enough solution this time.
*/
public Point getDialogLocation(final Dimension frameSize, final Point frameLocation) {
return null; // TODO implement the positioning
}
public String getFrameTitle() {
// TODO test if it calls and returns i18n.get("Export Project Model") - you need mocking here too
return null;
}
/**
* Two things to be tested here:
* - if CurrentProject.save() is called
* - if ReportExporter.export(...) is called with the right parameters
*
* You are quite stuck here, since static methods cannot be mocked. Instead it would be better to change your APIs to make
* these instance methods, since in the current way it is untestable. After changing these to instance methods, you should add
* 2 more parameters to the constructor: a Project instance and a ReportExporter instance.
* You can use mockito or easymock for mocking.
*/
public void save() {
}
/**
* You may call it from the view layer after calling fileSelected().
*
* To be tested:
* - the proper change of the encoding member
* - the proper change of the nument member
*/
public void selectedEncodingChanged(final int selectedIndex) {
// TODO implemenent the change of encoding and nument member
}
}
Run Code Online (Sandbox Code Playgroud)
摘要:
| 归档时间: |
|
| 查看次数: |
191 次 |
| 最近记录: |