无法模拟Java的最终课程

时间:2020-05-12 06:54:46

标签: java testing mockito static-classes

我有一个单元测试,看起来像这样:

DefaultCloudFoundryOperations cfOperationsMock = mock(DefaultCloudFoundryOperations.class);
        when(cfOperationsMock.getSpace()).thenReturn("development");
        when(cfOperationsMock.getOrganization()).thenReturn("cloud.foundry.cli");

        UserAdmin userAdminMock = mock(UserAdmin.class);
        when(cfOperationsMock.userAdmin()).thenReturn(userAdminMock);

        Mono<SpaceUsers> monoMock = (Mono<SpaceUsers>) mock(Mono.class);
        when(userAdminMock.listSpaceUsers(any())).thenReturn(monoMock);

        SpaceUsers spaceUsersMock = mock(SpaceUsers.class);
        when(monoMock.block()).thenReturn(spaceUsersMock);
        when(spaceUsersMock.getDevelopers())
            .thenReturn(Arrays.asList("one", "two", "three", "four"));

        SpaceDevelopersProvider spaceDeveloperProvider = new SpaceDevelopersProvider(
            cfOperationsMock);

        String spaceDevelopers = spaceDeveloperProvider.getSpaceDevelopers();

        assertThat(spaceDevelopers, is(Arrays.asList("one", "two", "three", "four")));

和一个gradle依赖项,看起来像:

dependencies {
    // This dependency is exported to consumers, that is to say found on their compile classpath.
    api 'org.apache.commons:commons-math3:3.6.1'

    // This dependency is used internally, and not exposed to consumers on their own compile classpath.
    implementation 'com.google.guava:guava:28.0-jre'

    compile 'info.picocli:picocli:4.2.0'
    compile 'org.yaml:snakeyaml:1.26'

    // Use JUnit test framework
    testRuntime("org.junit.jupiter:junit-jupiter-engine:5.5.2")
    testRuntime("org.junit.platform:junit-platform-runner:1.5.2")
    testImplementation('org.junit.jupiter:junit-jupiter:5.5.2')

    testImplementation 'org.hamcrest:hamcrest:2.2'
    testImplementation "org.mockito:mockito-core:2.+"


    compile 'io.swagger.parser.v3:swagger-parser:2.0.19'

    compile 'org.cloudfoundry:cloudfoundry-client-reactor:4.6.0.RELEASE'
    compile 'org.cloudfoundry:cloudfoundry-operations:4.6.0.RELEASE'}

当我尝试执行此测试时, 我收到:

org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: class org.cloudfoundry.operations.DefaultCloudFoundryOperations.
Can not mock final classes with the following settings :
 - explicit serialization (e.g. withSettings().serializable())
 - extra interfaces (e.g. withSettings().extraInterfaces(...))

有人有一个主意,如何测试它,或者应该如何调整代码? 我花了很多时间,但找不到解决方案,对我来说真的很有效。

谢谢

3 个答案:

答案 0 :(得分:1)

在生产代码中(与测试代码相反),不要使用DefaultCloudFoundryOperations,而要使用它实现的接口:CloudFoundryOperations

例如,这样声明:

public class SpaceDevelopersProvider {
  private final CloudFoundryOperations cfOps;
  public SpaceDevelopersProvider(CloudFoundryOperations cfOps) {
    this.cfOps = cfOps;
  }
  ...
}

然后,您可以模拟该界面。

CloudFoundryOperations cfOperationsMock = mock(CloudFoundryOperations.class);
...
SpaceDevelopersProvider spaceDeveloperProvider = new SpaceDevelopersProvider(cfOperationsMock);

答案 1 :(得分:0)

在运行时,当您使用Mockito模拟对象时,它将创建一个代理类并扩展您要模拟的类。像在Java中一样,最终类无法扩展,因此嘲笑无法模拟最终类。

答案 2 :(得分:0)

从类名(DefaultCloudFoundryOperations)中,我会 ,首先,该类实现一个接口,其次,所有使用者都将采用该接口界面,而不是具体的类。

如果我 对,那么您可以模拟该接口,而不是具体的类。

如上所述,这是一个猜测!