Groovy 对象依赖关系导致实例化错误

Groovy 对象依赖关系导致实例化错误,groovy,mocking,spock,byte-buddy,objenesis,Groovy,Mocking,Spock,Byte Buddy,Objenesis,刚刚开始一个新的Gradle项目 此测试通过: def 'Launcher.main should call App.launch'(){ given: GroovyMock(Application, global: true) when: Launcher.main() then: 1 * Application.launch( App, null ) >> null } 。。。在此之前,要使用(Java)模拟进行另一个测试,我

刚刚开始一个新的Gradle项目

此测试通过:

def 'Launcher.main should call App.launch'(){
    given:
    GroovyMock(Application, global: true)

    when:
    Launcher.main()

    then:
    1 * Application.launch( App, null ) >> null
}
。。。在此之前,要使用(Java)模拟进行另一个测试,我必须添加以下依赖项:

testImplementation 'net.bytebuddy:byte-buddy:1.10.8'
testImplementation 'org.objenesis:objenesis:3.1'
(注意,我假设这些版本适用于Groovy 3.+,我现在正在使用它……它们都是Maven Repo上可用的最新版本)

有了这些依赖项,上述测试将失败:

java.lang.InstantiationError: javafx.application.Application
    at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:48)
    at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)
    at org.objenesis.ObjenesisHelper.newInstance(ObjenesisHelper.java:44)
    at org.spockframework.mock.runtime.MockInstantiator$ObjenesisInstantiator.instantiate(MockInstantiator.java:45)
    at org.spockframework.mock.runtime.MockInstantiator.instantiate(MockInstantiator.java:31)
    at org.spockframework.mock.runtime.GroovyMockFactory.create(GroovyMockFactory.java:57)
    at org.spockframework.mock.runtime.CompositeMockFactory.create(CompositeMockFactory.java:42)
    at org.spockframework.lang.SpecInternals.createMock(SpecInternals.java:47)
    at org.spockframework.lang.SpecInternals.createMockImpl(SpecInternals.java:298)
    at org.spockframework.lang.SpecInternals.createMockImpl(SpecInternals.java:288)
    at org.spockframework.lang.SpecInternals.GroovyMockImpl(SpecInternals.java:215)
    at core.AppSpec.Launcher.main should call App.launch(first_tests.groovy:30)
我承认,我对“bytebuddy”和“Objeness”的实际用途只有最粗略的概念,尽管我认为它非常聪明。编辑:刚刚访问了他们各自的主页,我的想法现在稍微不那么粗略了,是的,它非常聪明

如果这方面的传统解决方案不可用,是否有可能将这些依赖项用于单个功能(即测试)?可能会使用一些注释

编辑

这是一个MCVE: 规范:Java11.0.5,OSLinuxMint18.3

build.gradle:

plugins {
    id 'groovy'
    id 'java'
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.8'
}
repositories { mavenCentral() }
javafx {
    version = "11.0.2"
    modules = [ 'javafx.controls', 'javafx.fxml' ]
}
dependencies {
    implementation 'org.codehaus.groovy:groovy:3.+'
    testImplementation 'junit:junit:4.12'
    testImplementation 'org.spockframework:spock-core:2.0-M2-groovy-3.0'
    testImplementation 'net.bytebuddy:byte-buddy:1.10.8'
    testImplementation 'org.objenesis:objenesis:3.1'
    // in light of kriegaex's comments:
    implementation group: 'cglib', name: 'cglib', version: '3.3.0'
}
test { useJUnitPlatform() }
application {
    mainClassName = 'core.Launcher'
}
installDist{}
main.groovy:

class Launcher {
    static void main(String[] args) {
        Application.launch(App, null )
    }
}
class App extends Application {
    void start(Stage primaryStage) {
    }
}
first_tests.groovy:

class AppSpec extends Specification {
    def 'Launcher.main should call App.launch'(){
        given:
        GroovyMock(Application, global: true)
        when:
        Launcher.main()
        then:
        1 * Application.launch( App, null ) >> null
    }
}
解释了这个项目需要调用
应用程序
子类的原因:这样就可以在JavaFX中绑定
installDist

我们不需要使用全局GroovyMock吗

如果要检查交互,请单击“是”。但实际上,您正在测试JavaFX启动器,而不是您的应用程序。所以我怀疑这有什么好处。我将重点测试
应用程序
类。还可以想象一下,您将用Java而不是Groovy编写具有主要方法的类。当从Java代码调用Groovy Mock时,尤其是从全局代码调用Groovy Mock时,Groovy Mock将无法工作。然后,您将通过来自Spock的Powermockito进行测试,这也可以工作,但您仍然需要测试JavaFX启动器,而不是您的应用程序

说任何使用Groovy Mock都是错误的,这不是有点极端吗

我没那么说。我说:“可能您的应用程序设计有问题”。我之所以这么说,是因为使用Groovy模拟和类似模拟静态方法的东西都是测试代码的味道。你可以检查气味,然后确定它是好的,而在大多数情况下它不是。此外,与应用程序设计不同,问题也可能出现在测试本身,在这种情况下,我会说它是。但这是有争议的,所以我将在下面向你们提出一个解决方案

在这种情况下,如果您坚持要测试JavaFX启动器,那么从技术上讲,全局
应用程序
模拟是您唯一的方法,因为即使是
App
上的全局模拟也无法工作,因为启动器使用反射来调用
App
构造函数,而模拟框架不会拦截它

你说Spock Spock core:2.0-M2-groovy-3.0是“预发行版”。我在这页上看不到任何内容(…)是这样说的。你怎么知道的

您已经通过查看GitHub存储库了解到了这一点,但我只是在包含“M2”的不寻常版本号中看到了它,如“里程碑2”,它类似于候选版本(或候选版本)的“RC”(或“CR”)


至于技术问题,您可以不在Gradle脚本中声明Objensis,因为它是可选的依赖项,然后测试编译并运行良好,正如您已经注意到的那样。但是假设您的套件中的其他测试需要可选的依赖项,如objeness、CGLIB(实际上是CGLIB nodep)、Bytebuddy和ASM,您可以告诉Spock在这种情况下不要使用objeness。假设您有一个Gradle构建文件,如下所示:

插件{
id‘groovy’
id‘java’
id“应用程序”
id“org.openjfx.javafxplugin”版本“0.0.8”
}
存储库{mavenCentral()}
javafx{
version=“11.0.2”
modules=['javafx.controls','javafx.fxml']
}
依赖关系{
实现'org.codehaus.groovy:groovy:3.+'
测试实现“org.spockframework:spock核心:2.0-M2-groovy-3.0”
//可选的Spock依赖项,与中列出的版本匹配
// https://mvnrepository.com/artifact/org.spockframework/spock-core/2.0-M2-groovy-3.0
测试实现'net.bytebuddy:byte buddy:1.9.11'
测试实施“org.objeness:objeness:3.0.1”
测试实施“cglib:cglib nodep:3.2.10”
测试实现'org.ow2.asm:asm:7.1'
}
测试{useJUnitPlatform()}
应用{
mainClassName='de.scrum\u master.app.Launcher'
}
installDist{}
我的版本是这样的(对不起,我添加了我自己的包名,还导入了,因为否则它不是真正的MCVE):

package de.scrum\u master.app
导入javafx.application.application
导入javafx.scene.scene
导入javafx.scene.control.Label
导入javafx.scene.layout.StackPane
导入javafx.stage.stage
类应用程序扩展了应用程序{
@凌驾
无效开始(阶段){
def javaVersion=System.getProperty(“java.version”)
def javafxVersion=System.getProperty(“javafx.version”)
Label l=新标签(“您好,JavaFX$javafxVersion,运行在Java$javaVersion上。”)
场景=新场景(新堆栈窗格(l),640480)
舞台场景(场景)
舞台秀
}
}
package de.scrum\u master.app
导入javafx.application.application
类启动器{
静态void main(字符串[]参数){
Application.launch(应用程序,空)
}
}
package de.scrum\u master.app
导入javafx.application.application
导入spock.lang.Specification
类AppSpec扩展了规范{
def'Launcher.main应调用App.launch'(){
鉴于:
GroovyMock(应用程序,全局:true,useObjensis:false)
什么时候:
Launcher.main()
然后:
1*应用程序启动(应用程序,空)
}
}
这里决定性的细节是
useobjeness:false
参数


更新:仅供参考,以下是使用PowerMockito在Java中实现的启动器类的实现方法