1

For example, any way to test that a given Groovy class has the annotation @Slf4j?

This is in a TDD context: I don't want to add an annotation until I can see a failing test.

mike rodent
  • 14,126
  • 11
  • 103
  • 157

1 Answers1

1

The @Slf4j annotation has @Retention(SOURCE). So the annotation information is not available at runtime. This makes testing almost impossible. You could read the source file and parse it in the test and cheeck that there is a @Slf4j annotation.

Having an annotation with @Retention(RUNTIME) would change the picture:

class AnnotationSpec extends Specification{

    void "test that class has annotation"() {
        given:
        def annotation = ShouldHaveAnnotation.class.getAnnotation(ClassAnnotation)

        expect:
        annotation != null
    }
}

@Target([TYPE])
@Retention(RetentionPolicy.RUNTIME)
@interface ClassAnnotation{

}

@ClassAnnotation
class ShouldHaveAnnotation {

}

So far for the fact based answer.

My thoughts on TDD:

What is the meaning of your test use case? Do you just want to follow TDD blindly?

Every so called software craftmenship workflow is motivated by the assumption that software development relates to 1800 century furniture manufacturing. I know how to build furniture and dove tails is the strongest joint, looks great, but very expensive. A biscuit joint is modern, doesn't look that nice but is strong as well and cheap. So I decide on base of my experience whether to go for dove tails or modern biscuit joints.

I usually don't test stuff, that don't cause trouble. How do I know, .. well experience. Just use a @CompileStatic annotation on your class and the compiler will catch a missing log instance or just write a test that calls a method that uses a logger.

One mark on TDD as a source for good design: TDD creates good micro software design, but ignores macro design and that's the most important part of the software.

Peter
  • 4,752
  • 2
  • 20
  • 32
  • 1
    I agree with this answer. Just a related comment: What you can do is to actually test for the existance of a non-null `private final static Logger log` (sometimes also `transient`, but not always) in the class under test, i.e. you do not test for the source retention annotation but for its effect. There is also a way to mock-test that logger with a little bit of reflection magic and maybe a `@Delegate` if you want to spy on a final class, see [my other answer](https://stackoverflow.com/a/49870470/1082681). – kriegaex Dec 25 '19 at 02:06
  • 1
    Thanks. I'm not quite clear what you're saying about `CompileStatic`, but I do understand your point about slavishly following TDD. Maybe `Slf4j` is not a good example (of an annotation one might want to test), and open to the accusation of testing implementation details rather than functionality. @kriegaex's "work-around" may therefore be more sensible. I am a big fan of TDD, however, and your answer teaches how to write a failing annotation test, which is what I was after! – mike rodent Dec 25 '19 at 10:40
  • @kriegaex: totally fine with that! – Peter Dec 25 '19 at 11:32
  • @mikerodent: A class annotated with `http://docs.groovy-lang.org/latest/html/gapi/groovy/transform/CompileStatic.html` will instruct the groovy compile to make compile time checks like the java compiler does. Using a non existent (or dynamic) member will cause compile time errors. In the context of TDD: You can use the logger that you would expect when annotating the class with `@Slf4j`. See the compilation fails and then add the `@Slf4j` annotation and make the compilation pass. In this case, the compiler is your test. Did that help? – Peter Dec 25 '19 at 11:38