【问题标题】:How do I mock an implementation class?如何模拟实现类?
【发布时间】:2017-07-07 12:16:21
【问题描述】:

我有这样的事情:

public interface SomeInterface {
    public String someMethod(String someArg1, String someArg2);
}

public class SomeInterfaceImpl {

    @Override
    public String someMethod(String someArg1, String someArg2) {
        String response;

        // a REST API call which fetches response (need to mock this)

        return response;
    }
}

public class SomeClass {

    public int execute() {

        int returnValue;

        // some code

        SomeInterface someInterface = new SomeInterfaceImpl();
        String response = someInterface.someMethod("some1", "some2");

        // some code

        return returnValue;
    }
}

我想使用 JUnit 测试 SomeClass 中的 execute() 方法。由于someMethod(String someArg1, String someArg2) 调用了一个REST API,我想模拟someMethod 以返回一些预定义的响应。但不知何故,真正的someMethod 被调用而不是返回预定义的响应。如何让它发挥作用?

这是我尝试使用 Mockito 和 PowerMockito 的方法:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ SomeInterface.class, SomeInterfaceImpl.class, SomeClass.class })
public class SomeClassTest {

    @Test
    public void testExecute() {
        String predefinedResponse = "Some predefined response";
        int expectedReturnValue = 10;

        SomeInterfaceImpl impl = PowerMockito.mock(SomeInterfaceImpl.class);
        PowerMockito.whenNew(SomeInterfaceImpl.class).withAnyArguments().thenReturn(impl);
        PowerMockito.when(impl.someMethod(Mockito.any(), Mockito.any())).thenReturn(predefinedResponse);

        SomeClass someClass = new SomeClass();
        int actualReturnValue = someClass.execute();
        assertEquals(expectedReturnValue, actualReturnValue);
      }
}

【问题讨论】:

  • 正是引入依赖注入的地方。不要使用new,将SomeInterface作为参数传递。
  • @AndyTurner 我试过SomeInterface interface = PowerMockito.mock(SomeInterface.class),然后是PowerMockito.when(interface.someMethod(Mockito.any(), Mockito.any())).thenReturn(predefinedResponse);。它导致了同样的问题。
  • @AndyTurner:看过相关问题。如果你看到我的测试,我已经处理好了。

标签: java unit-testing junit mockito powermockito


【解决方案1】:

您不必这样做。

您将被测方法更改为不直接调用 new。

例如,您使用依赖注入。

是的,这可以通过 Powermock 完成,但请相信我:这样做是错误的方法!

【讨论】:

  • 这是否意味着,我需要为依赖注入添加 Spring 支持,或者可能为 SomeInterfaceImpl 添加一个 setter?恐怕我不允许重构代码。您能展示一下如何使用 PowerMock 来完成吗?
  • 依赖注入并不意味着使用 DI 框架:它只是意味着你以某种方式将依赖项传递到实例中 - 构造函数参数、setter、DI 框架......
  • 另外,SomeInterface 有多种实现,用于不同的类,如SomeClass。使用哪个类,在运行时根据一些标准决定,使用反射实例化并调用其执行方法。所以无法设置SomeInterface的实现或在构造函数中传递。
  • 在这种情况下,您的方法不应再次调用 new 本身。它应该使用一些工厂对象,并且工厂应该通过依赖注入进入。
【解决方案2】:

这里是一个没有框架的注入依赖的例子:

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

interface SomeInterface {
  String someMethod(String someArg1, String someArg2);
}

class SomeInterfaceImpl implements SomeInterface {

  @Override
  public String someMethod(String someArg1, String someArg2) {
    String response;

    response = "the answer.";// a REST API call which fetches response (need to mock this)

    return response;
  }
}

class SomeClass {
  private final SomeInterface someInterface;

  SomeClass(final SomeInterface someInterface) {
    this.someInterface = someInterface;
  }

  public SomeClass() {
    this(new SomeInterfaceImpl());
  }

  public int execute() {

    int returnValue;

    // some code

    String response = someInterface.someMethod("some1", "some2");

    returnValue = 42; // some code

    return returnValue;
  }
}

@RunWith(MockitoJUnitRunner.class)
class SomeClassTest {
  private static final String SOME_PREDEFINED_RESPONSE = "Some predefined response";
  @Mock
  private SomeInterface someInterface;
  @InjectMocks
  private SomeClass underTest;

  @Before
  public void setup() {
    when(someInterface.someMethod(anyString(), anyString())).thenReturn(SOME_PREDEFINED_RESPONSE);
  }

  @Test
  public void testExecute() {
    int expectedReturnValue = 42;
    int actualReturnValue = underTest.execute();
    assertEquals(expectedReturnValue, actualReturnValue);
  }
}

【讨论】:

  • 如果您向我展示您的尝试,我很乐意提供帮助。
【解决方案3】:

这个答案与 Andreas 发布的答案非常相似,唯一的区别是您可以使用 @RunWith(SpringRunner.class) 运行它,它将避免 bean 实例化和额外配置的问题。避免使用 Powermocks,仅在需要模拟静态类时使用它。

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;

import org.springframework.test.context.junit4.SpringRunner;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

interface SomeInterface {
  String someMethod(String someArg1, String someArg2);
}

class SomeInterfaceImpl implements SomeInterface {

  @Override
  public String someMethod(String someArg1, String someArg2) {
    String response;

    response = "the answer.";// a REST API call which fetches response (need to mock this)

    return response;
  }
}

class SomeClass {
  private final SomeInterface someInterface;

  SomeClass(final SomeInterface someInterface) {
    this.someInterface = someInterface;
  }

  public SomeClass() {
    this(new SomeInterfaceImpl());
  }

  public int execute() {

    int returnValue;

    // some code

    String response = someInterface.someMethod("some1", "some2");

    returnValue = 42; // some code

    return returnValue;
  }
}

@RunWith(MockitoJUnitRunner.class)
class SomeClassTest {
  private static final String SOME_PREDEFINED_RESPONSE = "Some predefined response";
  @Mock
  private SomeInterface someInterface;
  @InjectMocks
  private SomeClass underTest;

  @Before
  public void setup() {
    when(someInterface.someMethod(anyString(), anyString())).thenReturn(SOME_PREDEFINED_RESPONSE);
  }

  @Test
  public void testExecute() {
    int expectedReturnValue = 42;
    int actualReturnValue = underTest.execute();
    assertEquals(expectedReturnValue, actualReturnValue);
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-22
    • 2018-01-14
    • 1970-01-01
    • 2018-07-19
    • 2015-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多