0328复利软件4.0测试
一、项目简介
项目名称:复利计算软件 |
版 本 :4.0 |
版本类型:java |
使用工具:eclipse+swing插件 |
主要功能:本金,利率,年数及期待值的相互转换以及存款的实现 |
备 注 :本代码仅作为学习交流用途,目前不适宜发布,更多功能及后续开发请关注本博客园 |
二、新的功能需求
以商业标准来要求数据保证万无一失
三、实验代码
本代码多处使用try catch语句保护数据。在此仅使用一个例子测试说明
源代码:
else if (b4.getText().equals("") && b1.getText() != null && b3.getText() != null && b2.getText() != null) // 求期望值 { try { rate = Double.parseDouble(b2.getText()); if (rate < 0) { JOptionPane.showMessageDialog(null, "利率输入有误,请重新输入数字"); rate = 0; } n = Integer.parseInt(b3.getText()); if (n < 0) { JOptionPane.showMessageDialog(null, "年份输入有误,请重新输入数字"); n = 0; } principal = Double.parseDouble(b1.getText()); // b1本金 if (principal < 0) { JOptionPane.showMessageDialog(null, "本金输入有误,请重新输入数字"); rate = 0; } } catch (Exception e2) { // TODO: handle exception JOptionPane.showMessageDialog(null, "数据输入有误,请重新输入数字"); } double tempprincipal = principal; result = JOptionPane.showConfirmDialog(null, "是否每年添加本金"); if (result == JOptionPane.YES_OPTION) { // while (year <= n) { // amount = principal * Math.pow(1 + rate, year) // + tempprincipal; // output += String.valueOf(year) + "\t\t\t" // + currencyformatter.format(amount) + "\n"; // year = year + 1; // } amount=CalculateA(year,n,principal,rate,tempprincipal); System.out.println("CalculateA(year,n,principal,rate,tempprincipal)"+year+n+principal+rate+tempprincipal); output += String.valueOf(year) + "\t\t\t" + currencyformatter.format(amount) + "\n"; } else { while (year <= n) { amount = principal * Math.pow(1 + rate, year); output += String.valueOf(year) + "\t\t\t" + currencyformatter.format(amount) + "\n"; year = year + 1; } } text.setText(output); int Isinfo = JOptionPane.showConfirmDialog(null, "是否要进行投资"); if (Isinfo == JOptionPane.YES_OPTION) { a[input] = principal; b[input] = amount; input += 1; }
将上面的注释内容分离出来写成一个函数,如下:
public double CalculateA(int years2,int n2,double principal2,double rate2,double tempprincipal2){ double amount2 = 0; while (years2 <= n2) { amount2 = principal2 * Math.pow(1 + rate2, years2) + tempprincipal2; years2 = years2 + 1; } return amount2; }
注意:类放在Text包中
类名用XXXText结尾
方法用textMethod命名
这样的方式能让我们保证一目了然,养成一个好习惯
测试类的代码如下:
public class Fulitext { @Test public void testFuli() { double z=new Fuli().CalculateA(1, 3, 100, 0.03, 100); boolean flag=0.0==z-209.2727; assertEquals(true, flag); } }
我们可以发现,测试类报错了,原因是:z是一个double型的函数,且输出为209.2727.由此可知这是运行错误,是数据的问题。于是这样修改:
可以看到,这样它没报错了。测试通过。
四、相关知识扩展
Assert是判断对不对。
assertArrayEquals(message,expecteds, actuals);
验证两个数组expecteds和actuals是否相等。若相等则断言成功;若不相等则断言失败,提供message时输出message消息。当expecteds和actuals均为null时,assertArrayEquals认为二者相等
static public void assertEquals(String message, Object expected, Object actual)
assertEquals验证expected和actual是否相等。若相等则断言成功;若不相等则断言失败,提供message时输出message消息。当expected和actual均为null时,assertEquals认为二者相等。
assertTrue(String message, boolean condition)
assertTrue验证condition是否为true。若为true则断言成功;若为false则断言失败,提供message时输出message消息。
assertFalse(String message, boolean condition)
assertfalse验证condition是否为false。若为false则断言成功;若为true则断言失败,提供message时输出message消息。
fail(String message)
fail会使测试立即失败,提供message时输出message消息。
assertNull(String message, Object object)
assertNull验证对象object是否为空。若为空,则断言成功;若非空,则断言失败,提供message时输出message消息。
assertSame(String message, Object expected, Object actual)
assertSame验证expected和actual所引用的是否是同一对象。若相同,则断言成功;若不同,则断言失败,提供message时输出message消息
assertNotSame(String message, Object unexpected, Object actual)
assertSame验证unexpected和actual所引用的是否是不同对象。若不同,则断言成功;若相同,则断言失败,提供message时输出message消息。
在谷歌下载1.2 FULL hamcrest包,把core和Library 1.2 的jar文件导入可使用hamcrest的Matchers,但是要注意,在导入的时候一定要使用静态的,因为只有静态的才能使用里面的静态方法和静态变量,在JUnit4.7的jar中有更强大的assertThat,它几乎包括了之前所有的方法。
assertThat有以下方法:
assertThat(n, allOf( greaterThan(8), lessThan(16) ) ) //表示n这个数满足右边所有的条件,这里举个例子大于8,小于16
assertThat( n, anyOf( greaterThan(16), lessThan(8) ) ) //表示n这个数满足任意一个条件就可以了 例如大于16或者小于8
assertThat( n, anything() ); //无论n是一个什么条件都可以成立的数
assertThat(str, is/not ("****") ) //字符串是不是等于****
assertThat(str, containsString( "***" ) ); //字符串是不是包含了***
assertThat( str, startsWith/endsWith( "***" ) ); //字符串是不是以****开始/结尾
其他更多功能便不再一一列举了。
五、阅读更多案例
代码部分:
public static String wordFormat4DB(String name){ Pattern p = Pattern.compile("[A-Z]"); Matcher m = p.matcher(name); StringBuffer sb = new StringBuffer(); while(m.find()){ m.appendReplacement(sb, "_"+m.group()); } return m.appendTail(sb).toString().toLowerCase(); } }
测试代码:
public class TestWordDealUtil { //测试wordFormat4DB正常运行的情况 @Test public void wordFormat4DBNormal(){ String target = "employeeInfo"; String result = WordDealUtil.wordFormat4DB(target); assertEquals("employee_info", result); } //测试null时的处理情况 @Test public void wordFormat4DBNull(){ String target = null; String result = WordDealUtil.wordFormat4DB(target); assertNull(result); } //测试空字符串的处理情况 @Test public void wordFormat4DBEmpty(){ String target = ""; String result = WordDealUtil.wordFormat4DB(target); assertEquals("", result); } //测试当首字母大写时的情况 @Test public void wordFormat4DBegin(){ String target = "EmployeeInfo"; String result = WordDealUtil.wordFormat4DB(target); assertEquals("employee_info", result); } //测试当尾字母为大写时的情况 @Test public void wordFormat4DBEnd(){ String target = "employeeInfoA"; String result = WordDealUtil.wordFormat4DB(target); assertEquals("employee_info_a", result); } //测试多个相连字母大写时的情况 @Test public void wordFormat4DBTogether(){ String target = "employeeAInfo"; String result = WordDealUtil.wordFormat4DB(target); assertEquals("employee_a_info", result); } }
六、预期异常处理
1、使用@Test(expected…)
expected允许设置一个Throwable的子类,可以使用canVote()方法验证上面抛出预期的异常
可以使用一个类,可以new一个对象,可以runnable
例如:
@Test(expected = IllegalArgumentException.class) public void canVote_throws_IllegalArgumentException_for_zero_age() { Student student = new Student(); student.canVote(0); }
2、ExpectedException
使用JUnit框架中的ExpectedException类,需要声明ExpectedException异常。
例如:
@Rule public ExpectedException thrown= ExpectedException.none();
@Test public void canVote_throws_IllegalArgumentException_for_zero_age() { Student student = new Student(); thrown.expect(IllegalArgumentException.class); student.canVote(0); }
这样就不用声明
expect(RuntimeException.class, “Expected exception message”)
thrown.expect(IllegalArgumentException.class, “age should be +ve”);
3、Try/catch with assert/fail
我比较喜欢使用这种方式,也在我的代码里有多处应用
public void Dcalculate() { double principal = 0; double amount = 0; double rate = 0; int n = 0; NumberFormat currencyformatter = NumberFormat.getCurrencyInstance(); // 字符串转化为数字 String output = "年" + "/" + "复利存款"; int year = 1; int result; if (b1.getText().equals("") && b2.getText() != null && b3.getText() != null && b4.getText() != null) { // 计算本金 try { rate = Double.parseDouble(b2.getText()); if (rate < 0) { JOptionPane.showMessageDialog(null, "利率输入有误,请重新输入数字"); rate = 0; } n = Integer.parseInt(b3.getText()); if (n < 0) { JOptionPane.showMessageDialog(null, "年份输入有误,请重新输入数字"); n = 0; } amount = Double.parseDouble(b4.getText()); if (amount < 0) { JOptionPane.showMessageDialog(null, "期待值输入有误,请重新输入数字"); amount = 0; } } catch (Exception e2) { // TODO: handle exception JOptionPane.showMessageDialog(null, "数据输入有误,请重新输入数字"); } principal = 0; while (year <= n) { principal = amount / (Math.pow(1 + rate, year)); year = year + 1; } output = "本金" + currencyformatter.format(principal) + "\n"; text.setText(output); } else if (b2.getText().equals("") && b1.getText() != null && b3.getText() != null && b4.getText() != null) // 求利率 { try { principal = Double.parseDouble(b1.getText()); // b1本金 if (principal < 0) { JOptionPane.showMessageDialog(null, "本金输入有误,请重新输入数字"); principal = 0; } n = Integer.parseInt(b3.getText()); // b3年份 if (n < 0) { JOptionPane.showMessageDialog(null, "年份输入有误,请重新输入数字"); n = 0; } amount = Double.parseDouble(b4.getText()); // b4期望值 if (amount < 0) { JOptionPane.showMessageDialog(null, "期待值输入有误,请重新输入数字"); amount = 0; } } catch (Exception e2) { // TODO: handle exception JOptionPane.showMessageDialog(null, "数据输入有误,请重新输入数字"); } rate = java.lang.StrictMath.pow(amount / principal, 1.0 / n) - 1; output = "利率" + rate + "\n"; text.setText(output); } else if (b3.getText().equals("") && b1.getText() != null && b2.getText() != null && b4.getText() != null) // 求年份 { try { principal = Double.parseDouble(b1.getText()); // b1本金 if (principal < 0) { JOptionPane.showMessageDialog(null, "本金输入有误,请重新输入数字"); principal = 0; } amount = Double.parseDouble(b4.getText()); // b4期望值 if (amount < 0) { JOptionPane.showMessageDialog(null, "期待值输入有误,请重新输入数字"); amount = 0; } rate = Double.parseDouble(b2.getText()); if (rate < 0) { JOptionPane.showMessageDialog(null, "利率输入有误,请重新输入数字"); rate = 0; } } catch (Exception e2) { // TODO: handle exception JOptionPane.showMessageDialog(null, "数据输入有误,请重新输入数字"); } double year2 = Math.log(amount / principal) / Math.log(1 + rate); output = "至少年数" + year2 + "\n"; text.setText(output); } else if (b4.getText().equals("") && b1.getText() != null && b3.getText() != null && b2.getText() != null) // 求期望值 { try { rate = Double.parseDouble(b2.getText()); if (rate < 0) { JOptionPane.showMessageDialog(null, "利率输入有误,请重新输入数字"); rate = 0; } n = Integer.parseInt(b3.getText()); if (n < 0) { JOptionPane.showMessageDialog(null, "年份输入有误,请重新输入数字"); n = 0; } principal = Double.parseDouble(b1.getText()); // b1本金 if (principal < 0) { JOptionPane.showMessageDialog(null, "本金输入有误,请重新输入数字"); rate = 0; } } catch (Exception e2) { // TODO: handle exception JOptionPane.showMessageDialog(null, "数据输入有误,请重新输入数字"); } double tempprincipal = principal; result = JOptionPane.showConfirmDialog(null, "是否每年添加本金"); if (result == JOptionPane.YES_OPTION) { // while (year <= n) { // amount = principal * Math.pow(1 + rate, year) // + tempprincipal; // output += String.valueOf(year) + "\t\t\t" // + currencyformatter.format(amount) + "\n"; // year = year + 1; // } amount=CalculateA(year,n,principal,rate,tempprincipal); System.out.println("CalculateA(year,n,principal,rate,tempprincipal)"+year+n+principal+rate+tempprincipal); output += String.valueOf(year) + "\t\t\t" + currencyformatter.format(amount) + "\n"; } else { while (year <= n) { amount = principal * Math.pow(1 + rate, year); output += String.valueOf(year) + "\t\t\t" + currencyformatter.format(amount) + "\n"; year = year + 1; } } text.setText(output); int Isinfo = JOptionPane.showConfirmDialog(null, "是否要进行投资"); if (Isinfo == JOptionPane.YES_OPTION) { a[input] = principal; b[input] = amount; input += 1; } } else if (b1.getText() != null && b4.getText() != null && b3.getText() != null && b2.getText() != null) { JOptionPane.showMessageDialog(null, "请删除一个数据"); } else { JOptionPane.showMessageDialog(null, "请增加数据"); } }
这样再使用JOptionpane。提示错误
七、总结
1、以后写代码尽量把模块分得清楚点方便进行测试
2、多使用try catch语句包围保证程序正常运行
3、写代码的要有意识地思考可能会出现哪些情况错误,单元测试不能完全保证无bug。
4、在建立项目的时候要注意起到项目,要有创建包的习惯,把类和测试类分在不同的包,方法也要规范化,尽量,最好别用拼音。
八、资源
学习JUnit网站视频:http://www.sxt.cn/course/showdetail-10003-one.html
github代码:https://github.com/hellolaona/text
Junit测试代码参考网址:https://github.com/hellolaona/junitdemo