首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对象相关性会导致实例化错误。

对象相关性会导致实例化错误。
EN

Stack Overflow用户
提问于 2020-03-18 19:47:04
回答 1查看 1.1K关注 0票数 3

刚刚开始一个新的项目。

此测试通过:

代码语言:javascript
复制
def 'Launcher.main should call App.launch'(){
    given:
    GroovyMock(Application, global: true)

    when:
    Launcher.main()

    then:
    1 * Application.launch( App, null ) >> null
}

..。直到,要使用(Java) Mock获得另一个测试才能工作,我必须添加以下依赖项:

代码语言:javascript
复制
testImplementation 'net.bytebuddy:byte-buddy:1.10.8'
testImplementation 'org.objenesis:objenesis:3.1'

(我假设这些版本可以用于Groovy 3.+,我现在使用的是.这两款产品都是Maven Repo提供的最新产品。

对于这些依赖项,上面的测试失败:

代码语言:javascript
复制
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)

我承认,我对“字节伙伴”和“讣告”实际上所做的事情只有一个最粗略的概念,尽管我认为它非常聪明。编辑:刚刚访问了他们各自的主页后,我的想法现在稍微不那么粗略了,是的,这是非常聪明的。

如果对此没有正统的解决方案,那么是否有可能将这些依赖关系用于单独的特性(即测试)?可能会使用注释吗?

编辑

这是一个MCVE: Specs: Java11.0.5,OSLinuxMint18.3。

build.gradle:

代码语言:javascript
复制
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:

代码语言:javascript
复制
class Launcher {
    static void main(String[] args) {
        Application.launch(App, null )
    }
}
class App extends Application {
    void start(Stage primaryStage) {
    }
}

first_tests.groovy:

代码语言:javascript
复制
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子类的原因解释了here:这样就可以在JavaFX中进行捆绑的installDist了。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-03-20 03:40:57

,难道我们不需要使用全局GroovyMock吗?

如果你想检查互动,是的。但实际上,您是在测试JavaFX启动程序,而不是应用程序。因此,我怀疑是否有任何好处。我将重点放在测试App类上。还可以想象一下,您将用Java而不是Groovy的主要方法编写类。当从Java代码调用Groovy模拟时不能工作,尤其是全局代码。然后,您将通过Spock的Powermockito进行测试,这也可以工作,但您仍然会测试JavaFX启动程序,而不是应用程序。

也说--任何使用Groovy模拟的--都是错误的,这不是有点极端吗?

我可没那么说。我说:“可能应用程序设计有问题”。我之所以这么说,是因为Groovy模拟的使用以及类似模拟静态方法之类的东西都是测试代码的味道。你可以检查气味,然后判断它是否正常,而在大多数情况下,它不是。此外,除了应用程序设计之外,问题还可以发生在测试本身,在这种情况下,我可以这样说。但这是有争议的,所以我将在下面向你们提出一个解决方案。

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

你说斯波克-核心:2.0-M2-Groovy-3.0是一个“预发行版”。我在这页上什么也看不见(.)上面写着。怎么又知道了?

您已经通过查看GitHub存储库了解到了这一点,但我刚才看到的是不寻常的版本号,其中包含"M2“之类的”里程碑2“,类似于用于发布候选版本(或候选版本)的"RC”(或"CR")。

至于技术问题,您既不能在Gradle脚本中声明Objenesis,因为它是一个可选的依赖项,那么测试就会编译并运行良好,正如您已经注意到的那样。但是,假设您需要Objenesis、CGLIB (实际上是cglib)、Bytebuddy和ASM这样的可选依赖项来进行您的套件中的其他测试,那么您只需告诉Spock在本例中不要使用Objenesis。因此,假设您有一个如下所示的Gradle构建文件:

代码语言:javascript
复制
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):

代码语言:javascript
复制
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()
  }
}
代码语言:javascript
复制
package de.scrum_master.app

import javafx.application.Application

class Launcher {
  static void main(String[] args) {
    Application.launch(App, null)
  }
}
代码语言:javascript
复制
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实现的launcher类来实现它。

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

代码语言:javascript
复制
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)
  }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60746571

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档