简易四则运算生成程序——第一次改进后的单元测试
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简易四则运算生成程序——第一次改进后的单元测试相关的知识,希望对你有一定的参考价值。
测试项目:减一四则运算生成程序
项目成员:张金生 张政
工程地址:https://coding.net/u/jx8zjs/p/paperOne/git
ssh://[email protected]:jx8zjs/paperOne.git
测试单元概览:
1. Fraction: 分数类,支持分数加减乘除法,约分,取相反数等
2.QuestionGen:题目生成类,支持生成各种难度的题目,和答案。
待测单元:
Fraction类:
1 public class Fraction { 2 public int up; 3 public int down; 4 5 public Fraction(int up, int down) { 6 if (down == 0 || up == 0) { 7 System.out.println("divided by zero error"); 8 return; 9 } 10 int smaller = up > down ? up : down; 11 int maxCommonFactor = 1; 12 for (int i = 1; i <= smaller; i++) { 13 if (up % i == 0 && down % i == 0) { 14 maxCommonFactor = i; 15 } 16 } 17 18 this.up = up / maxCommonFactor; 19 this.down = down / maxCommonFactor; 20 } 21 22 23 public Fraction gcd(Fraction f) { 24 int smaller = f.up > f.down ? f.up : f.down; 25 int maxCommonFactor = 1; 26 for (int i = 1; i <= smaller; i++) { 27 if (f.up % i == 0 && f.down % i == 0) { 28 maxCommonFactor = i; 29 } 30 } 31 f.up = f.up / maxCommonFactor; 32 f.down = f.down / maxCommonFactor; 33 34 return f; 35 } 36 37 public String toString() { 38 if (down == 1) 39 return "" + up; 40 if(Math.abs(up)/down>0){ 41 return up>0?up/down+" "+up%down+"/"+down:"-"+Math.abs(up)/down+" "+Math.abs(up)%down+"/"+down; 42 } 43 return up + "/" + down; 44 } 45 46 public Fraction add(Fraction f) { 47 Fraction a = new Fraction(up, down); 48 a.up = f.up * a.down + a.up * f.down; 49 a.down = a.down * f.down; 50 51 return a.gcd(a); 52 } 53 54 public Fraction minus(Fraction f) { 55 Fraction a = new Fraction(up, down); 56 a.up = a.up * f.down - f.up * a.down; 57 a.down = a.down * f.down; 58 59 return a.gcd(a); 60 } 61 62 public Fraction multiply(Fraction f) { 63 Fraction a = new Fraction(up, down); 64 a.up = a.up * f.up; 65 a.down = a.down * f.down; 66 return a.gcd(a); 67 } 68 69 public Fraction divide(Fraction f) { 70 Fraction a = new Fraction(up, down); 71 a.up = a.up * f.down; 72 a.down = a.down * f.up; 73 return a.gcd(a); 74 } 75 76 public Fraction changeSign(){ 77 up = -up; 78 return this; 79 } 80 81 public static Fraction getRandiom(int Max) { 82 return new Fraction((int) (Math.random() * Max / 2) + 1, (int) (Math.random() * Max / 2) + 1); 83 } 84 85 public static Fraction getRandiom(int Max, boolean isInt) { 86 return new Fraction((int) (Math.random() * Max / 2) + 1, isInt ? 1 : (int) (Math.random() * Max / 2) + 1); 87 }
QuestionGen类待测单元:
1 public String generateSimpleQuestion() { 2 SupportedOperation[] operation = { SupportedOperation.ADD, SupportedOperation.MINUS }; 3 return generateQuestion(2, 0, 20, operation, false, false); 4 } 5 6 public String generateCommonQuestion() { 7 SupportedOperation[] operation = { SupportedOperation.ADD, SupportedOperation.MINUS, 8 SupportedOperation.MULTIPLY, SupportedOperation.DIVIDE }; 9 return generateQuestion(4, 0, 20, operation, false, true); 10 } 11 12 public String generateMediumQuestion() { 13 SupportedOperation[] operation = { SupportedOperation.ADD, SupportedOperation.MINUS, 14 SupportedOperation.MULTIPLY, SupportedOperation.DIVIDE }; 15 return generateQuestion(4, 0, 20, operation, true, true); 16 } 17 18 public String generateComplexQuestion() { 19 SupportedOperation[] operation = { SupportedOperation.ALL }; 20 return generateQuestion(6, 0, 20, operation, true, true); 21 } 22 23 public String answer; 24 25 Stack<SupportedOperation> oplist = new Stack<SupportedOperation>(); 26 Stack<Fraction> numlist = new Stack<Fraction>(); 27 ArrayList<Integer[]> bclist; 28 TreeMap<Integer, Integer> frequency; 29 TreeMap<Integer, Integer> direction; 30 31 private void getBcPrint(int numOfOperand) { 32 bclist = new ArrayList<Integer[]>(); 33 if (numOfOperand > 2) { 34 int bcnum = (int) (Math.random() * (numOfOperand - 2)); 35 for (int n = 0; n < bcnum; n++) { 36 Integer[] bracket = new Integer[2]; 37 bracket[0] = (int) (Math.random() * (numOfOperand - 2)); 38 bracket[1] = (int) (Math.random() * (numOfOperand - 2 - bracket[0]) + bracket[0]); 39 if (bracket[0] == bracket[1]) { 40 bracket[1]++; 41 } 42 boolean canput = true; 43 for (int i = 0; i < bclist.size(); i++) { 44 Integer[] tmp = bclist.get(i); 45 if (bracket[0] < tmp[0] & bracket[1] >= tmp[0] & bracket[1] < tmp[1]) { 46 canput = false; 47 break; 48 } else if (bracket[1] > tmp[1] & bracket[0] > tmp[0] & bracket[0] <= tmp[1]) { 49 canput = false; 50 break; 51 } else if (bracket[0] == tmp[0] & bracket[1] == tmp[1]) { 52 53 } 54 } 55 if (canput) { 56 bclist.add(bracket); 57 } 58 } 59 60 } 61 frequency = new TreeMap<Integer, Integer>(); 62 direction = new TreeMap<Integer, Integer>(); 63 for (int i = 0; i < bclist.size(); i++) { 64 Integer[] tmp = bclist.get(i); 65 if (frequency.containsKey(tmp[0])) { 66 frequency.put(tmp[0], frequency.get(tmp[0]) + 1); 67 } else { 68 frequency.put(tmp[0], 1); 69 direction.put(tmp[0], 0); 70 } 71 if (frequency.containsKey(tmp[1])) { 72 frequency.put(tmp[1], frequency.get(tmp[1]) + 1); 73 } else { 74 frequency.put(tmp[1], 1); 75 direction.put(tmp[1], 1); 76 } 77 } 78 } 79 80 public Fraction getanswer(ArrayList<Fraction> frlist, SupportedOperation[] so) { 81 82 numlist.push(frlist.get(0)); 83 for (int n = 0; n < so.length; n++) { 84 switch (so[n]) { 85 case ADD: 86 oplist.push(so[n]); 87 numlist.push(frlist.get(n + 1)); 88 break; 89 case MINUS: 90 oplist.push(SupportedOperation.ADD); 91 numlist.push(frlist.get(n + 1).changeSign()); 92 break; 93 case MULTIPLY: { 94 Fraction r = numlist.pop().multiply(frlist.get(n + 1)); 95 numlist.push(r); 96 } 97 break; 98 case DIVIDE: { 99 Fraction r = numlist.pop().divide(frlist.get(n + 1)); 100 numlist.push(r); 101 } 102 break; 103 default: 104 System.out.println("不支持的运算"); 105 break; 106 } 107 } 108 109 while (!oplist.isEmpty()) { 110 Fraction answer = numlist.pop(); 111 switch (oplist.pop()) { 112 case ADD: { 113 answer = answer.add(numlist.pop()); 114 numlist.push(answer); 115 } 116 break; 117 // case MINUS: { 118 // answer = answer.minus(numlist.pop()); 119 // numlist.push(answer); 120 // } 121 // break; 122 default: 123 System.out.println("不支持的运算"); 124 break; 125 } 126 127 } 128 129 return numlist.pop(); 130 }
QuestionGen核心题目生成单元:
1 public String generateQuestion(int numOfOperand, int rangeMin, int rangMax, SupportedOperation[] operation, 2 boolean isFractional, boolean hasbracket) { 3 String question = ""; 4 int[] ioperands = null; 5 ArrayList<Fraction> af = new ArrayList<Fraction>(); 6 SupportedOperation[] so = null; 7 if (numOfOperand < 2) { 8 System.out.println("操作数数量至少为2"); 9 return ""; 10 } 11 if (rangMax > 500) { 12 System.out.println("操作数数最大值不能超过500"); 13 return ""; 14 } 15 getBcPrint(numOfOperand); 16 if (!isFractional) { 17 ScriptEngine se = new ScriptEngineManager().getEngineByName("javascript"); 18 ioperands = new int[numOfOperand]; 19 for (int i = 0; i < numOfOperand; i++) { 20 ioperands[i] = (int) (Math.random() * rangMax / 2 + 1); 21 22 } 23 so = new SupportedOperation[numOfOperand - 1]; 24 for (int i = 0; i < operation.length; i++) { 25 if (operation[i] == SupportedOperation.ALL) { 26 operation = new SupportedOperation[4]; 27 operation[0] = SupportedOperation.ADD; 28 operation[1] = SupportedOperation.MINUS; 29 operation[2] = SupportedOperation.MULTIPLY; 30 operation[3] = SupportedOperation.DIVIDE; 31 32 } 33 } 34 // 除法運算,保证整除 35 int value = 0; 36 for (int j = numOfOperand - 1; j > 0; j--) { 37 so[numOfOperand - 1 - j] = operation[(int) (Math.random() * operation.length)]; 38 } 39 for (int j = numOfOperand - 2; j >= 0; j--) { 40 if (so[j] == SupportedOperation.DIVIDE) { 41 if (value < 1) { 42 ioperands[j] = ioperands[j] * ioperands[j + 1]; 43 value++; 44 45 } else { 46 so[j] = operation[(int) (Math.random() * (operation.length - 2))]; 47 } 48 } 49 } 50 // 输出括号 51 for (int i = 0; i < numOfOperand - 1; i++) { 52 if (frequency.containsKey(i)) { 53 if (direction.get(i) == 0) { 54 for (int k = 0; k < frequency.get(i); k++) { 55 question += "("; 56 } 57 } 58 } 59 question += ioperands[i]; 60 if (frequency.containsKey(i)) { 61 if (direction.get(i) == 1) { 62 for (int k = 0; k < frequency.get(i); k++) { 63 question += ")"; 64 } 65 } 66 } 67 question += so[i]; 68 } 69 if (frequency.containsKey(numOfOperand - 1)) { 70 if (direction.get(numOfOperand - 1) == 0) { 71 for (int k = 0; k < frequency.get(numOfOperand - 1); k++) { 72 question += "("; 73 } 74 } 75 } 76 question += ioperands[numOfOperand - 1]; 77 if (frequency.containsKey(numOfOperand - 1)) { 78 if (direction.get(numOfOperand - 1) == 1) { 79 for (int k = 0; k < frequency.get(numOfOperand - 1); k++) { 80 question += ")"; 81 } 82 } 83 } 84 85 try { 86 Integer d = (Integer) se.eval(question); 87 answer = "" + d; 88 } catch (Exception e) { 89 generateQuestion(numOfOperand, rangeMin, rangMax, operation, isFractional, hasbracket); 90 } 91 92 } else { 93 for (int i = 0; i < numOfOperand; i++) { 94 af.add(Fraction.getRandiom(rangMax)); 95 } 96 97 so = new SupportedOperation[numOfOperand - 1]; 98 for (int i = 0; i < operation.length; i++) { 99 if (operation[i] == SupportedOperation.ALL) { 100 operation = new SupportedOperation[4]; 101 operation[0] = SupportedOperation.ADD; 102 operation[1] = SupportedOperation.MINUS; 103 operation[2] = SupportedOperation.MULTIPLY; 104 operation[3] = SupportedOperation.DIVIDE; 105 106 } 107 } 108 question += af.get(0); 109 for (int j = 0; j < numOfOperand - 1; j++) { 110 so[j] = operation[(int) (Math.random() * operation.length)]; 111 question += (so[j] == SupportedOperation.DIVIDE ? "÷" : so[j].toString()) + af.get(j + 1); 112 113 } 114 answer = getanswer(af, so).toString(); 115 try { 116 } catch (Exception e) { 117 e.printStackTrace(); 118 } 119 120 } 121 122 return question; 123 124 }
测试类:
FractionTest
1 public class FractionTest { 2 3 Fraction f1 = new Fraction(9, 4); 4 Fraction f2 = new Fraction(16, 4); 5 6 @Test 7 public void testdown0() { 8 new Fraction(0, 0); 9 } 10 11 @Test 12 public void testGcd() { 13 assertEquals("4", f2.gcd(f2).toString()); 14 } 15 16 @Test 17 public void testToString() { 18 assertEquals("2 1/4", f1.toString()); 19 } 20 21 @Test 22 public void testAddFraction() { 23 assertEquals(new Fraction(25, 4).toString(), f1.add(f2).toString()); 24 } 25 26 @Test 27 public void testMinusFraction() { 28 assertEquals(new Fraction(-7, 4).toString(), f1.minus(f2).toString()); 29 } 30 31 @Test 32 public void testMultiplyFraction() { 33 assertEquals(new Fraction(144, 16).toString(), f1.multiply(f2).toString()); 34 } 35 36 @Test 37 public void testDivideFraction() { 38 assertEquals(new Fraction(9, 16).toString(), f1.divide(f2).toString()); 39 } 40 41 @Test 42 public void testChangeSign() { 43 assertEquals(new Fraction(-9, 4).toString(), f1.changeSign().toString()); 44 } 45 46 @Test 47 public void testGetRandiomInt() { 48 Fraction a = Fraction.getRandiom(7); 49 System.out.println(a.toString()); 50 } 51 52 @Test 53 public void testGetRandiomIntBoolean() { 54 Fraction a = Fraction.getRandiom(7, true); 55 Fraction b = Fraction.getRandiom(7, true); 56 System.out.println("true = " + a.toString() + "\\nfalse = " + b.toString()); 57 } 58 59 }
QuestionGenTest:
1 public class QuestionGenTest { 2 3 QuestionGen qg = new QuestionGen(); 4 ArrayList<Fraction> flt = new ArrayList<Fraction>(); 5 SupportedOperation[] sot = { SupportedOperation.ADD, SupportedOperation.MINUS, SupportedOperation.MULTIPLY, 6 SupportedOperation.DIVIDE }; 7 8 @Test 9 public void testGetanswer() { 10 11 flt.add(new Fraction(1, 2)); 12 flt.add(new Fraction(1, 3)); 13 flt.add(new Fraction(1, 4)); 14 flt.add(new Fraction(1, 3)); 15 flt.add(new Fraction(1, 2)); 16 17 Fraction r = qg.getanswer(flt, sot); 18 assertEquals("2/3", r.toString()); 19 20 } 21 22 @Test 23 public void testGenerateSimpleQuestion() { 24 qg.generateSimpleQuestion(); 25 } 26 27 @Test 28 public void testGenerateCommonQuestion() { 29 qg.generateCommonQuestion(); 30 } 31 32 @Test 33 public void testGenerateMediumQuestion() { 34 qg.generateMediumQuestion(); 35 } 36 37 @Test 38 public void testGenerateComplexQuestion() { 39 qg.generateComplexQuestion(); 40 } 41 42 43 }
测试结果:
Fraction类测试结果:
QuestionGen类测试结果:
单元测试总结:
单元测试时发现了一些功能或方法定义了,但在使用的时候没有用到,有些可能预计不会再用到的则选择注释掉,这样可以增加代码覆盖率。测试过的单元和功能都能成功的达到想要的预期结果。
代码覆盖率:
Fraction类测试代码覆盖率:
QuestionGen类测试代码覆盖率:
总代吗覆盖率:
代码覆盖率总结:
Fraction类的代码覆盖率能达到97.4%,确实非常高,没有测试到的部分为一些复杂的随机分支,或者减少程序出错的校验内容。QuestionGen类的代码覆盖率为74.2%,一部分未测试部分也是由于随机程度够多的时候才能完全覆盖,本次测试也运行了多次测试用例,目前显示未覆盖的部分也是可达的,还有一部分未测试行是保证程序鲁棒性的代码。因此我们认为本次测试基本达到单元测试要求。
工程地址:https://coding.net/u/jx8zjs/p/paperOne/git
ssh://[email protected]:jx8zjs/paperOne.git
以上是关于简易四则运算生成程序——第一次改进后的单元测试的主要内容,如果未能解决你的问题,请参考以下文章