对象生成依赖导致实例化错误



刚刚开始一个新的Gradle项目。

此测试通过:

def 'Launcher.main should call App.launch'(){
given:
GroovyMock(Application, global: true)
when:
Launcher.main()
then:
1 * Application.launch( App, null ) >> null
}

直到,为了使用(Java(Mock进行另一个测试,我必须添加以下依赖项:

testImplementation 'net.bytebuddy:byte-buddy:1.10.8'
testImplementation 'org.objenesis:objenesis:3.1'

(注意,我认为这些版本适用于Groovy3.+,我现在正在使用……它们都是MavenRepo中最新的版本(。

有了这些依赖关系,上述测试失败:

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)

我承认,我对"字节伙伴"one_answers"对象论"的实际作用只有最粗略的概念,尽管我认为它非常聪明。编辑:刚刚访问了他们各自的主页,我的想法现在稍微不那么粗略了,是的,它非常聪明。

如果没有正统的解决方案,是否有可能关闭对单个功能(即测试(的这些依赖关系的使用?可能使用了一些注释?

编辑

这是一个MCVE:规格:Java 11.0.5,OS Linux Mint 18.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
}
}

这里解释了这个项目需要调用Application子类的原因:这样就可以在JavaFX中绑定installDist

我们不需要使用全局GroovyMockk吗?

如果您想检查交互,可以。但实际上,您测试的是JavaFX启动器,而不是应用程序。所以我怀疑这是否有任何好处。我将专注于测试App类。还可以想象一下,您将用Java而不是Groovy编写带有主要方法的类。从Java代码调用Groovy mock时将不起作用,尤其是全局mock。然后,您将通过Spock的Powermockito进行测试,这也会起作用,但您仍然要测试JavaFX启动器,而不是应用程序。

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

我没有这么说。我说:">可能您的应用程序设计有问题"。我之所以这么说,是因为Groovy mock和类似于mocking静态方法的东西的使用是测试代码的味道。你可以检查气味,然后决定它是正常的,而IMO在大多数情况下是不正常的。此外,问题也可能出现在测试本身,而不是应用程序设计中,在这种情况下,我会说是这样。但这是有争议的,所以我将在下面向您提供一个解决方案。

在这种情况下,如果你坚持测试JavaFX启动器,那么从技术上讲,全局Applicationmock是你唯一的方法,因为即使是App上的全局mock也无法工作,因为启动器使用反射来调用App构造函数,而这不会被mock框架截获。

你说斯波克-斯波克核心:2.0-M2-groovy-3.0是一个"预发布"。我在这一页上看不到任何内容(…(。你怎么知道的?

你已经通过查看GitHub存储库发现了这一点,但我只是在一个不寻常的版本号中看到了它,它包含"M2",就像"里程碑2"一样,类似于候选版本(或候选版本(的"RC"(或"CR"(。


至于技术问题,您可以在Gradle脚本中不声明Obgenesis,因为它是一个可选的依赖项,然后测试编译并运行良好,正如您自己已经注意到的那样。但是,假设您的套件中的其他测试需要可选的依赖项,如Obgenesis、CGLIB(实际上是CGLIB-nodep(、Bytebuddy和ASM,您可以告诉Spock在这种情况下不要使用Obgenesis。因此,假设您有一个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 'org.spockframework:spock-core:2.0-M2-groovy-3.0'
// Optional Spock dependencies, versions matching the ones listed at
// https://mvnrepository.com/artifact/org.spockframework/spock-core/2.0-M2-groovy-3.0
testImplementation 'net.bytebuddy:byte-buddy:1.9.11'
testImplementation 'org.objenesis:objenesis:3.0.1'
testImplementation 'cglib:cglib-nodep:3.2.10'
testImplementation 'org.ow2.asm:asm:7.1'
}
test { useJUnitPlatform() }
application {
mainClassName = 'de.scrum_master.app.Launcher'
}
installDist {}

我的MCVE版本是这样的(对不起,我添加了我自己的包名,也导入了,因为否则它就不是真正的MCVE(:

package de.scrum_master.app
import javafx.application.Application
import javafx.scene.Scene
import javafx.scene.control.Label
import javafx.scene.layout.StackPane
import javafx.stage.Stage
class App extends Application {
@Override
void start(Stage stage) {
def javaVersion = System.getProperty("java.version")
def javafxVersion = System.getProperty("javafx.version")
Label l = new Label("Hello, JavaFX $javafxVersion, running on Java $javaVersion.")
Scene scene = new Scene(new StackPane(l), 640, 480)
stage.setScene(scene)
stage.show()
}
}
package de.scrum_master.app
import javafx.application.Application
class Launcher {
static void main(String[] args) {
Application.launch(App, null)
}
}
package de.scrum_master.app
import javafx.application.Application
import spock.lang.Specification
class AppSpec extends Specification {
def 'Launcher.main should call App.launch'() {
given:
GroovyMock(Application, global: true, useObjenesis: false)
when:
Launcher.main()
then:
1 * Application.launch(App, null)
}
}

这里的决定性细节是useObjenesis: false参数。


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

注意,此解决方案需要Spock 1.x中的Sputnik运行程序,该运行程序在2.x中被删除。因此,在Spock 2中,这目前不起作用,因为它基于JUnit 5,并且不能再使用@RunWith(PowerMockRunner)@PowerMockRunnerDelegate(Sputnik),因为PowerMock当前不支持JUnit 5。但我用Spock 1.3-groovy-2.5和groovy 2.5.8测试了它。

package de.scrum_master.app
import javafx.application.Application
import org.junit.runner.RunWith
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.PowerMockRunner
import org.powermock.modules.junit4.PowerMockRunnerDelegate
import org.spockframework.runtime.Sputnik
import spock.lang.Specification
import static org.mockito.Mockito.*
import static org.powermock.api.mockito.PowerMockito.*
@RunWith(PowerMockRunner)
@PowerMockRunnerDelegate(Sputnik)
@PrepareForTest(Application)
class JavaAppSpec extends Specification {
def 'JavaLauncher.main should launch JavaApp'() {
given:
mockStatic(Application)
when:
JavaLauncher.main()
then:
verifyStatic(Application, times(1))
Application.launch(JavaApp)
}
}

相关内容

  • 没有找到相关文章

最新更新