Mockito验证lambda被调用n次

huangapple go评论65阅读模式
英文:

Mockito verify lambda is called n-times

问题

I see that you want to test whether the repo.getData() method is called a specific number of times when an error occurs in your Service class. It seems like you're using Mockito for mocking and testing. The issue you're facing is that getData() is never called during the test.

Here's a corrected version of your test code:

import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class ServiceTest {

    @InjectMocks
    Service service;

    @Mock
    Repository repository;

    @Mock
    RetryHelper<Collection<String>> retryHelper;

    @Captor
    ArgumentCaptor<RetryHelper.Operation<Collection<String>>> operation;

    @Before
    public void setUp() throws Exception {
        when(retryHelper.doWithRetry(any())).thenAnswer(invocation -> {
            RetryHelper.Operation<Collection<String>> op = invocation.getArgument(0);
            return op.doIt();
        });
    }

    @Test
    public void shouldRetryIfDataQueryFailsForNonFatalError() throws Exception {
        when(repository.getData())
            .thenThrow(new RuntimeException("Runtime Exception"));

        service.callService();

        verify(retryHelper).doWithRetry(operation.capture());

        // Assert that the operation was retried 2 times
        verify(repository, times(2)).getData();
    }
}

Here are the changes I made to your code:

  1. Added @RunWith(MockitoJUnitRunner.class) to your test class to enable Mockito annotations automatically.

  2. Used @InjectMocks for the Service class, @Mock for the other dependencies, and @Captor for capturing the RetryHelper.Operation argument.

  3. In the setUp method, I mocked the behavior of retryHelper.doWithRetry to capture the operation passed to it and execute it immediately. This is necessary to trigger the retry logic.

With these changes, the test should now verify that the getData() method is called twice when an exception is thrown.

英文:

I need to test if a lambda function is called n-times from a service instance.

I have a Service class, that interact with the repository, when an error occur on retriving data from repository the service should retry until a max number of retries is reached so I have implemented as follow:

interface Repository {
   Collection&lt;String&gt; getData();
}

public class RetryHelper&lt;T&gt; {

    private Integer retries;

    public RetryHelper(Integer retries) {
        this.retries = retries;
    }

    public interface Operation&lt;T&gt; {
        T doIt() throws Exception;
    }

    public T doWithRetry(Operation&lt;T&gt; operation) throws Exception {
        int remainRetries = retries;
        do {
            try {
                return operation.doIt();
            } catch (Exception e) {
                if (remainRetries == 0) {
                    throw e;
                }
                //TODO: wait before retry
                remainRetries--;
            }
        } while (true);
    }
}

class Service {
   @Inject
   Repository repo;

   private final RetryHelper&lt;Collection&lt;String&gt;&gt; retryHelper;

   public Collection&lt;String&gt; callService() {
        try {
            Collection&lt;String&gt; res = retryHelper.doWithRetry(() -&gt;
                repo.getData());
            return res;
        } catch (Exception e) {
            throw (CustomException) e;
        }
   }

}

I need to test using Mockito that repo.getData() is called n-times when error occurs. I can change the Service code and the RetryHelper, so I am open to suggestions.

I have try to implment the test following tutorials and documentations:

public class ServiceTest {

    @Inject
    Service service;

    @InjectMock
    Repository repository;

    @InjectMock
    RetryHelper&lt;Collection&lt;String&gt;&gt; retryHelper;


    @Captor
    ArgumentCaptor&lt;RetryHelper.Operation&lt;Collection&lt;String&gt;&gt;&gt; operation;

    @BeforeEach
    void init_mocks() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void shouldRetryIfDataQueryFailsForNonFatalError() throws Exception {
        when(repository.getData())
            .thenThrow(new RuntimeException(&quot;Runtime Exception&quot;));

        service.callService();

        verify(retryHelper).doWithRetry(operation.capture());

        verify(repository, times(2)).getData();

    }
}

The test fail with message that getData() is never called.

答案1

得分: -1

我终于找到了不使用`Captor`的解决方案

public class ServiceTest {

    @Inject
    Service service;

    @InjectMock
    Repository repository;

    @Inject
    RetryHelper<Collection<String>> retryHelper;


    @Test
    void shouldRetryIfDataQueryFailsForNonFatalError() throws Exception {
        when(repository.getData())
            .thenThrow(new RuntimeException("Runtime Exception"));
        
        try {
           service.callService();
        } catch(Exception e) {
             verify(repository, times(2)).getData();
        }

    }
}
英文:

I have finally found the solution without using Captor


public class ServiceTest {

    @Inject
    Service service;

    @InjectMock
    Repository repository;

    @Inject
    RetryHelper&lt;Collection&lt;String&gt;&gt; retryHelper;


    @Test
    void shouldRetryIfDataQueryFailsForNonFatalError() throws Exception {
        when(repository.getData())
            .thenThrow(new RuntimeException(&quot;Runtime Exception&quot;));
        
        try {
           service.callService();
        } catch(Exception e) {
             verify(repository, times(2)).getData();
        }

    }
}

huangapple
  • 本文由 发表于 2020年9月4日 21:33:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/63742245.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定