如果要模拟静态方法,则需要使用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
我该怎么办?你可以帮帮我吗?谢谢!
答案 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-theReturn
和verifyStatic
与您自己的静态类或系统类一起使用。
此外,这不是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
}
}
}