Someone on our team changed this code:
public class Rectangle implements Cloneable, Serializable {
@Override
public Rectangle clone() {
return new Rectangle(x, y, width, height);
}
}
to this code:
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;
}
}
}
They wrote a unit test that covers the try code path.
They did not write a test that covers the catch code path. The catch code path relies on things "baked" into Java, the only way to make it crash is to change the structure of the class, by dropping the Cloneable marker interface. But if that class structure changes, then the other unit test should fail.
Because the catch code path isn't covered by a unit test, the SonarQube quality gate "code coverage on new code" fails, and because the quality gate fails, the Jenkins job that builds that branch fails, and because the Jenkins job fails, Bitbucket won't allow the merge.
Already tried:
- FAILED: Mark as false positive in SonarQube: not possible for code coverage quality gate, you can only do that for rule-based issues.
- DIRTY HACK: Turn off (or lower) the quality gate in SonarQube before running the job, and turning it back on after the branch is merged: this works, but it feels so dirty that I think it should happen really exceptionally. I'm looking for a better solution that doesn't require a manual intervention.
- FAILED: Mocking
Rectangle: won't work,super.clone()would have to be mocked, which is onObject.clone(), which is a protected method. - FAILED: Fake it by having an additional class, e.g.
Rectangle->EvilRectangle->TestRectangle, whereEvilRectanglethrows theCloneNotSupportedExceptionand thenTestRectangleis the actual class instantiated for the test. Won't work because that would change the signature of theclone()method, it doesn't throwCloneNotSupportedExceptioninRectangle.
Questions
- Java people:
- How does one write a unit test for the
catchcode path? - How can the code be rewritten, without changing it's public API, so that it becomes testable?
- SonarQube people
- How does one make the quality gate "code coverage on new code" pass?
EDIT HISTORY
- added original code
clone(). Refer to "Effective Java" for a discussion as to why. - daniuclone()is just a bad idea. You can find plenty of questions on SO about copying objects, whereclone()may be suggested by someone (and debunked by many others). - Kayamansuper.clone()inside theclone()method. - Amedee Van Gasse