mockito:有没有办法捕获存根方法的返回值?

发布于 2024-12-02 09:13:02 字数 426 浏览 1 评论 0原文

如果我模拟一个方法来返回某个对象的新实例,如何捕获返回的实例?

例如:

 when(mock.someMethod(anyString())).thenAnswer(new Answer() {
     Object answer(InvocationOnMock invocation) {
         Object[] args = invocation.getArguments();
         Object mock = invocation.getMock();
         return new Foo(args[0])
     }
 });

显然,我可以有一个 Foo 类型的字段,并在 answer 内部将其设置为新实例,但是有更好的方法吗?像 ArgumentCaptor 这样的东西?

If I mock a method to return a new instance of some object, how can I capture the returned instance?

E.g.:

 when(mock.someMethod(anyString())).thenAnswer(new Answer() {
     Object answer(InvocationOnMock invocation) {
         Object[] args = invocation.getArguments();
         Object mock = invocation.getMock();
         return new Foo(args[0])
     }
 });

Obviously, I can have a field of type Foo and inside answer set it to the new instance, but is there a nicer way? Something like ArgumentCaptor?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

暮年 2024-12-09 09:13:02

我想做类似的事情,但使用的是监视对象而不是模拟对象。具体来说,给定一个间谍对象,我想捕获返回值。根据 Andreas_D 的回答,这就是我的想法。

public class ResultCaptor<T> implements Answer {
    private T result = null;
    public T getResult() {
        return result;
    }

    @Override
    public T answer(InvocationOnMock invocationOnMock) throws Throwable {
        result = (T) invocationOnMock.callRealMethod();
        return result;
    }
}

预期用途:

// spy our dao
final Dao spiedDao = spy(dao);
// instantiate a service that does some stuff, including a database find
final Service service = new Service(spiedDao);

// let's capture the return values from spiedDao.find()
final ResultCaptor<QueryResult> resultCaptor = new ResultCaptor<>();
doAnswer(resultCaptor).when(spiedDao).find(any(User.class), any(Query.class));

// execute once
service.run();
assertThat(resultCaptor.getResult()).isEqualTo(/* something */);

/// change conditions ///

// execute again
service.run();
assertThat(resultCaptor.getResult()).isEqualTo(/* something different */);

I wanted to do something similar, but with a spied object rather than a mock. Specifically, given a spied object, I want to capture the return value. Based on Andreas_D's answer, here's what I came up with.

public class ResultCaptor<T> implements Answer {
    private T result = null;
    public T getResult() {
        return result;
    }

    @Override
    public T answer(InvocationOnMock invocationOnMock) throws Throwable {
        result = (T) invocationOnMock.callRealMethod();
        return result;
    }
}

Intended usage:

// spy our dao
final Dao spiedDao = spy(dao);
// instantiate a service that does some stuff, including a database find
final Service service = new Service(spiedDao);

// let's capture the return values from spiedDao.find()
final ResultCaptor<QueryResult> resultCaptor = new ResultCaptor<>();
doAnswer(resultCaptor).when(spiedDao).find(any(User.class), any(Query.class));

// execute once
service.run();
assertThat(resultCaptor.getResult()).isEqualTo(/* something */);

/// change conditions ///

// execute again
service.run();
assertThat(resultCaptor.getResult()).isEqualTo(/* something different */);
演出会有结束 2024-12-09 09:13:02

看起来您想要观察然后Answer实例,并在每次调用answer方法时接收通知(这会触发创建新的Foo)。那么为什么不发明一个 ObservableAnswer 类:

public abstract class ObservableAnswer implements Answer {
  private Listener[] listeners; // to keep it very simple...

  public ObservableAnswer(Listener...listeners) {
    this.listeners = listeners;
  }

  @Override
  public Object answer(InvocationOnMock invocation) {
    Object answer = observedAnswer(invocation);
    for (Listener listener:listeners) {
       listener.send(answer);
    }
    return answer;
  }

  // we'll have to implement this method now
  public abstract Object observedAnswer(InvocationOnMock invocation);
}

预期用途:

Listener[] myListeners = getListeners();  // some magic (as usual)
when(mock.someMethod(anyString())).thenAnswer(new ObservableAnswer(myListeners) {
     Object observedAnswer(InvocationOnMock invocation) {
         Object[] args = invocation.getArguments();
         Object mock = invocation.getMock();
         return new Foo(args[0])
     }

 });

Looks like you want to observe and then Answer instances, and receive notifications each time the answer method is called (which triggers the creation of a new Foo). So why not invent an ObservableAnswer class:

public abstract class ObservableAnswer implements Answer {
  private Listener[] listeners; // to keep it very simple...

  public ObservableAnswer(Listener...listeners) {
    this.listeners = listeners;
  }

  @Override
  public Object answer(InvocationOnMock invocation) {
    Object answer = observedAnswer(invocation);
    for (Listener listener:listeners) {
       listener.send(answer);
    }
    return answer;
  }

  // we'll have to implement this method now
  public abstract Object observedAnswer(InvocationOnMock invocation);
}

Intended use:

Listener[] myListeners = getListeners();  // some magic (as usual)
when(mock.someMethod(anyString())).thenAnswer(new ObservableAnswer(myListeners) {
     Object observedAnswer(InvocationOnMock invocation) {
         Object[] args = invocation.getArguments();
         Object mock = invocation.getMock();
         return new Foo(args[0])
     }

 });
金橙橙 2024-12-09 09:13:02

作为 @JeffFairley 答案的替代方案,您可以利用 AtomicReference。它将充当 Holder,但我更喜欢它而不是真正的持有者,因为它是在 Java 的基本框架中定义的。

// spy our dao
final Dao spiedDao = spy(dao);
// instantiate a service that does some stuff, including a database find
final Service service = new Service(spiedDao);

// let's capture the return values from spiedDao.find()
AtomicReference<QueryResult> reference = new AtomicReference<>();
doAnswer(invocation -> {
    QueryResult result = (QueryResult)invocation.callRealMethod();
    reference.set(result);
    return result;
}).when(spiedDao).find(any(User.class), any(Query.class));

// execute once
service.run();
assertThat(reference.get()).isEqualTo(/* something */);

/// change conditions ///

// execute again
service.run();
assertThat(result.get()).isEqualTo(/* something different */);

在我看来: ResultCaptor 是很酷的东西,将来可能会集成到 Mockito 中,可广泛重用且语法简短。但如果您偶尔需要,那么 lambda 的几行可以更简洁

As an alternative to @JeffFairley's answer, you can leverage AtomicReference<T>. It will act as a Holder<T>, but I prefer this over real holders because it's defined in Java's base framework.

// spy our dao
final Dao spiedDao = spy(dao);
// instantiate a service that does some stuff, including a database find
final Service service = new Service(spiedDao);

// let's capture the return values from spiedDao.find()
AtomicReference<QueryResult> reference = new AtomicReference<>();
doAnswer(invocation -> {
    QueryResult result = (QueryResult)invocation.callRealMethod();
    reference.set(result);
    return result;
}).when(spiedDao).find(any(User.class), any(Query.class));

// execute once
service.run();
assertThat(reference.get()).isEqualTo(/* something */);

/// change conditions ///

// execute again
service.run();
assertThat(result.get()).isEqualTo(/* something different */);

In my opinion: ResultCaptor is cool stuff that may be integrated in Mockito in the future, is widely reusable and short in syntax. But if you need that sporadically, then few lines of a lambda can be more concise

总攻大人 2024-12-09 09:13:02

调用 doAnswer,然后调用真正的方法并将返回值添加到列表中,如下所示:

final var capturedValues = new ArrayList<Integer>();
final var myObjectList = spy(new MyObject());
doAnswer(invocation -> {
  final var r = invocation.callRealMethod();
  capturedValues.add((Integer) r);
  return r;
})
.when(myObjectList)
.mySuperMethod;

完整示例:

@Test
public void test() {

  // arrange
  final var capturedValues = new ArrayList<Integer>();
  final var myObjectList = spy(new ArrayList<>());
  doAnswer(invocation -> {
    final var r = invocation.callRealMethod();
    capturedValues.add((Integer) r);
    return r;
  })
      .when(myObjectList)
      .size();

  // act
  myObjectList.size();
  myObjectList.add("one");

  myObjectList.size();
  myObjectList.add("two");

  myObjectList.size();

  // assert
  assertEquals(3, capturedValues.size());
  assertEquals("[0, 1, 2]", capturedValues.toString());
}

Call doAnswer, then call the real method and add the returning value to a list, as the following:

final var capturedValues = new ArrayList<Integer>();
final var myObjectList = spy(new MyObject());
doAnswer(invocation -> {
  final var r = invocation.callRealMethod();
  capturedValues.add((Integer) r);
  return r;
})
.when(myObjectList)
.mySuperMethod;

A full example:

@Test
public void test() {

  // arrange
  final var capturedValues = new ArrayList<Integer>();
  final var myObjectList = spy(new ArrayList<>());
  doAnswer(invocation -> {
    final var r = invocation.callRealMethod();
    capturedValues.add((Integer) r);
    return r;
  })
      .when(myObjectList)
      .size();

  // act
  myObjectList.size();
  myObjectList.add("one");

  myObjectList.size();
  myObjectList.add("two");

  myObjectList.size();

  // assert
  assertEquals(3, capturedValues.size());
  assertEquals("[0, 1, 2]", capturedValues.toString());
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文