简体   繁体   English

使用Mockito和PowerMockito模拟类对象

[英]Mocking a class object using Mockito and PowerMockito

Is it possible to mock a class object using Mockito and/or PowerMockito? 是否可以使用Mockito和/或PowerMockito模拟类对象?

Something like: 就像是:

Class<Runnable> mockRunnableClass = mock(Class<Runnable>.class);

First, as stated in the comments, you would need to do: 首先,如评论中所述,您需要执行以下操作:

Class<Runnable> mockRunnableaClass = (Class<Runnable>)mock(Class.class);

But that won't work in the usual way because of a limitation with PowerMock. 但这由于PowerMock的限制而无法以通常的方式起作用。 You cannot simply mock classes in from java.lang, java.net, java.io or other system classes because they're loaded by Java's bootstrap classloader and cannot be byte-code manipulated by PowerMock's classloader. 您不能简单地从java.lang,java.net,java.io或其他系统类中模拟类,因为它们是由Java的引导类加载器加载的,并且不能由PowerMock的类加载器进行字节码操作。 (See PowerMock FAQ #4.) As of PowerMock 1.2.5, you can work around this. (请参阅PowerMock常见问题解答 #4。)从PowerMock 1.2.5开始,您可以解决此问题。 If the class you wanted to test was this: 如果您要测试的课程是这样的:

public class ClassToTest {
  private Class<Runnable> runnableClass;

  public void setRunnableClass(Class<Runnable> runnableClass) {
    this.runnableClass = runnableClass;
  }

  public Runnable foo() {
    return runnableClass.newInstance();
  }
}

Then you would do this: 然后,您将执行以下操作:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ ClassToTest.class }) // Prepare the calling class for test
public class SystemClassUserTest {

  @Test
  public void testFoo() throws Exception {
    Class<Runnable> mockClass = (Class<Runnable>) mock(Class.class);
    Runnable mockRunnable = mock(Runnable.class);

    ClassToTest objectUT = new ClassToTest();
    objectUT.setRunnableClass(mockClass);

    when(mockClass.newInstance()).thenReturn(mockRunnable);
    assertThat(objectUT.foo(), is(sameInstance(mockRunnable);
  }
}

why not using an agent if you can't refactor the code there isn't many options, as @jherics mentionned, java system classes are loaded by the bootstrap classloader and powermock can't redefine their bytecode. 如果无法重构代码,为什么不使用代理,因为选项不多,如@jherics所述,java系统类由引导类加载器加载,而powermock无法重新定义其字节码。

However Powermock now coms with an agent, that will allow system classes mock. 但是,Powermock现在附带了一个代理,它将允许模拟系统类。 Check here for complete explanation. 在此处查看完整说明。

The main idea is to modify your java command and add : 主要思想是修改您的java命令并添加:

-javaagent: path/to/powermock-module-javaagent-1.4.12.jar

The basic thing this agent is doing is to definalize classes, to allow future mocking in a specific test, that's why you'll need to use specific types to communicate with the agent, for example with JUnit : 该代理所做的基本操作是对类进行最终化处理,以允许将来在特定测试中进行模拟,这就是为什么您需要使用特定类型与代理进行通信的原因,例如与JUnit进行通信:

@Rule PowerMockRule rule = new PowerMockRule(); // found in the junit4 rule agent jar

TestNG is also supported. 还支持TestNG。 Just check the wiki page for more information. 只需查看Wiki页面以了解更多信息。

Hope that helps. 希望能有所帮助。

An alternative to mocking Class might be to use a Factory instead. 模拟类的替代方法可能是使用Factory。 I know you are concerned about refactoring, but this could be done without changing the public API of the class. 我知道您担心重构,但这可以在不更改类的公共API的情况下完成。 You haven't provided much code to understand the class you are trying to test, but here's an example of refactoring without changing the API. 您没有提供太多代码来理解要尝试测试的类,但是这里有一个无需更改API即可重构的示例。 It's a trivial class, but it might give you an idea. 这是一个琐碎的课程,但可能会给您一个想法。

public class Instantiator {

  public Runnable getNewInstance(Class<Runnable> runnableClass) throws Exception {
    return runnableClass.newInstance();
  }
}

Of course, the easiest thing to do to test this trivial class would be to use a genuine Runnable class, but if you tried to mock the Class, you would run into the problems you're having. 当然,测试这个琐碎的类最简单的方法是使用真正的Runnable类,但是如果您尝试模拟该类,则会遇到问题。 So, you could refactor it thus: 因此,您可以这样重构它:

public class PassThruFactory {
  public Object newInstance(Class<?> clazz) throws Exception {
    return clazz.newInstance();
  }
}

public class Instantiator {
  private PassThruFactory factory = new PassThruFactory();

  public Runnable getNewInstance(Class<Runnable> runnableClass) throws Exception {
    return (Runnable)factory.newInstance(runnableClass);
  }
}

Now Instantiator does exactly the (trivially simple) thing it was doing before with the same public API and no need for any client of the class to do any special injecting of their own. 现在,Instantiator使用相同的公共API完全完成了它以前所做的(非常简单)的事情,而该类的任何客户端都不需要自己进行任何特殊的注入。 However, if you wanted to mock the factory class and inject it, that's very easy to do. 但是,如果您想模拟工厂类并注入它,那很容易做到。

How about this. 这个怎么样。 creating a get method of the has a Object (MS) in class PCService and then mock it. 在类PCService中创建具有对象(MS)的get方法,然后对其进行模拟。

public class PCService implements PCServiceIf {
    public MSIf getMS() {
        return ms;
    }

    private MSIf ms = new MS();
    public boolean isMovieAccessibleToMyLevel(String myLevel, String movieId)  {
        return  getMS().getPCL(movieId);
    }
}


@Test
public void testIsMovieAccessibleToMyLevelMock()  {
    msMock = mock(MS.class);
    spy  = spy(new PCService());

    doReturn(msMock).when(spy).getMS();
    when(msMock.getPCL(movieId)).thenReturn(value);
    when(spy.getMS().getPCL(movieId)).thenReturn(value);
    assertTrue(spy.isMovieAccessibleToMyLevel("PG", movieId) == true);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM