TestNG + Mockito + PowerMock - verifyStatic() 不起作用
TestNG + Mockito + PowerMock - verifyStatic() does not work
我是新的 TestNG 和一般的单元测试。我将 TestNG 6.9.6 与 Mockito 1.10.19 和 PowerMock 1.6.4 一起使用。我想验证 MyService
class 中的 myMethod()
方法是否使用正确的参数在内部调用静态方法 Util.myStaticMethod
。由于 Mockito 本身不支持静态方法的验证,因此我将 PowerMock 与它一起使用。我的测试 class 如下所示:
public class MyTest
{
private MyService myService;
@Captor ArgumentCaptor<String> argCaptor;
@BeforeMethod
public void setup()
{
MockitoAnnotations.initMocks( this );
myService = new MyService();
}
@Test
@PrepareForTest(MyService.class)
public void myTest()
{
PowerMockito.mockStatic(Util.class);
myService.myMethod("arg");
PowerMockito.verifyStatic(10);
Util.myStaticMethod(anyString());
}
}
此测试预计会失败,因为 myMethod
只调用静态方法 Util.myStaticMethod()
一次。但是当我 运行 测试时,它总是通过,无论我将什么值传递给 PowerMockito.verifyStatic()
。
此外,如果我在此class中编写另一个测试方法,然后运行测试,我会得到以下错误
org.mockito.exceptions.misusing.UnfinishedVerificationException:
Missing method call for verify(mock) here:
-> at mypackage.MyTest.myTest(MyTest.java:21)
Example of correct verification:
verify(mock).doSomething()
Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
at mypackage.MyTest.myTest.setup(MyTest.java:10)
Results :
Failed tests:
MyTest.setup:10 UnfinishedVerification
Missing method call for ver...
Tests run: 3, Failures: 1, Errors: 0, Skipped: 1
它在 verifyStatic()
方法处失败,这让我觉得 verifyStatic() 方法需要更多我没有提供的东西。此外,它表示测试总数为 3,而在这种情况下,我只有两种测试方法。
如有任何帮助,我们将不胜感激。
编辑: 按照建议,我尝试将 MyUtil
class 放在 @PrepareForTest
注释中,它仍然给出相同的错误。
好吧,我认为这是非常特定于 TestNG 配置的,因为所有 JUnit 示例都有效 'out of the box'!
从 PowerMock GitHub 站点通读 this link,该站点描述了如何将 TestNG 与 PowerMock 一起使用的更多详细信息。那里使用此示例代码准确描述了验证对模拟 static
方法的调用的场景:
@PrepareForTest(IdGenerator.class)
public class MyTestClass {
@Test
public void demoStaticMethodMocking() throws Exception {
mockStatic(IdGenerator.class);
when(IdGenerator.generateNewId()).thenReturn(2L);
new ClassUnderTest().methodToTest();
// Optionally verify that the static method was actually called
verifyStatic();
IdGenerator.generateNewId();
}
}
接下来是这条信息:
For this to work you need to tell TestNG to use the PowerMock object factory
这是使用 TestNG XML 配置或在测试代码本身中完成的。为了完整起见,我在上面 URL 中给出的选项下面进行了复制。 FWIW 我扩展了 PowerMockTestCase
并且验证工作如你所料。
最后,不要忘记 @PrepareForTest
正确的 class - 即 class 包含你想要模拟的 static
方法,正如@Bax 指出的输出 here.
作为进一步的提示(您可能已经知道,但无论如何在这里值得一提)因为您没有使用 Mockito 来模拟对象,所以可以安全地删除 MockitoAnnotations.initMocks(this)
。
完成所有这些工作后,您可能还想考虑使用 'Black Magic' tools like Powermock is actually exposing code smells in your design? Especially since it looks like the classes containing static
methods are under your ownership. This means you could use an alternative design that doesn't use static
s. I highly recommend Michael Feathers' book Working Effectively with Legacy Code,它是否会改变您的整个软件设计和测试方法...
祝你好运!
Configure TestNG to use the PowerMock object factory
Using suite.xml
In your suite.xml add the following in the suite tag:
object-factory="org.powermock.modules.testng.PowerMockObjectFactory"
e.g.
<suite name="dgf" verbose="10"
object-factory="org.powermock.modules.testng.PowerMockObjectFactory">
<test name="dgf">
<classes>
<class name="com.mycompany.Test1"/>
<class name="com.mycompany.Test2"/>
</classes>
</test>
If you're using Maven you may need to point out the file to Surefire:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>suite.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
Programmatically
Add a method like this to your test class:
@ObjectFactory public IObjectFactory getObjectFactory() {
return new org.powermock.modules.testng.PowerMockObjectFactory();
}
or to be on the safe side you can also extend from the PowerMockTestCase
:
@PrepareForTest(IdGenerator.class)
public class MyTestClass extends PowerMockTestCase { ... }
由于此页面在搜索结果中的排名很高,我只是想在 Stephen 已经写过的有用文档中再添加一些东西。在最初遇到一些问题之后,我刚刚成功地将 JUnit 测试用例迁移到 TestNG。我发现你既可以做 suite.xml 模组,也可以做 "programmatically",但没有必要两者都做。
为了以编程方式进行,@ObjectFactory
方法对我不起作用,但子类化 PowerMockTestCase
起作用了。这是我的示例代码:
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;
import org.testng.annotations.Test;
@PrepareForTest(Pokeball.class)
public class PokeballTestTestNG extends PowerMockTestCase {
// Did not work for me
// @ObjectFactory
// public IObjectFactory getObjectFactory() {
// return new org.powermock.modules.testng.PowerMockObjectFactory();
// }
@Test
public void test1() throws Exception {
PowerMockito.spy(Pokeball.class);
Pokeball.getNetworkMessage();
Pokeball.getNetworkMessageLang(42);
Pokeball.getNetworkMessageLang(42);
PowerMockito.verifyStatic(); //verify one time invocation (default), passes
// PowerMockito.verifyStatic(Mockito.times(11)); //fails
Pokeball.getNetworkMessage(); //need to call the method per PowerMock operation - actual test is done here - Exception thrown here upon failure
PowerMockito.verifyStatic(Mockito.times(2));
Pokeball.getNetworkMessageLang(42);
}
}
编辑:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>myG</groupId>
<artifactId>myTestDouble</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<powermock.version>1.7.0</powermock.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.8.9</version>
<scope>test</scope>
</dependency>
<!--JUnit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--JUnit-->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<!--TestNG-->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-testng</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<!--Both TestNG & JUnit-->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
我是新的 TestNG 和一般的单元测试。我将 TestNG 6.9.6 与 Mockito 1.10.19 和 PowerMock 1.6.4 一起使用。我想验证 MyService
class 中的 myMethod()
方法是否使用正确的参数在内部调用静态方法 Util.myStaticMethod
。由于 Mockito 本身不支持静态方法的验证,因此我将 PowerMock 与它一起使用。我的测试 class 如下所示:
public class MyTest
{
private MyService myService;
@Captor ArgumentCaptor<String> argCaptor;
@BeforeMethod
public void setup()
{
MockitoAnnotations.initMocks( this );
myService = new MyService();
}
@Test
@PrepareForTest(MyService.class)
public void myTest()
{
PowerMockito.mockStatic(Util.class);
myService.myMethod("arg");
PowerMockito.verifyStatic(10);
Util.myStaticMethod(anyString());
}
}
此测试预计会失败,因为 myMethod
只调用静态方法 Util.myStaticMethod()
一次。但是当我 运行 测试时,它总是通过,无论我将什么值传递给 PowerMockito.verifyStatic()
。
此外,如果我在此class中编写另一个测试方法,然后运行测试,我会得到以下错误
org.mockito.exceptions.misusing.UnfinishedVerificationException:
Missing method call for verify(mock) here:
-> at mypackage.MyTest.myTest(MyTest.java:21)
Example of correct verification:
verify(mock).doSomething()
Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
at mypackage.MyTest.myTest.setup(MyTest.java:10)
Results :
Failed tests:
MyTest.setup:10 UnfinishedVerification
Missing method call for ver...
Tests run: 3, Failures: 1, Errors: 0, Skipped: 1
它在 verifyStatic()
方法处失败,这让我觉得 verifyStatic() 方法需要更多我没有提供的东西。此外,它表示测试总数为 3,而在这种情况下,我只有两种测试方法。
如有任何帮助,我们将不胜感激。
编辑: 按照建议,我尝试将 MyUtil
class 放在 @PrepareForTest
注释中,它仍然给出相同的错误。
好吧,我认为这是非常特定于 TestNG 配置的,因为所有 JUnit 示例都有效 'out of the box'!
从 PowerMock GitHub 站点通读 this link,该站点描述了如何将 TestNG 与 PowerMock 一起使用的更多详细信息。那里使用此示例代码准确描述了验证对模拟 static
方法的调用的场景:
@PrepareForTest(IdGenerator.class) public class MyTestClass { @Test public void demoStaticMethodMocking() throws Exception { mockStatic(IdGenerator.class); when(IdGenerator.generateNewId()).thenReturn(2L); new ClassUnderTest().methodToTest(); // Optionally verify that the static method was actually called verifyStatic(); IdGenerator.generateNewId(); } }
接下来是这条信息:
For this to work you need to tell TestNG to use the PowerMock object factory
这是使用 TestNG XML 配置或在测试代码本身中完成的。为了完整起见,我在上面 URL 中给出的选项下面进行了复制。 FWIW 我扩展了 PowerMockTestCase
并且验证工作如你所料。
最后,不要忘记 @PrepareForTest
正确的 class - 即 class 包含你想要模拟的 static
方法,正如@Bax 指出的输出 here.
作为进一步的提示(您可能已经知道,但无论如何在这里值得一提)因为您没有使用 Mockito 来模拟对象,所以可以安全地删除 MockitoAnnotations.initMocks(this)
。
完成所有这些工作后,您可能还想考虑使用 'Black Magic' tools like Powermock is actually exposing code smells in your design? Especially since it looks like the classes containing static
methods are under your ownership. This means you could use an alternative design that doesn't use static
s. I highly recommend Michael Feathers' book Working Effectively with Legacy Code,它是否会改变您的整个软件设计和测试方法...
祝你好运!
Configure TestNG to use the PowerMock object factory
Using suite.xml
In your suite.xml add the following in the suite tag:
object-factory="org.powermock.modules.testng.PowerMockObjectFactory"
e.g.<suite name="dgf" verbose="10" object-factory="org.powermock.modules.testng.PowerMockObjectFactory"> <test name="dgf"> <classes> <class name="com.mycompany.Test1"/> <class name="com.mycompany.Test2"/> </classes> </test>
If you're using Maven you may need to point out the file to Surefire:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> <suiteXmlFiles> <suiteXmlFile>suite.xml</suiteXmlFile> </suiteXmlFiles> </configuration> </plugin>
Programmatically
Add a method like this to your test class:
@ObjectFactory public IObjectFactory getObjectFactory() { return new org.powermock.modules.testng.PowerMockObjectFactory(); }
or to be on the safe side you can also extend from the
PowerMockTestCase
:@PrepareForTest(IdGenerator.class) public class MyTestClass extends PowerMockTestCase { ... }
由于此页面在搜索结果中的排名很高,我只是想在 Stephen 已经写过的有用文档中再添加一些东西。在最初遇到一些问题之后,我刚刚成功地将 JUnit 测试用例迁移到 TestNG。我发现你既可以做 suite.xml 模组,也可以做 "programmatically",但没有必要两者都做。
为了以编程方式进行,@ObjectFactory
方法对我不起作用,但子类化 PowerMockTestCase
起作用了。这是我的示例代码:
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;
import org.testng.annotations.Test;
@PrepareForTest(Pokeball.class)
public class PokeballTestTestNG extends PowerMockTestCase {
// Did not work for me
// @ObjectFactory
// public IObjectFactory getObjectFactory() {
// return new org.powermock.modules.testng.PowerMockObjectFactory();
// }
@Test
public void test1() throws Exception {
PowerMockito.spy(Pokeball.class);
Pokeball.getNetworkMessage();
Pokeball.getNetworkMessageLang(42);
Pokeball.getNetworkMessageLang(42);
PowerMockito.verifyStatic(); //verify one time invocation (default), passes
// PowerMockito.verifyStatic(Mockito.times(11)); //fails
Pokeball.getNetworkMessage(); //need to call the method per PowerMock operation - actual test is done here - Exception thrown here upon failure
PowerMockito.verifyStatic(Mockito.times(2));
Pokeball.getNetworkMessageLang(42);
}
}
编辑:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>myG</groupId>
<artifactId>myTestDouble</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<powermock.version>1.7.0</powermock.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.8.9</version>
<scope>test</scope>
</dependency>
<!--JUnit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--JUnit-->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<!--TestNG-->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-testng</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<!--Both TestNG & JUnit-->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>