一、项目简介
项目名称:复利计算软件 |
版 本 :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