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 java.io.FileNotFoundException; import java.util.*; public class DuplicateAssert extends AbstractSmell { private List<SmellyElement> smellyElementList; public DuplicateAssert() { smellyElementList = new ArrayList<>(); } /** * Checks of 'Duplicate Assert' smell */ @Override public String getSmellName() { return "Duplicate Assert"; } /** * Returns true if any of the elements has a smell */ @Override public boolean getHasSmell() { return smellyElementList.stream().filter(x -> x.getHasSmell()).count() >= 1; } /** * Analyze the test file for test methods that have multiple assert statements with the same explanation message */ @Override public void runAnalysis(CompilationUnit testFileCompilationUnit, CompilationUnit productionFileCompilationUnit, String testFileName, String productionFileName) throws FileNotFoundException { DuplicateAssert.ClassVisitor classVisitor; classVisitor = new DuplicateAssert.ClassVisitor(); classVisitor.visit(testFileCompilationUnit, null); } /** * Returns the set of analyzed elements (i.e. test methods) */ @Override public List<SmellyElement> getSmellyElements() { return smellyElementList; } private class ClassVisitor extends VoidVisitorAdapter<Void> { private MethodDeclaration currentMethod = null; TestMethod testMethod; List<String> assertMessage = new ArrayList<>(); List<String> assertMethod = new ArrayList<>(); // 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.setHasSmell(false); //default value is false (i.e. no smell) super.visit(n, arg); // if there are duplicate messages, then the smell exists Set<String> set1 = new HashSet<String>(assertMessage); if (set1.size() < assertMessage.size()) { testMethod.setHasSmell(true); } // if there are duplicate assert methods, then the smell exists Set<String> set2 = new HashSet<String>(assertMethod); if (set2.size() < assertMethod.size()) { testMethod.setHasSmell(true); } smellyElementList.add(testMethod); //reset values for next method currentMethod = null; assertMessage = new ArrayList<>(); assertMethod = new ArrayList<>(); } } // 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 start with 'assert' // 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(("assertThat"))) { assertMethod.add(n.toString()); // assert method contains a message if (n.getArguments().size() == 3) { assertMessage.add(n.getArgument(0).toString()); } } // 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")) { assertMethod.add(n.toString()); // assert method contains a message if (n.getArguments().size() == 2) { assertMessage.add(n.getArgument(0).toString()); } } // if the name of a method being called is 'fail' else if (n.getNameAsString().equals("fail")) { assertMethod.add(n.toString()); // fail method contains a message if (n.getArguments().size() == 1) { assertMessage.add(n.getArgument(0).toString()); } } } } } }