-
Notifications
You must be signed in to change notification settings - Fork 38
/
AssertionRoulette.java
135 lines (116 loc) · 5.35 KB
/
AssertionRoulette.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package testsmell.smell;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import testsmell.AbstractSmell;
import testsmell.SmellyElement;
import testsmell.TestMethod;
import testsmell.Util;
import thresholds.Thresholds;
import java.io.FileNotFoundException;
import java.util.List;
/**
* "Guess what's wrong?" This smell comes from having a number of assertions in a test method that have no explanation.
* If one of the assertions fails, you do not know which one it is.
* A. van Deursen, L. Moonen, A. Bergh, G. Kok, “Refactoring Test Code”, Technical Report, CWI, 2001.
*/
public class AssertionRoulette extends AbstractSmell {
private int assertionsCount = 0;
public AssertionRoulette(Thresholds thresholds) {
super(thresholds);
}
/**
* Checks of 'Assertion Roulette' smell
*/
@Override
public String getSmellName() {
return "Assertion Roulette";
}
/**
* Analyze the test file for test methods for multiple assert statements without an explanation/message
*/
@Override
public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException {
AssertionRoulette.ClassVisitor classVisitor;
classVisitor = new AssertionRoulette.ClassVisitor();
classVisitor.visit(testFileCompilationUnit, null);
assertionsCount = classVisitor.overallAssertions;
}
public int getAssertionsCount() {
return assertionsCount;
}
private class ClassVisitor extends VoidVisitorAdapter<Void> {
private MethodDeclaration currentMethod = null;
private int assertNoMessageCount = 0;
private int assertCount = 0;
private int overallAssertions = 0;
TestMethod testMethod;
// examine all methods in the test class
@Override
public void visit(MethodDeclaration n, Void arg) {
if (Util.isValidTestMethod(n)) {
currentMethod = n;
testMethod = new TestMethod(n.getNameAsString());
testMethod.setSmell(false); //default value is false (i.e. no smell)
super.visit(n, arg);
boolean isSmelly = assertNoMessageCount >= thresholds.getAssertionRoulette();
//the method has a smell if there is more than 1 call to production methods
testMethod.setSmell(isSmelly);
// if there is only 1 assert statement in the method, then a explanation message is not needed
if (assertCount == 1)
testMethod.setSmell(false);
//if there is more than one assert statement, then all the asserts need to have an explanation message
else if (isSmelly) {
testMethod.setSmell(true);
}
testMethod.addDataItem("AssertCount", String.valueOf(assertNoMessageCount));
smellyElementsSet.add(testMethod);
//reset values for next method
currentMethod = null;
overallAssertions += assertCount;
assertCount = 0;
assertNoMessageCount = 0;
}
}
// examine the methods being called within the test method
@Override
public void visit(MethodCallExpr n, Void arg) {
super.visit(n, arg);
if (currentMethod != null) {
// if the name of a method being called is an assertion and has 3 parameters
if (n.getNameAsString().startsWith(("assertArrayEquals")) ||
n.getNameAsString().startsWith(("assertEquals")) ||
n.getNameAsString().startsWith(("assertNotSame")) ||
n.getNameAsString().startsWith(("assertSame")) ||
n.getNameAsString().startsWith("assertThrows") ||
n.getNameAsString().startsWith(("assertThat"))) {
assertCount++;
// assert methods that do not contain a message
if (n.getArguments().size() < 3) {
assertNoMessageCount++;
}
}
// if the name of a method being called is an assertion and has 2 parameters
else if (n.getNameAsString().equals("assertFalse") ||
n.getNameAsString().equals("assertNotNull") ||
n.getNameAsString().equals("assertNull") ||
n.getNameAsString().equals("assertTrue")) {
assertCount++;
// assert methods that do not contain a message
if (n.getArguments().size() < 2) {
assertNoMessageCount++;
}
}
// if the name of a method being called is 'fail'
else if (n.getNameAsString().equals("fail")) {
assertCount++;
// fail method does not contain a message
if (n.getArguments().size() < 1) {
assertNoMessageCount++;
}
}
}
}
}
}