新代码覆盖率的 SonarQube 质量门对于依赖于烘焙到 Java 中的东西的未发现代码失败
SonarQube quality gate on new code coverage fails for uncovered code that relies on things baked into Java
我们团队中有人更改了此代码:
public class Rectangle implements Cloneable, Serializable {
@Override
public Rectangle clone() {
return new Rectangle(x, y, width, height);
}
}
此代码:
public class Rectangle implements Cloneable, Serializable {
@Override
public Rectangle clone() {
try {
// super.clone is safe to return since all of the Rectangle's fields are primitive.
return (Rectangle) super.clone();
} catch (CloneNotSupportedException e) {
// should never happen since Cloneable is implemented
return null;
}
}
}
他们编写了涵盖 try
代码路径的单元测试。
他们没有编写涵盖 catch
代码路径的测试。 catch
代码路径依赖东西"baked"变成Java,让它崩溃的唯一方法是改变[=89]的结构 =],通过删除 Cloneable
标记界面。但是如果 class 结构发生变化,那么另一个单元测试应该会失败。
因为 catch
代码路径未被单元测试覆盖,SonarQube 质量门 "code coverage on new code" 失败,并且由于质量门失败,构建该分支的 Jenkins 作业失败,由于 Jenkins 作业失败,Bitbucket 将不允许合并。
已经尝试过:
- 失败:在 SonarQube 中标记为误报:代码覆盖质量门不可能,您只能针对基于规则的问题这样做。
- DIRTY HACK:在 运行 作业之前关闭(或降低)SonarQube 中的质量门,并在分支合并后将其重新打开:这有效,但我觉得感觉太脏了它应该非常特别地发生。我正在寻找不需要手动干预的更好解决方案。
- FAILED: Mocking
Rectangle
: 不起作用,super.clone()
必须被 mock,这是在 Object.clone()
上,这是一个受保护的方法。
- 失败:通过额外的 class 来伪造它,例如
Rectangle
-> EvilRectangle
-> TestRectangle
,其中 EvilRectangle
抛出 CloneNotSupportedException
然后 TestRectangle
是实例化的实际 class考试。不会工作,因为这会改变 clone()
方法的签名,它不会在 Rectangle
. 中抛出 CloneNotSupportedException
问题
- Java 人:
- 如何为
catch
代码路径编写单元测试?
- 如何在不更改代码的情况下重写代码 public API,使其变得可测试?
- SonarQube 人
- 如何让质量门 "code coverage on new code" 通过?
编辑历史记录
- 添加了原始代码
Apache Commons Lang 提供了一个 clone
方法,它可以进行深拷贝并且不会抛出异常。没有异常意味着不需要 try/catch
,因此要测试的代码路径更少。我在一篇讨论 Object.clone()
替代方案的文章中发现了这一点:https://dzone.com/articles/java-cloning-copy-constructor-vs-cloning
将此添加到 pom.xml
:
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
将此添加到您的 class:
的导入语句中
import org.apache.commons.lang.SerializationUtils;
您的 class 必须有 implements Serializable
。添加一个 private static final long serialVersionUID
,这显然是最佳做法。
然后将super.clone()
替换为SerializationUtils.clone(this)
。
最后,您可以删除 clone()
语句周围的 try/catch
,或从方法中删除 trows
。
这是我的新代码:
@Override
public Rectangle clone() {
return (Rectangle) SerializationUtils.clone(this);
}
它已经被之前的单元测试覆盖了,那个单元测试仍然通过。
我们团队中有人更改了此代码:
public class Rectangle implements Cloneable, Serializable {
@Override
public Rectangle clone() {
return new Rectangle(x, y, width, height);
}
}
此代码:
public class Rectangle implements Cloneable, Serializable {
@Override
public Rectangle clone() {
try {
// super.clone is safe to return since all of the Rectangle's fields are primitive.
return (Rectangle) super.clone();
} catch (CloneNotSupportedException e) {
// should never happen since Cloneable is implemented
return null;
}
}
}
他们编写了涵盖 try
代码路径的单元测试。
他们没有编写涵盖 catch
代码路径的测试。 catch
代码路径依赖东西"baked"变成Java,让它崩溃的唯一方法是改变[=89]的结构 =],通过删除 Cloneable
标记界面。但是如果 class 结构发生变化,那么另一个单元测试应该会失败。
因为 catch
代码路径未被单元测试覆盖,SonarQube 质量门 "code coverage on new code" 失败,并且由于质量门失败,构建该分支的 Jenkins 作业失败,由于 Jenkins 作业失败,Bitbucket 将不允许合并。
已经尝试过:
- 失败:在 SonarQube 中标记为误报:代码覆盖质量门不可能,您只能针对基于规则的问题这样做。
- DIRTY HACK:在 运行 作业之前关闭(或降低)SonarQube 中的质量门,并在分支合并后将其重新打开:这有效,但我觉得感觉太脏了它应该非常特别地发生。我正在寻找不需要手动干预的更好解决方案。
- FAILED: Mocking
Rectangle
: 不起作用,super.clone()
必须被 mock,这是在Object.clone()
上,这是一个受保护的方法。 - 失败:通过额外的 class 来伪造它,例如
Rectangle
->EvilRectangle
->TestRectangle
,其中EvilRectangle
抛出CloneNotSupportedException
然后TestRectangle
是实例化的实际 class考试。不会工作,因为这会改变clone()
方法的签名,它不会在Rectangle
. 中抛出
CloneNotSupportedException
问题
- Java 人:
- 如何为
catch
代码路径编写单元测试? - 如何在不更改代码的情况下重写代码 public API,使其变得可测试?
- SonarQube 人
- 如何让质量门 "code coverage on new code" 通过?
编辑历史记录
- 添加了原始代码
Apache Commons Lang 提供了一个 clone
方法,它可以进行深拷贝并且不会抛出异常。没有异常意味着不需要 try/catch
,因此要测试的代码路径更少。我在一篇讨论 Object.clone()
替代方案的文章中发现了这一点:https://dzone.com/articles/java-cloning-copy-constructor-vs-cloning
将此添加到 pom.xml
:
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
将此添加到您的 class:
的导入语句中import org.apache.commons.lang.SerializationUtils;
您的 class 必须有 implements Serializable
。添加一个 private static final long serialVersionUID
,这显然是最佳做法。
然后将super.clone()
替换为SerializationUtils.clone(this)
。
最后,您可以删除 clone()
语句周围的 try/catch
,或从方法中删除 trows
。
这是我的新代码:
@Override
public Rectangle clone() {
return (Rectangle) SerializationUtils.clone(this);
}
它已经被之前的单元测试覆盖了,那个单元测试仍然通过。