如何在Spock + PowerMock中模拟UUID?

时间:2019-03-11 12:17:20

标签: uuid spock powermock

如果要模拟静态方法,则需要使用PowerMock。 我在PowerMock + JUnit上进行了很好的测试,但是与Spock结合使用失败。

Encry.java

public class Encrypt {
    public String genUUID() {
        return UUID.randomUUID().toString();
    }
}

EncryptTest2Java.java 的工作原理:

@PrepareForTest({ UUID.class, Encrypt.class })
@RunWith(PowerMockRunner.class)
public class EncryptTest2Java {
    @Test
    public void genUUID() {
        final String id = "493410b3-dd0b-4b78-97bf-289f50f6e74f";
        UUID uuid = UUID.fromString(id);
        PowerMockito.mockStatic(UUID.class);
        PowerMockito.when(UUID.randomUUID()).thenReturn(uuid);
        Encrypt encrypt = new Encrypt();
        Assert.assertEquals(id, encrypt.genUUID());
    }
}

但是,它在Spock中不起作用:

EncryptTest.groovy

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(Sputnik.class)
@PrepareForTest([UUID.class, Encrypt.class])
class EncryptTest extends Specification {
    def "PasswordGenerator"() {
        given:
        final String id = "493410b3-dd0b-4b78-97bf-289f50f6e74f";
        UUID uuid = UUID.fromString(id)

        PowerMockito.mockStatic(UUID.class)
        PowerMockito.when(UUID.randomUUID()).thenReturn(uuid)
        Encrypt encrypt = new Encrypt()

        when:
        String ret = encrypt.genUUID()

        then:
        ret == id
    }
}

错误信息:

Notifications are not supported for behaviour ALL_TESTINSTANCES_ARE_CREATED_FIRST
Notifications are not supported for behaviour ALL_TESTINSTANCES_ARE_CREATED_FIRST
Notifications are not supported when all test-instances are created first!
Notifications are not supported for behaviour ALL_TESTINSTANCES_ARE_CREATED_FIRST
Notifications are not supported for behaviour ALL_TESTINSTANCES_ARE_CREATED_FIRST
Notifications are not supported for behaviour ALL_TESTINSTANCES_ARE_CREATED_FIRST
Notifications are not supported for behaviour ALL_TESTINSTANCES_ARE_CREATED_FIRST

org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.
For example:
    when(mock.getArticles()).thenReturn(articles);

Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
   Those methods *cannot* be stubbed/verified.
   Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.


    at arthur.dy.lee.mybatisplusdemo.EncryptTest.PasswordGenerator(EncryptTest.groovy:24)


Process finished with exit code -1

我该怎么办?你可以帮帮我吗?谢谢!

1 个答案:

答案 0 :(得分:0)

我再次偶然发现了这个,并在我的机器上进行了复制。我创建了Spock ticket #1014是为了寻求解释和解决方法。

同时,我通过PowerMockito.stub找到了一种解决方法,至少对于您不想检查任何交互(即,验证调用具有特定参数的方法的次数)的简单情况而言:

package de.scrum_master.stackoverflow.q55101703

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.powermock.api.support.membermodification.MemberMatcher.method
import static org.powermock.api.support.membermodification.MemberModifier.stub

@RunWith(PowerMockRunner)
@PowerMockRunnerDelegate(Sputnik)
@PrepareForTest(Encrypt)
class EncryptTest extends Specification {

  def testUUID() {
    given:
    def id = "493410b3-dd0b-4b78-97bf-289f50f6e74f"
    UUID uuid = UUID.fromString(id)
    stub(method(UUID, "randomUUID")).toReturn(uuid)

    expect:
    new Encrypt().genUUID() == id
  }

}

更新:实际上,有一种方法可以解决此问题,并且可以将when-theReturnverifyStatic与您自己的静态类或系统类一起使用。

此外,这不是Spock问题,而是Groovy问题,因为用Groovy编写的JUnit测试也会发生这种情况。解决方法是将@CompileStatic用于Groovy JUnit测试。在Spock中,不能将静态编译用于特征方法或整个规范,因为Spock依赖于Grrovy的动态特征。但是您可以使用辅助方法来执行PowerMock内容,并使用@CompileStatic进行注释。

我在PowerMock ticket #1008中记录了我的发现 我已经记录了我的发现。在这里您还将找到示例代码。在您的情况下,解决方案如下所示:

package de.scrum_master.stackoverflow.q55101703

import groovy.transform.CompileStatic
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 de.scrum_master.stackoverflow.q55101703.EncryptTest.PowerMockHelper.*
import static org.mockito.Mockito.times
import static org.powermock.api.mockito.PowerMockito.*

@RunWith(PowerMockRunner)
@PowerMockRunnerDelegate(Sputnik)
@PrepareForTest([Encrypt, UUID])
class EncryptTest extends Specification {

  def testUUID() {
    given:
    def id = "493410b3-dd0b-4b78-97bf-289f50f6e74f"
    UUID uuid = UUID.fromString(id)
    testUUID_mockStatic(uuid)

    expect:
    new Encrypt().genUUID() == id
    // This does not work because UUID is final. Otherwise it would work.
    //PowerMockHelper.testUUID_verifyStatic()
  }

  @CompileStatic
  static class PowerMockHelper {
    def static testUUID_mockStatic(UUID uuid) {
      mockStatic(UUID)
      when(UUID.randomUUID()).thenReturn(uuid)
    }

    def static testUUID_verifyStatic() {
      // This does not work for final classes, at least not with Mockito back-end.
      // Maybe there is a way with EasyMock. The documentation at least mentions
      // mocking final classes for EasyMock, but I only saw an example with non-static
      // methods.
      //
      // Executing the following line yields this error:
      // org.mockito.exceptions.base.MockitoException:
      //   Cannot mock/spy class java.util.UUID
      //   Mockito cannot mock/spy because :
      //     - final class
      verifyStatic(UUID, times(1))
      UUID.randomUUID()
      true
    }
  }
}