Use Groovy Category implicitly in all instance met

2020-02-26 06:35发布

问题:

I have simple Groovy category class which adds method to String instances:

final class SampleCategory {

    static String withBraces(String self) {
        "($self)"
    }

}

I want to use this category in my unit tests (for example). It looks like this:

class MyTest {
    @Test
    void shouldDoThis() {
        use (SampleCategory) {
            assert 'this'.withBraces() == '(this)'
        }
    }

    @Test
    void shouldDoThat() {
        use (SampleCategory) {
            assert 'that'.withBraces() == '(that)'
        }
    }
}

What I'd like to achieve, however, is ability to specify that category SampleCategory is used in scope of each and every instance method of MyTest so I don't have to specify use(SampleCategory) { ... } in every method.

Is it possible?

回答1:

You can use mixin to apply the category directly to String's metaClass. Assign null to the metaClass to reset it to groovy defaults. For example:

@Before void setUp() { 
    String.mixin(SampleCategory)
}

@After void tearDown() {
    String.metaClass = null
}

@Test
void shouldDoThat() {
    assert 'that'.withBraces() == '(that)'
}


回答2:

Now you have the option to use extension modules instead of categories: http://mrhaki.blogspot.se/2013/01/groovy-goodness-adding-extra-methods.html

On the plus side Intellij will recognize the extensions. I've just noticed that it doesn't even need to be a separate module as suggested by the link, just add META-INF/services/org.codehaus.groovy.runtime.ExtensionModule to the project:

# File: src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
moduleName = module
moduleVersion = 1.0
extensionClasses = SampleExtension

The extension class is pretty much defined like a normal category:

class SampleExtension {
    static String withBraces(String self) {
        "($self)"
    }
}

Can be used like:

def "Sample extension"() {
    expect: 'this'.withBraces() == '(this)'
}

If you are using Spock there is a @Use annotation that can be used on the specifications. The drawback with that is that Intellij will not recognize it.