SonarQube测试自定义Java规则无法显示语义信息

时间:2016-06-03 08:36:49

标签: java sonarqube

我正在学习SonarQube的API,试图扩展java插件规则。我成功地遵循了这个tutorial

现在我想构建一个简单的分析来检查是否toString() 方法用于单元测试。

public class TS_SensitiveEqualityCheck extends BaseTreeVisitor implements JavaFileScanner {

  private final Deque<Boolean> methodContainsToStringInAssert = new ArrayDeque<Boolean>();
  private final Deque<Boolean> inUnitTest = new ArrayDeque<Boolean>();

  private JavaFileScannerContext context;

  @Override
  public void scanFile(final JavaFileScannerContext context) {
    this.context = context;
    scan(context.getTree());
  }

  @Override
  public void visitMethod(MethodTree methodTree) {
    if (ModifiersUtils.hasModifier(methodTree.modifiers(), Modifier.ABSTRACT)) {
      return;
    }
    boolean isUnitTest = isUnitTest(methodTree);
    inUnitTest.push(isUnitTest);
    System.out.println("For method " + methodTree.simpleName() 
    + " found [isUnitTest | isViolation] :  " 
    + String.valueOf(isUnitTest));
    methodContainsToStringInAssert.push(false);
    super.visitMethod(methodTree);
    inUnitTest.pop();
    Boolean isViolation = methodContainsToStringInAssert.pop();
    System.out.println("For method " + methodTree.simpleName() 
            + " found [isUnitTest | isViolation] :  " 
            + String.valueOf(isUnitTest) + " " 
            + String.valueOf(isViolation) );
    if (isUnitTest && isViolation) { 
      context.reportIssue(this, methodTree.simpleName(), "This test method uses unsafe equality checking!");
    }
  }

  @Override
  public void visitMethodInvocation(MethodInvocationTree mit) {
    if (!inUnitTest()) {
      return;
    }
    Symbol mis = mit.symbol();
    System.out.println(mis.name()); // null when encountering an assertion.
    if (mis.name() != null && mis.name().equals("toString")) {
        setTrue(methodContainsToStringInAssert);
    }
    super.visitMethodInvocation(mit);
  }

  private boolean inUnitTest() {
    return !inUnitTest.isEmpty() && inUnitTest.peek();
  }

  private static void setTrue(Deque<Boolean> collection) {
    if (collection != null && !collection.peek()) {
      collection.pop();
      collection.push(true);
    }
  }

  private static boolean isUnitTest(MethodTree methodTree) {
    JavaSymbol.MethodJavaSymbol symbol = (JavaSymbol.MethodJavaSymbol) methodTree.symbol();
    while (symbol != null) {
      if (symbol.metadata().isAnnotatedWith("org.junit.Test")) {
        return true;
      }
      symbol = symbol.overriddenSymbol();
    }
    Symbol.TypeSymbol enclosingClass = methodTree.symbol().enclosingClass();
    return (enclosingClass != null 
            // && enclosingClass.type().isSubtypeOf("junit.framework.TestCase")  // errors!!! does not get the package name of the class!!!
            && methodTree.simpleName().name().startsWith("test"));
  }

}

对于给定的测试文件,SonarQube找不到任何断言方法调用!只有方法B.is()会给出结果,即mit.symbol().name != null。谁能解释为什么会出错?这是用作测试的文件:

import junit.framework.TestCase;
    import javax.annotation.Nullable;

public class AssertionsInTestsCheckTestJunit3 extends TestCase { public void testCompliant() { B b = new B(); b.is(); org.junit.Assert.assertTrue(b.is()); } public void testNoncompliant() { // Noncompliant org.junit.Assert.assertTrue(this.toString().equals("")); } public void testNoncompliant2() { // Noncompliant org.junit.Assert.assertEquals(this.toString(), ""); } public void testNoncompliant3() { // Noncompliant org.junit.Fail.fail(this.toString()); doWork(); } @Nullable public Test notAtest() { compliant1(); } } public class B { public boolean is() { return true; } } </pre></code>

请注意,此代码的作用并不重要!

1 个答案:

答案 0 :(得分:4)

Java Analyzer需要源文件中使用的库的字节代码才能完成语义模型。没有它,大多数可以检索的语义信息都会丢失。

在测试文件中,您使用的是 junit 。但是,缺少与junit相关的二进制文件,因为您很可能不会向检查验证程序提供库。该部分尚未在教程中描述。

默认情况下,没有为检查验证程序提供外部库,因为教程在当前状态下不需要外部库。解释如何使用外部资源的部分尚未编写,但一旦完成,应该可以通过以下链接获得:How to test sources requiring external binaries

现在,为了解决您的问题并总结本教程中将要介绍的内容,您需要做的就是:

为了在分析文件时使用足够的字节代码,您必须向检查验证程序提供 junit二进制文件。有多种方法可以做到这一点,但最简单的方法可能是将jar放在项目的专用位置来提供jar:target/test-jars。默认情况下,检查验证程序将查找此文件夹。

您可以通过更改项目根目录下的pom.xml文件自动添加任何所需的库:

  1. 在插件根目录的pom.xml文件中,您应该看到一个注释部分:pom.xml#L108(来自教程中使用的模板)
  2. 取消评论此部分代码。请注意,对于该示例,它已包含apache commons-collections的二进制文件。
  3.  
     <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.10</version>
        <executions>
          <execution>
            <id>copy</id>
            <phase>test-compile</phase>
            <goals>
              <goal>copy</goal>
            </goals>
            <configuration>
              <artifactItems>
                <artifactItem>
                  <groupId>org.apache.commons</groupId>
                  <artifactId>commons-collections4</artifactId>
                  <version>4.0</version>
                  <type>jar</type>
                </artifactItem>
              </artifactItems>
              <outputDirectory>${project.build.directory}/test-jars</outputDirectory>
            </configuration>
          </execution>
        </executions>
      </plugin>
    
    1. <artifactItem>替换为使用junit,或者只是添加一个新工件:
    2.   
        <artifactItem>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
          <type>jar</type>
        </artifactItem>
      
      1. 使用mvn clean install -DskipTests重新构建项目。具有所需版本的junit jar将由maven下载,并放在target/test-jars文件夹中。

      2. 重新运行测试。应该检测问题。