Improvement request: Include toString() of custom DoNotMock annotations in DefaultDoNotMockEnforcer
It would generally be considered bad form for production code to have a dependency on a test library. Fortunately, the DefaultDoNotMockEnforcer is nice enough to honor a custom annotation as long as it follows the naming convention of ending with org.mockito.DoNotMock, so it is easy to roll your own. However, while the reason is included in the exception message when you use the Mockito-provided annotation, the message is not useful when using a custom annotation:
public class DefaultDoNotMockEnforcer implements DoNotMockEnforcer {
@Override
public String checkTypeForDoNotMockViolation(Class<?> type) {
for (Annotation annotation : type.getAnnotations()) {
if (annotation.annotationType().getName().endsWith("org.mockito.DoNotMock")) { // <=== endsWith, so you can use your own if you copy the name and package structure
String exceptionMessage =
type + " is annotated with @org.mockito.DoNotMock and can't be mocked.";
if (DoNotMock.class.equals(annotation.annotationType())) {
exceptionMessage += " " + type.getAnnotation(DoNotMock.class).reason(); // <=== Includes the `reason` from built-in annotation, but not from any others
}
return exceptionMessage;
}
}
return null;
}
}The offender can always go look at the source of the class they incorrectly tried to mock to see what it says, but this is nowhere near as convenient as having the message in the output. You could use the plugin mechanism to completely override the behavior of the DoNotMockEnforcer, but that's much more invasive and fragile to have to deal with than if the default message simply included the full information. Why not do something like this, instead?
@Override
public String checkTypeForDoNotMockViolation(Class<?> type) {
for (Annotation annotation : type.getAnnotations()) {
if (annotation.annotationType().getName().endsWith("org.mockito.DoNotMock")) {
if (DoNotMock.class.equals(annotation.annotationType())) {
return type + " is annotated with @org.mockito.DoNotMock and can't be mocked. "
+ ((DoNotMock) annotation).reason();
}
return type + " is annotated with " + annotation + " and can't be mocked.";
}
}
return null;
}That way if the annotation has any additional information in its fields, they will be conviently included in the test output:
class org.example.Example is annotated with @org.example.org.mockito.DoNotMock(info="use ExampleFake instead", value=USE_FAKE) and cannot be mocked.
The message isn't quite as pretty as the one that knows how to get the reason out of the annotation class, but it is a lot more useful.
Source: mockito/mockito