Groovy классы приложений и модульные тесты Java - PullRequest
0 голосов
/ 10 ноября 2010

У меня есть приложение, создаваемое Maven, которое представляет собой смешанный проект на Groovy и Java.

Используя плагин GMaven (v1.3), я могу легко запускать тесты Groovy для классов Java и Groovy. А во время сборки приложения мои java-классы связываются с расширенными заглушками, которые объявляют методы из GroovyObject.

Однако, если я напишу тест на Java на код приложения Groovy и попытаюсь вызвать методы из GroovyObject, я получу ошибки во время компиляции.

Есть ли обходной путь для этого? Есть ли какие-либо параметры конфигурации для GMaven, которые сделают это возможным?

спасибо.

вот материал build.plugins из моего pom:

             <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 ]

2 голосов
/ 10 ноября 2010

Объявите объект 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, я бы посмотрел, как получить необходимую информацию метакласса из классов groovy с помощью обычных java-доступных методов.

0 голосов
/ 10 ноября 2010

Краткий ответ: Вы не можете получить доступ к Groovy "мета-методам" из Java.

Длинный ответ:

DefaultGroovyMethods.getMetaClass(..) - это , а не метод, который может быть статически скомпилирован в байт-код Java.

[Исправление: Тем временем Дон опубликовал ответ, предлагая привести к GroovyObject. Это правильно, и таким образом вы сможете вызывать мета-методы Groovy следующим образом:

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

(или аналогичный). - Тем не менее, это нецелесообразно для повседневного программирования. ]

Посмотрите на DefaultGroovyMethods ApiDocs . Первым параметром каждого метода является тип времени выполнения объекта. Оставшаяся часть соответствующего метода формирует подпись метода для использования в коде Groovy. Вы уже будете знакомы со многими из них.

Все эти «мета-методы времени выполнения» не статически скомпилированы в GroovyObject ((почти) любой объект Groovy, производный от этого типа), но при вызове динамически отправляются во время выполнения - обычно с использованием метода GroovyObject.invokeMethod(String, Object) .

Итак, ваш Java-код вызывает метод, который просто не существует во время компиляции. - Но как Groovy-код может ссылаться на этот метод, если он не скомпилирован в байт-код Java? - Байт-код 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 (включая сборщики и другие библиотеки) в значительной степени полагаются на эту концепцию метапрограммирования , которая может быть реализована в во время компиляции или выполнения .

К сожалению, Groovy предпочитает последнее, хотя все динамически добавленные функции не скомпилированы в байт-код Java и могут not напрямую вызываться кодом Java.

...