📜  Mockito-超时(1)

📅  最后修改于: 2023-12-03 15:17:40.323000             🧑  作者: Mango

Mockito-超时

在写单元测试时,我们经常会用到Mockito框架来模拟一些对象和操作,以便测试我们的代码。Mockito提供了丰富的API来让我们方便的模拟各种场景,而本文要介绍的就是Mockito的超时功能。

在一些场景下,我们的代码需要在规定时间内完成某些操作,如果超时了就需要抛出异常或者做一些其他的处理。这时候我们就可以使用Mockito的超时功能来测试这个代码逻辑了。

基本用法

我们以一个简单的例子来介绍Mockito的超时功能:假设我们有一个计算器类,其中有一个方法add,它在10ms内必须返回计算结果,否则就认为它失败了。我们的测试代码如下所示:

import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;

import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;

public class CalculatorTest {

    @Mock
    private Calculator calculator;

    @Test
    public void testAdd() {
        Mockito.when(calculator.add(1, 2)).thenReturn(3);
        int result = calculator.add(1, 2);
        verify(calculator, timeout(10)).add(1, 2);
        assert result == 3;
    }
}

我们在这个测试方法中,使用Mockito的when方法来为计算器类的add方法设置返回值,然后调用add方法并将结果赋值给result变量。接着使用verify方法来验证add方法是否被调用了,并且设置了10ms的超时时间。

如果add方法在10ms内返回了结果,那么这个测试方法就会通过;否则就会抛出org.mockito.exceptions.verification.TimeoutException异常,表示add方法超时失败了。

超时和重试

有时候我们希望当要测试的方法超时失败时,我们可以进行多次重试,直到测试通过或者达到重试次数的上限。这时候我们可以使用Mockito的retrytimeout方法来实现。

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.util.concurrent.atomic.AtomicInteger;

import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class RetryTest {

    @Mock
    private Service service;

    @Test
    public void testRetry() {
        AtomicInteger i = new AtomicInteger(0);
        //模拟方法调用, 前三次都抛出异常,第四次返回结果
        doThrow(new RuntimeException()).doThrow(new RuntimeException())
                .doThrow(new RuntimeException()).doAnswer(i -> "success")
                .when(service).doSomeThing();

        long startTime = System.currentTimeMillis();
        String result = retry(5, 100, () -> service.doSomeThing());
        System.out.println(result);
        long endTime = System.currentTimeMillis();

        verify(service, timeout(450)).doSomeThing();
        System.out.println("usedTime: " + (endTime - startTime) + " ms");

    }

    interface Service {
        String doSomeThing();
    }

    public static <T> T retry(int maxAttempts, int delay, RetryableSupplier<T> supplier) {
        for (int i = 0; i < maxAttempts; i++) {
            try {
                return supplier.get();
            } catch (Exception e) {
                try {
                    Thread.sleep(delay);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
        throw new RuntimeException("Max attempts reached.");
    }

    interface RetryableSupplier<T> {
        T get() throws Exception;
    }
}

这里我们将测试的方法抽象出来了一个接口RetryableSupplier,里面有一个get方法,这个方法就是我们要测试的方法。我们在testRetry方法中模拟了这个方法的调用,前三次都抛出了异常,第四次返回成功的结果,这样我们可以测试重试功能和超时功能。

在测试方法中,我们调用retry方法来实现重试,其中maxAttempts参数表示最大重试次数,delay表示重试的延迟时间,supplier就是我们要测试的方法。在测试中,我们将最大重试次数设为了5次,延迟时间是100ms,这样总共会用掉400ms,如果成功则返回"success",否则就会抛出异常。

在测试方法中,我们还验证了doSomething方法是否被调用了,并且添加了450ms的超时时间,保证不会超过这个时间限制。

小结

Mockito的超时功能和重试功能可以让我们方便的测试一些需要限时操作的代码逻辑,例如一些网络请求或者IO操作等等。在使用时,我们可以根据自己的需要灵活的设置超时时间和重试次数,以保证我们的测试用例能够充分覆盖代码逻辑。