Groovy应用程序类和Java单元测试

时间:2010-11-09 23:08:06

标签: maven-2 groovy

我有一个由Maven构建的应用程序,它是一个混合的Groovy和Java项目。

使用GMaven插件(v1.3),我可以轻松地针对Java和Groovy类运行Groovy测试。在应用程序的构建期间,我的java类与扩展的存根文件链接,这些文件从GroovyObject声明方法。

但是,如果我使用Java编写针对应用程序Groovy代码的测试并尝试从GroovyObject调用方法,则会导致编译时失败。

这有什么解决方法吗? GMaven是否有任何配置参数可以实现这一目标?

感谢。

这是我的pom中的build.plugins:

             <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <includes>
                            <include>target/generated-sources/groovy-stubs/main</include>
                        </includes>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.codehaus.gmaven</groupId>
                    <artifactId>gmaven-plugin</artifactId>
                    <version>1.3</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>generateStubs</goal>
                                <goal>generateTestStubs</goal>

                                <goal>compile</goal>
                                <goal>generateTestStubs</goal>
                                <goal>testCompile</goal>
                            </goals>
                            <configuration>
                                <!-- providerSelection probably defaults to 1.7 now -->
                                <providerSelection>1.7</providerSelection>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>

这是java测试类:

public class JavaGroovyTest extends TestCase {
      @Test
      public void testGroovyClasses(){
        Model m = new Model();  //Model is an application class written in Groovy
        assertNotNull(m);
        assertEquals(4,m.getMetaClass().getProperties().size());
      }
}

这是编译器输出:

[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure
/Users/mkohout/Documents/trunk/src/test/java/JavaGroovyTest.java:[17,24] cannot find symbol
symbol  : method getMetaClass()
location: class com.q.Model

2 个答案:

答案 0 :(得分:2)

将groovy对象声明为GroovyObject。例如:

import groovy.lang.GroovyObject

public class JavaGroovyTest extends TestCase {
    @Test
    public void testGroovyClasses(){
        GroovyObject m = new Model();  //Model is an application class written in Groovy
        assertNotNull(m);
        assertEquals(4,m.getMetaClass().getProperties().size());
    }
}

编辑:更长的解释

groovy编译器向类添加getMetaClass方法,但它将其标记为synthetic。这是作为“实现细节”生成的方法和字段的内部JVM标志,并且不应对代码可见。您可以使用javap验证:

$ javap -verbose Model | grep -A18 getMetaClass\(\)
public groovy.lang.MetaClass getMetaClass();
  Code:
   Stack=2, Locals=1, Args_size=1
   0:   aload_0
   1:   getfield        #42; //Field metaClass:Lgroovy/lang/MetaClass;
   4:   dup
   5:   ifnull  9
   8:   areturn
   9:   pop
   10:  aload_0
   11:  dup
   12:  invokevirtual   #28; //Method $getStaticMetaClass:()Lgroovy/lang/MetaClass;
   15:  putfield        #42; //Field metaClass:Lgroovy/lang/MetaClass;
   18:  aload_0
   19:  getfield        #42; //Field metaClass:Lgroovy/lang/MetaClass;
   22:  areturn
   23:  nop
   Synthetic: true

你可以通过将它转换为groovy.lang.GroovyObject接口来解决这个问题,该接口声明了getMetaClass方法(这次不合成)。

这可能不是一个很好的解决方案,但是说,首先在Java中使用groovy metaClass可能是不明智的。如果在测试中不可能只使用groovy,我会考虑使用普通的java可访问方法从groovy类中公开所需的metaClass信息。

答案 1 :(得分:0)

简短回答:您无法从Java访问Groovy“元方法”。

答案很长:

DefaultGroovyMethods.getMetaClass(..) 不是可以静态编译为Java字节码的方法。

<强> [校正: 与此同时,Don发布了一个答案,建议转向GroovyObject。这是正确的,这样你应该能够像这样调用Groovy元方法:

List<Book> books = (List<Book>) 
    ((GroovyObject) Book).invokeMethod(
    "findAllByAuthorAndTitle", new String[] {"author", "title"})

(或类似)。 - 然而,这对于日常编程来说是不切实际的。的

查看DefaultGroovyMethods ApiDocs。每个方法的第一个参数是对象的运行时类型。其余的相应方法形成了在Groovy代码中使用的方法签名。你已经熟悉了很多这些。

所有这些“运行时元方法”都不是静态编译GroovyObject((几乎)任何Groovy对象派生自该类型),但是,当被调用时,在运行时动态调度 - 通常使用GroovyObject.invokeMethod(String, Object) method

因此,您的Java代码在编译时调用一个根本不存在的方法。 - 但是,如果编译为Java字节码,那么Groovy代码如何引用该方法? - Groovy(Java)字节码不直接引用方法(构造函数,属性等),而是构建在运行时调用以进行动态分派的结构。

例如,用Groovy编写的测试类将编译为此(为清晰起见缩短了):

public class JavaGroovyTest extends TestCase
  implements GroovyObject
{
  public JavaGroovyTest()
  {
    JavaGroovyTest this;
    CallSite[] arrayOfCallSite = $getCallSiteArray();
    MetaClass tmp12_9 = $getStaticMetaClass(); this.metaClass = ((MetaClass)ScriptBytecodeAdapter.castToType(tmp12_9, $get$$class$groovy$lang$MetaClass())); tmp12_9;
  }
  public void testGroovyClasses() { CallSite[] arrayOfCallSite = $getCallSiteArray(); Model m = arrayOfCallSite[0].callConstructor($get$$class$Model());
    arrayOfCallSite[1].callStatic($get$$class$JavaGroovyTest(), m);
    arrayOfCallSite[2].callStatic($get$$class$JavaGroovyTest(), $const$0, arrayOfCallSite[3].call(arrayOfCallSite[4].call(arrayOfCallSite[5].call(m)))); return;
  }
} 

当执行此代码时,Groovy运行时将执行数百,数千甚至数十亿甚至数十亿的查找;-)以便最终不直接调用方法,而是通过类似反射的invokeMethod(..)调用

Groovy编程语言的大多数部分(包括构建器和其他库)都非常依赖meta programming这一概念,可以在compile-timeruntime实现。{{3}}。

不幸的是,Groovy更喜欢后者,虽然所有动态添加的功能都编译为Java字节码,并且可以直接由Java代码访问。