简体   繁体   English

Mockito 的 argumentCaptor 示例

[英]Example of Mockito's argumentCaptor

Can anyone please provide me an example showing how to use the org.mockito.ArgumentCaptor class and how it is different from simple matchers that are provided with mockito.谁能给我提供一个示例,说明如何使用org.mockito.ArgumentCaptor class 以及它与 mockito 提供的简单匹配器有何不同。

I read the provided mockito documents but those doesn't illustrate it clearly, none of them are able to explain it with clarity.我阅读了提供的 mockito 文档,但这些文档并没有清楚地说明,没有一个能够清楚地解释它。

I agree with what @fge said, more over.我同意@fge 所说的,更多。 Lets look at example.让我们看看例子。 Consider you have a method:考虑你有一个方法:

class A {
    public void foo(OtherClass other) {
        SomeData data = new SomeData("Some inner data");
        other.doSomething(data);
    }
}

Now if you want to check the inner data you can use the captor:现在如果你想检查内部数据,你可以使用捕获器:

// Create a mock of the OtherClass
OtherClass other = mock(OtherClass.class);

// Run the foo method with the mock
new A().foo(other);

// Capture the argument of the doSomething function
ArgumentCaptor<SomeData> captor = ArgumentCaptor.forClass(SomeData.class);
verify(other, times(1)).doSomething(captor.capture());

// Assert the argument
SomeData actual = captor.getValue();
assertEquals("Some inner data", actual.innerData);

The two main differences are:两个主要区别是:

  • when you capture even a single argument, you are able to make much more elaborate tests on this argument, and with more obvious code;当您捕获甚至一个参数时,您就能够对这个参数进行更精细的测试,并使用更明显的代码;
  • an ArgumentCaptor can capture more than once.一个ArgumentCaptor可以捕捉不止一次

To illustrate the latter, say you have:为了说明后者,假设您有:

final ArgumentCaptor<Foo> captor = ArgumentCaptor.forClass(Foo.class);

verify(x, times(4)).someMethod(captor.capture()); // for instance

Then the captor will be able to give you access to all 4 arguments, which you can then perform assertions on separately.然后捕获者将能够让您访问所有 4 个参数,然后您可以分别对其执行断言。

This or any number of arguments in fact, since a VerificationMode is not limited to a fixed number of invocations;事实上,这个或任何数量的参数,因为VerificationMode不限于固定数量的调用; in any event, the captor will give you access to all of them, if you wish.无论如何,如果您愿意,俘虏者将允许您访问所有这些文件。

This also has the benefit that such tests are (imho) much easier to write than having to implement your own ArgumentMatcher s -- particularly if you combine mockito with assertj.这也有一个好处,即这样的测试(恕我直言)比必须实现自己的ArgumentMatcher更容易编写——特别是如果你将 mockito 与 assertj 结合起来。

Oh, and please consider using TestNG instead of JUnit.哦,请考虑使用 TestNG 而不是 JUnit。

The steps in order to make a full check are :进行全面检查的步骤是:

First , prepare the argument captor :首先,准备参数捕获器:

ArgumentCaptor<ArgumentClass> argumentCaptor = ArgumentCaptor.forClass(ArgumentClass.class);

Second , verify the call to the dependent on component (collaborator of subject under test).其次,验证对依赖组件(被测对象的合作者)的调用。

times(1) is the default value, so ne need to add it. times(1) 是默认值,所以需要添加它。

verify(dependentOnComponent, times(1)).method(argumentCaptor.capture());

Third , get the argument passed to collaborator using getValue() of the captor第三,使用捕获者的 getValue() 获取传递给合作者的参数

ArgumentClass someArgument = messageCaptor.getValue();

Fourth , use someArgument for assertions、使用 someArgument 进行断言

I created this example that simulates a very simple service that uses a repository to save a String (no dependency injection, no entities), just to teach ArgumentCaptor quickly.我创建了这个示例来模拟一个非常简单的服务,该服务使用存储库保存字符串(无依赖注入,无实体),只是为了快速教授 ArgumentCaptor。

  • The service receives, converts to uppercase and trim a name, then invoke the repository.该服务接收、转换为大写并修剪名称,然后调用存储库。
  • The repository "saves" the String.存储库“保存”了字符串。
  • With ArgumentCaptor I want to know which value is passed to the repository and then check if it's trimmed and in uppercase, as expected使用 ArgumentCaptor 我想知道哪个值被传递到存储库,然后检查它是否按预期被修剪和大写

3 classes: PersonService, PersonRepository and PersonServiceTest (packages omitted) 3 个类:PersonService、PersonRepository 和 PersonServiceTest(省略包)

public class PersonService {

    private PersonRepository personRepository;

    public void setPersonRepository(final PersonRepository personRepository) {
        this.personRepository = personRepository;
    }

    public void savePerson(final String name) {
        this.personRepository.save(name.toUpperCase().trim());
    }

}

public class PersonRepository {

    public void save(final String person) {
        System.out.println(".. saving person ..");
    }
}


import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;

class PersonServiceTest {

    @Test
    void testPersonService() {

        // Create the repository mock
        final PersonRepository personRepositoryMock = mock(PersonRepository.class);

        // Create the service and set the repository mock
        final PersonService personService = new PersonService();
        personService.setPersonRepository(personRepositoryMock);

        // Save a person
        personService.savePerson("Mario ");

        // Prepare an ArgumentCaptor to capture the value passed to repo.saveMethod
        final ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);

        // Capture the argument passed in the unique method invocation
        verify(personRepositoryMock, times(1)).save(captor.capture());

        // Check if the captured value is the expected one
        final String capturedParameter = captor.getValue();
        assertEquals("MARIO", capturedParameter);
    }
}

You might want to use verify() in combination with the ArgumentCaptor to assure execution in the test and the ArgumentCaptor to evaluate the arguments:您可能希望将 verify() 与 ArgumentCaptor 结合使用以确保在测试中执行,并使用 ArgumentCaptor 来评估 arguments:

ArgumentCaptor<Document> argument = ArgumentCaptor.forClass(Document.class);
verify(reader).document(argument.capture());
assertEquals(*expected value here*, argument.getValue());
    

The argument's value is obviously accessible via the argument.getValue() for further manipulation / checking or whatever you wish to do.参数的值显然可以通过 argument.getValue() 访问,以进行进一步的操作/检查或任何您想做的事情。

Use annotations for this and most things @Spy, @Mock, @Captor, etc.对此和大多数事情使用注释@Spy、@Mock、@Captor 等。

Here is a sample (using JUnit 5):这是一个示例(使用 JUnit 5):

public class Blam
{
  public void foo(final OtherClass other)
  {
    final SomeData data = new SomeData("Some inner data");
    other.doSomething(data);
  }
}

@ExtendWith(MockitoExtension.class)
public class TestBlam
{
  @Captor
  private ArgumentCaptor<SomeData> captorSomeData;

  private Blam classToTest;

  @Mock
  private OtherClass mockOtherClass;

  @BeforeEach
  void beforeEach()
  {
    classToTest = new Blam();
  }

  @Test
  void foo_allGood_success()
  {
    SomeData actualSomeData;

    classToTest.foo(mockOtherClass);
    
    verify(mockOtherClass).doSomething(captorSomeData.capture());
    
    actualSomeData = captorSomeData.getValue();
    
    assert(stuff about actualSomeData);
  }
}

Here I am giving you a proper example of one callback method .在这里,我给你一个回调方法的正确例子。 so suppose we have a method like method login() :所以假设我们有一个类似方法 login() 的方法:

 public void login() {
    loginService = new LoginService();
    loginService.login(loginProvider, new LoginListener() {
        @Override
        public void onLoginSuccess() {
            loginService.getresult(true);
        }

        @Override
        public void onLoginFaliure() {
            loginService.getresult(false);

        }
    });
    System.out.print("@@##### get called");
}

I also put all the helper class here to make the example more clear: loginService class为了让例子更清晰,我也把所有的帮助类放在这里: loginService 类

public class LoginService implements Login.getresult{
public void login(LoginProvider loginProvider,LoginListener callback){

    String username  = loginProvider.getUsername();
    String pwd  = loginProvider.getPassword();
    if(username != null && pwd != null){
        callback.onLoginSuccess();
    }else{
        callback.onLoginFaliure();
    }

}

@Override
public void getresult(boolean value) {
    System.out.print("login success"+value);
}}

and we have listener LoginListener as :我们有监听器 LoginListener 为:

interface LoginListener {
void onLoginSuccess();

void onLoginFaliure();

} }

now I just wanted to test the method login() of class Login现在我只想测试类 Login 的方法 login()

 @Test
public void loginTest() throws Exception {
    LoginService service = mock(LoginService.class);
    LoginProvider provider = mock(LoginProvider.class);
    whenNew(LoginProvider.class).withNoArguments().thenReturn(provider);
    whenNew(LoginService.class).withNoArguments().thenReturn(service);
    when(provider.getPassword()).thenReturn("pwd");
    when(provider.getUsername()).thenReturn("username");
    login.getLoginDetail("username","password");

    verify(provider).setPassword("password");
    verify(provider).setUsername("username");

    verify(service).login(eq(provider),captor.capture());

    LoginListener listener = captor.getValue();

    listener.onLoginSuccess();

    verify(service).getresult(true);

also dont forget to add annotation above the test class as也不要忘记在测试类上方添加注释

@RunWith(PowerMockRunner.class)
@PrepareForTest(Login.class)

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

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